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
diff --git a/lib/lp/registry/model/distributionmirror.py b/lib/lp/registry/model/distributionmirror.py
index 8d3bd82..2454187 100644
--- a/lib/lp/registry/model/distributionmirror.py
+++ b/lib/lp/registry/model/distributionmirror.py
@@ -385,13 +385,14 @@ class DistributionMirror(SQLBase):
385 message = template % replacements385 message = template % replacements
386 subject = "Launchpad: Verification of %s failed" % self.name386 subject = "Launchpad: Verification of %s failed" % self.name
387387
388 mirror_admin_address = get_contact_email_addresses(388 mirror_admin_addresses = get_contact_email_addresses(
389 self.distribution.mirror_admin)389 self.distribution.mirror_admin)
390 simple_sendmail(fromaddress, mirror_admin_address, subject, message)390 for admin_address in mirror_admin_addresses:
391 simple_sendmail(fromaddress, admin_address, subject, message)
391392
392 if notify_owner:393 if notify_owner:
393 owner_address = get_contact_email_addresses(self.owner)394 owner_addresses = get_contact_email_addresses(self.owner)
394 if len(owner_address) > 0:395 for owner_address in owner_addresses:
395 simple_sendmail(fromaddress, owner_address, subject, message)396 simple_sendmail(fromaddress, owner_address, subject, message)
396397
397 def newProbeRecord(self, log_file):398 def newProbeRecord(self, log_file):
diff --git a/lib/lp/registry/tests/test_distributionmirror.py b/lib/lp/registry/tests/test_distributionmirror.py
index 08dae43..8f1423d 100644
--- a/lib/lp/registry/tests/test_distributionmirror.py
+++ b/lib/lp/registry/tests/test_distributionmirror.py
@@ -170,14 +170,22 @@ class TestDistributionMirror(TestCaseWithFactory):
170 mirror.disable(notify_owner=True, log=log)170 mirror.disable(notify_owner=True, log=log)
171 # A notification was sent to the owner and other to the mirror admins.171 # A notification was sent to the owner and other to the mirror admins.
172 transaction.commit()172 transaction.commit()
173 self.assertEqual(len(stub.test_emails), 2)173 self.assertEqual(len(stub.test_emails), 3)
174
175 # In order to prevent data disclosure, emails have to be sent to one
176 # person each, ie it is not allowed to have multiple recipients in an
177 # email's `to` field.
178 for email in stub.test_emails:
179 number_of_to_addresses = len(email[1])
180 self.assertLess(number_of_to_addresses, 2)
181
174 stub.test_emails = []182 stub.test_emails = []
175183
176 mirror.disable(notify_owner=True, log=log)184 mirror.disable(notify_owner=True, log=log)
177 # Again, a notification was sent to the owner and other to the mirror185 # Again, a notification was sent to the owner and other to the mirror
178 # admins.186 # admins.
179 transaction.commit()187 transaction.commit()
180 self.assertEqual(len(stub.test_emails), 2)188 self.assertEqual(len(stub.test_emails), 3)
181 stub.test_emails = []189 stub.test_emails = []
182190
183 # For mirrors that have been probed more than once, we'll only notify191 # For mirrors that have been probed more than once, we'll only notify
@@ -187,7 +195,7 @@ class TestDistributionMirror(TestCaseWithFactory):
187 mirror.disable(notify_owner=True, log=log)195 mirror.disable(notify_owner=True, log=log)
188 # A notification was sent to the owner and other to the mirror admins.196 # A notification was sent to the owner and other to the mirror admins.
189 transaction.commit()197 transaction.commit()
190 self.assertEqual(len(stub.test_emails), 2)198 self.assertEqual(len(stub.test_emails), 3)
191 stub.test_emails = []199 stub.test_emails = []
192200
193 # We can always disable notifications to the owner by passing201 # We can always disable notifications to the owner by passing
@@ -195,7 +203,7 @@ class TestDistributionMirror(TestCaseWithFactory):
195 mirror.enabled = True203 mirror.enabled = True
196 mirror.disable(notify_owner=False, log=log)204 mirror.disable(notify_owner=False, log=log)
197 transaction.commit()205 transaction.commit()
198 self.assertEqual(len(stub.test_emails), 1)206 self.assertEqual(len(stub.test_emails), 2)
199 stub.test_emails = []207 stub.test_emails = []
200208
201 mirror.enabled = False209 mirror.enabled = False
@@ -217,12 +225,12 @@ class TestDistributionMirror(TestCaseWithFactory):
217 transaction.commit()225 transaction.commit()
218 stub.test_emails = []226 stub.test_emails = []
219227
220 # Disabling the mirror results in a single notification to the228 # Disabling the mirror results in one notification for each of
221 # mirror admins.229 # the three mirror admins.
222 self.factory.makeMirrorProbeRecord(mirror)230 self.factory.makeMirrorProbeRecord(mirror)
223 mirror.disable(notify_owner=True, log="It broke.")231 mirror.disable(notify_owner=True, log="It broke.")
224 transaction.commit()232 transaction.commit()
225 self.assertEqual(len(stub.test_emails), 1)233 self.assertEqual(len(stub.test_emails), 3)
226234
227 def test_permissions_for_resubmit(self):235 def test_permissions_for_resubmit(self):
228 self.assertRaises(236 self.assertRaises(
diff --git a/lib/lp/translations/scripts/po_import.py b/lib/lp/translations/scripts/po_import.py
index 0cb9200..8b7d9b7 100644
--- a/lib/lp/translations/scripts/po_import.py
+++ b/lib/lp/translations/scripts/po_import.py
@@ -140,11 +140,8 @@ class TranslationsImport(LaunchpadCronScript):
140 katie = getUtility(ILaunchpadCelebrities).katie140 katie = getUtility(ILaunchpadCelebrities).katie
141 if entry.importer == katie:141 if entry.importer == katie:
142 # Email import state to Debian imports email.142 # Email import state to Debian imports email.
143 to_email = None143 return
144 else:144 for to_email in get_contact_email_addresses(entry.importer):
145 to_email = get_contact_email_addresses(entry.importer)
146
147 if to_email:
148 text = MailWrapper().format(mail_body)145 text = MailWrapper().format(mail_body)
149 simple_sendmail(from_email, to_email, mail_subject, text)146 simple_sendmail(from_email, to_email, mail_subject, text)
150147
diff --git a/lib/lp/translations/scripts/tests/test_translations_import.py b/lib/lp/translations/scripts/tests/test_translations_import.py
index 07d8776..ffc61fd 100644
--- a/lib/lp/translations/scripts/tests/test_translations_import.py
+++ b/lib/lp/translations/scripts/tests/test_translations_import.py
@@ -188,6 +188,25 @@ class TestTranslationsImport(TestCaseWithFactory):
188 self.assertEqual(188 self.assertEqual(
189 [self.owner.preferredemail.email], self._getEmailRecipients())189 [self.owner.preferredemail.email], self._getEmailRecipients())
190190
191 def test_notifies_uploader_separately(self):
192 """Do not put several addresses into one `to`-field"""
193 p1 = self.factory.makePerson()
194 p2 = self.factory.makePerson()
195 uploader = self.factory.makeTeam(
196 members=[p1, p2],
197 )
198 entry = self._makeApprovedEntry(uploader=uploader)
199 transaction.commit()
200 self.script._importEntry(entry)
201 transaction.commit()
202
203 # three emails get generated
204 # and each has only one recipient
205 self.assertEqual(3, len(stub.test_emails))
206 for email in stub.test_emails:
207 number_of_to_addresses = len(email[1])
208 self.assertEqual(1, number_of_to_addresses)
209
191 def test_does_not_notify_vcs_imports(self):210 def test_does_not_notify_vcs_imports(self):
192 vcs_imports = getUtility(ILaunchpadCelebrities).vcs_imports211 vcs_imports = getUtility(ILaunchpadCelebrities).vcs_imports
193 entry = self._makeApprovedEntry(vcs_imports)212 entry = self._makeApprovedEntry(vcs_imports)

Subscribers

People subscribed via source and target branches

to status/vote changes: