Merge ~jugmac00/launchpad:prevent-email-disclosure into launchpad:master

Proposed by Jürgen Gmach
Status: Merged
Approved by: Jürgen Gmach
Approved revision: e2c4848ab0dd8c6a6a8d75f29b796202f4f99027
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~jugmac00/launchpad:prevent-email-disclosure
Merge into: launchpad:master
Diff against target: 134 lines (+41/-16)
4 files modified
lib/lp/registry/model/distributionmirror.py (+5/-4)
lib/lp/registry/tests/test_distributionmirror.py (+15/-7)
lib/lp/translations/scripts/po_import.py (+2/-5)
lib/lp/translations/scripts/tests/test_translations_import.py (+19/-0)
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+412672@code.launchpad.net

Commit message

Prevent email address disclosure for mirror notifications

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) wrote :

This looks good as far as it goes.

https://git.launchpad.net/launchpad/tree/lib/lp/soyuz/mail/binarypackagebuild.py#n166 isn't a problem: an SPR's creator is a single person.

The importer in https://git.launchpad.net/launchpad/tree/lib/lp/translations/scripts/po_import.py#n145 sometimes ends up being ~rosetta-admins, which is a large team, so let's add a loop there.

https://git.launchpad.net/launchpad/tree/lib/lp/translations/scripts/translations_to_branch.py#n297 is marginal, but I'd be inclined to leave it alone, at least for now; I think the case where a team explicitly owns something is a bit different from the mirror-admins and rosetta-admins cases where the ownership is rather more, um, ambient.

Revision history for this message
Jürgen Gmach (jugmac00) wrote :

Thanks, Colin!

I updated the code in `po_import.py` accordingly.

The MP is ready for review.

Revision history for this message
Colin Watson (cjwatson) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lp/registry/model/distributionmirror.py b/lib/lp/registry/model/distributionmirror.py
2index 8d3bd82..2454187 100644
3--- a/lib/lp/registry/model/distributionmirror.py
4+++ b/lib/lp/registry/model/distributionmirror.py
5@@ -385,13 +385,14 @@ class DistributionMirror(SQLBase):
6 message = template % replacements
7 subject = "Launchpad: Verification of %s failed" % self.name
8
9- mirror_admin_address = get_contact_email_addresses(
10+ mirror_admin_addresses = get_contact_email_addresses(
11 self.distribution.mirror_admin)
12- simple_sendmail(fromaddress, mirror_admin_address, subject, message)
13+ for admin_address in mirror_admin_addresses:
14+ simple_sendmail(fromaddress, admin_address, subject, message)
15
16 if notify_owner:
17- owner_address = get_contact_email_addresses(self.owner)
18- if len(owner_address) > 0:
19+ owner_addresses = get_contact_email_addresses(self.owner)
20+ for owner_address in owner_addresses:
21 simple_sendmail(fromaddress, owner_address, subject, message)
22
23 def newProbeRecord(self, log_file):
24diff --git a/lib/lp/registry/tests/test_distributionmirror.py b/lib/lp/registry/tests/test_distributionmirror.py
25index 08dae43..8f1423d 100644
26--- a/lib/lp/registry/tests/test_distributionmirror.py
27+++ b/lib/lp/registry/tests/test_distributionmirror.py
28@@ -170,14 +170,22 @@ class TestDistributionMirror(TestCaseWithFactory):
29 mirror.disable(notify_owner=True, log=log)
30 # A notification was sent to the owner and other to the mirror admins.
31 transaction.commit()
32- self.assertEqual(len(stub.test_emails), 2)
33+ self.assertEqual(len(stub.test_emails), 3)
34+
35+ # In order to prevent data disclosure, emails have to be sent to one
36+ # person each, ie it is not allowed to have multiple recipients in an
37+ # email's `to` field.
38+ for email in stub.test_emails:
39+ number_of_to_addresses = len(email[1])
40+ self.assertLess(number_of_to_addresses, 2)
41+
42 stub.test_emails = []
43
44 mirror.disable(notify_owner=True, log=log)
45 # Again, a notification was sent to the owner and other to the mirror
46 # admins.
47 transaction.commit()
48- self.assertEqual(len(stub.test_emails), 2)
49+ self.assertEqual(len(stub.test_emails), 3)
50 stub.test_emails = []
51
52 # For mirrors that have been probed more than once, we'll only notify
53@@ -187,7 +195,7 @@ class TestDistributionMirror(TestCaseWithFactory):
54 mirror.disable(notify_owner=True, log=log)
55 # A notification was sent to the owner and other to the mirror admins.
56 transaction.commit()
57- self.assertEqual(len(stub.test_emails), 2)
58+ self.assertEqual(len(stub.test_emails), 3)
59 stub.test_emails = []
60
61 # We can always disable notifications to the owner by passing
62@@ -195,7 +203,7 @@ class TestDistributionMirror(TestCaseWithFactory):
63 mirror.enabled = True
64 mirror.disable(notify_owner=False, log=log)
65 transaction.commit()
66- self.assertEqual(len(stub.test_emails), 1)
67+ self.assertEqual(len(stub.test_emails), 2)
68 stub.test_emails = []
69
70 mirror.enabled = False
71@@ -217,12 +225,12 @@ class TestDistributionMirror(TestCaseWithFactory):
72 transaction.commit()
73 stub.test_emails = []
74
75- # Disabling the mirror results in a single notification to the
76- # mirror admins.
77+ # Disabling the mirror results in one notification for each of
78+ # the three mirror admins.
79 self.factory.makeMirrorProbeRecord(mirror)
80 mirror.disable(notify_owner=True, log="It broke.")
81 transaction.commit()
82- self.assertEqual(len(stub.test_emails), 1)
83+ self.assertEqual(len(stub.test_emails), 3)
84
85 def test_permissions_for_resubmit(self):
86 self.assertRaises(
87diff --git a/lib/lp/translations/scripts/po_import.py b/lib/lp/translations/scripts/po_import.py
88index 0cb9200..8b7d9b7 100644
89--- a/lib/lp/translations/scripts/po_import.py
90+++ b/lib/lp/translations/scripts/po_import.py
91@@ -140,11 +140,8 @@ class TranslationsImport(LaunchpadCronScript):
92 katie = getUtility(ILaunchpadCelebrities).katie
93 if entry.importer == katie:
94 # Email import state to Debian imports email.
95- to_email = None
96- else:
97- to_email = get_contact_email_addresses(entry.importer)
98-
99- if to_email:
100+ return
101+ for to_email in get_contact_email_addresses(entry.importer):
102 text = MailWrapper().format(mail_body)
103 simple_sendmail(from_email, to_email, mail_subject, text)
104
105diff --git a/lib/lp/translations/scripts/tests/test_translations_import.py b/lib/lp/translations/scripts/tests/test_translations_import.py
106index 07d8776..ffc61fd 100644
107--- a/lib/lp/translations/scripts/tests/test_translations_import.py
108+++ b/lib/lp/translations/scripts/tests/test_translations_import.py
109@@ -188,6 +188,25 @@ class TestTranslationsImport(TestCaseWithFactory):
110 self.assertEqual(
111 [self.owner.preferredemail.email], self._getEmailRecipients())
112
113+ def test_notifies_uploader_separately(self):
114+ """Do not put several addresses into one `to`-field"""
115+ p1 = self.factory.makePerson()
116+ p2 = self.factory.makePerson()
117+ uploader = self.factory.makeTeam(
118+ members=[p1, p2],
119+ )
120+ entry = self._makeApprovedEntry(uploader=uploader)
121+ transaction.commit()
122+ self.script._importEntry(entry)
123+ transaction.commit()
124+
125+ # three emails get generated
126+ # and each has only one recipient
127+ self.assertEqual(3, len(stub.test_emails))
128+ for email in stub.test_emails:
129+ number_of_to_addresses = len(email[1])
130+ self.assertEqual(1, number_of_to_addresses)
131+
132 def test_does_not_notify_vcs_imports(self):
133 vcs_imports = getUtility(ILaunchpadCelebrities).vcs_imports
134 entry = self._makeApprovedEntry(vcs_imports)

Subscribers

People subscribed via source and target branches

to status/vote changes: