1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
|
Security Teams
==============
Responsibility for security-related bugs, are modelled in Launchpad with
a "security contact" on a Distribution or a Product.
>>> from itertools import chain
>>> from zope.component import getUtility
>>> from lp.bugs.interfaces.securitycontact import IHasSecurityContact
>>> from lp.registry.interfaces.distribution import IDistributionSet
>>> from lp.registry.interfaces.person import IPersonSet
>>> from lp.registry.interfaces.product import IProductSet
>>> personset = getUtility(IPersonSet)
>>> productset = getUtility(IProductSet)
>>> ubuntu = getUtility(IDistributionSet).get(1)
>>> firefox = productset.get(4)
>>> IHasSecurityContact.providedBy(ubuntu)
True
>>> IHasSecurityContact.providedBy(firefox)
True
>>> mark = personset.get(1)
>>> ubuntu_team = personset.get(17)
Security contacts are set through properties.
>>> login("foo.bar@canonical.com")
>>> ubuntu.security_contact = mark
>>> firefox.security_contact = ubuntu_team
>>> print ubuntu.security_contact.name
mark
>>> print firefox.security_contact.name
ubuntu-team
When creating a bug, use the security_related flag to indicate that the
bug is a security vulnerability, and the security contact should be
subscribed to the bug, even when it's marked private.
>>> from lp.services.webapp.interfaces import ILaunchBag
>>> from lp.bugs.interfaces.bug import CreateBugParams
>>> ubuntu_firefox = ubuntu.getSourcePackage("mozilla-firefox")
>>> params = CreateBugParams(
... owner=getUtility(ILaunchBag).user,
... title="a security bug",
... comment="this is an example security bug",
... security_related=True, private=True)
>>> bug = ubuntu.createBug(params)
>>> bug.security_related
True
>>> bug.private
True
The reporter, Foo Bar, and the Ubuntu security contact, Mark
Shuttleworth are both subscribed to the bug.
>>> def subscriber_names(bug):
... subscribers = chain(
... bug.getDirectSubscribers(),
... bug.getIndirectSubscribers())
... return sorted(subscriber.name for subscriber in subscribers)
>>> subscriber_names(bug)
[u'mark', u'name16']
If the bug were not reported as security-related, only Foo Bar would
have been subscribed:
>>> from lp.services.webapp.interfaces import ILaunchBag
>>> ubuntu_firefox = ubuntu.getSourcePackage("mozilla-firefox")
>>> params = CreateBugParams(
... owner=getUtility(ILaunchBag).user,
... title="a security bug",
... comment="this is an example security bug",
... security_related=False)
>>> bug = ubuntu.createBug(params)
>>> bug.security_related
False
>>> subscriber_names(bug)
[u'name16', u'ubuntu-team']
Likewise, filing a security-related bug on Firefox will subscribe the
security contact, the Ubuntu team, to the bug.
>>> params = CreateBugParams(
... owner=getUtility(ILaunchBag).user,
... title="another security bug",
... comment="this is another security bug",
... security_related=True, private=True)
>>> bug = firefox.createBug(params)
>>> bug.security_related
True
>>> bug.private
True
>>> subscriber_names(bug)
[u'name16', u'ubuntu-team']
Again, if the bug were not reported as security-related, the security
contact, the Ubuntu Team, would not have been subscribed:
>>> params = CreateBugParams(
... owner=getUtility(ILaunchBag).user,
... title="another security bug",
... comment="this is another security bug",
... security_related=False)
>>> bug = firefox.createBug(params)
>>> bug.security_related
False
>>> subscriber_names(bug)
[u'name12', u'name16']
When no security contact exists, only the reporter and product
registrant get subscribed.
>>> firefox.security_contact = None
>>> print firefox.owner.name
name12
>>> params = CreateBugParams(
... owner=getUtility(ILaunchBag).user,
... title="another security bug",
... comment="this is another security bug",
... security_related=True, private=True)
>>> bug = firefox.createBug(params)
>>> bug.security_related
True
>>> subscriber_names(bug)
[u'name12', u'name16']
When a bug is reported in another package or upstream, the security
contact for that package or upstream is automatically subscribed to the
bug, *if the bug is public*. Malone never auto-subscribes anyone to
private bugs, except when the user chooses that option when filing a
security bug.
Let's first demonstrate adding a task to a public bug causing the
security contact of the new product to be subscribed.
>>> evolution = productset.get(5)
We'll set lifeless as the security_contact for evolution.
>>> from lp.bugs.interfaces.bugtask import IBugTaskSet
(Make the bug public to ensure the security contact will get
subscribed.)
>>> bug.setPrivate(False, getUtility(ILaunchBag).user)
True
>>> lifeless = personset.get(2)
>>> print lifeless.name
lifeless
>>> evolution.security_contact = lifeless
>>> foobar = personset.get(16)
>>> print foobar.name
name16
>>> bugtaskset = getUtility(IBugTaskSet)
>>> bug_in_evolution = bugtaskset.createTask(bug, foobar, evolution)
lifeless is subscribed to the public security bug when a task is added
for evolution.
>>> subscriber_names(bug)
[u'lifeless', u'name12', u'name16']
But if we repeat the process, using a private bug, he won't be
subscribed.
>>> params = CreateBugParams(
... owner=getUtility(ILaunchBag).user,
... title="another security bug",
... comment="this is private security bug",
... private=True, security_related=True)
>>> bug = firefox.createBug(params)
>>> bug.security_related
True
>>> bug.private
True
>>> subscriber_names(bug)
[u'name12', u'name16']
We are moving away from allowing private bugs to affect multiple projects.
This is required still for some teams until they update their tools and
processes. So we need to use a feature flag to perform the next tests.
>>> from lp.services.features.testing import FeatureFixture
>>> feature_flag = {
... 'disclosure.allow_multipillar_private_bugs.enabled': 'on'}
>>> privacy_flags = FeatureFixture(feature_flag)
>>> privacy_flags.setUp()
>>> bug_in_evolution = bugtaskset.createTask(bug, foobar, evolution)
>>> subscriber_names(bug)
[u'name12', u'name16']
Finally, reassigning a public bug to a different product will subscribe
the new security contact, if present and if the original bug was marked
as a security issue. Let's set stub to the security contact for
thunderbird to see how this works.
>>> thunderbird = productset.get(8)
>>> print thunderbird.name
thunderbird
>>> stub = personset.get(22)
>>> print stub.name
stub
>>> thunderbird.security_contact = stub
>>> from zope.event import notify
>>> from lazr.lifecycle.event import ObjectModifiedEvent
>>> from lazr.lifecycle.snapshot import Snapshot
>>> from lp.bugs.interfaces.bugtask import IBugTask
>>> old_state = Snapshot(bug_in_evolution, providing=IBugTask)
>>> bug_in_evolution.transitionToTarget(thunderbird)
>>> bug_product_changed = ObjectModifiedEvent(
... bug_in_evolution, old_state, ["product"])
First, let's set the bug to non security related with the bug still marked,
private and notice that the subscription list doesn't change:
>>> bug.private
True
>>> bug.setSecurityRelated(False, getUtility(ILaunchBag).user)
True
>>> subscriber_names(bug)
[u'name12', u'name16']
Now the bug is marked as security related, when also marked public does cause
stub to get subscribed:
>>> bug.setPrivate(False, getUtility(ILaunchBag).user)
True
>>> bug.setSecurityRelated(True, getUtility(ILaunchBag).user)
True
>>> bug.security_related
True
>>> subscriber_names(bug)
[u'name12', u'name16', u'stub']
But if it is not a security issue originally, stub does not get
subscribed when moving it to the new project.
>>> bug.unsubscribe(stub, stub)
>>> subscriber_names(bug)
[u'name12', u'name16']
>>> bug.setSecurityRelated(False, getUtility(ILaunchBag).user)
True
>>> bug.security_related
False
>>> notify(bug_product_changed)
>>> subscriber_names(bug)
[u'name12', u'name16']
When a bug becomes security-related, the security contacts for the pillars it
affects are subscribed to it. This happens regardless of whether the feature
flag is set.
We currently use a feature flag to control who is subscribed when a bug is
made security related.
>>> feature_flag = {
... 'disclosure.enhanced_private_bug_subscriptions.enabled': 'on'}
>>> security_flags = FeatureFixture(feature_flag)
>>> security_flags.setUp()
>>> from zope.event import notify
>>> from lazr.lifecycle.event import ObjectModifiedEvent
>>> from lazr.lifecycle.snapshot import Snapshot
>>> from lp.bugs.interfaces.bug import IBug
>>> product = factory.makeProduct()
>>> product.security_contact = factory.makePerson(
... displayname='Product Security Contact')
>>> distribution = factory.makeDistribution()
>>> distribution.security_contact = factory.makePerson(
... displayname='Distribution Security Contact')
>>> reporter = factory.makePerson(displayname=u'Bug Reporter')
>>> bug = factory.makeBug(product=product, owner=reporter)
>>> bug.addTask(owner=reporter, target=distribution)
<BugTask ...>
>>> old_state = Snapshot(bug, providing=IBug)
>>> bug.setSecurityRelated(True, getUtility(ILaunchBag).user)
True
>>> notify(ObjectModifiedEvent(bug, old_state, ['security_related']))
>>> for subscriber_name in sorted(
... s.displayname for s in bug.getDirectSubscribers()):
... print subscriber_name
Bug Reporter
Distribution Security Contact
Product Security Contact
Clean up the feature flags.
>>> security_flags.cleanUp()
>>> privacy_flags.cleanUp()
And once more without the feature flag.
>>> product = factory.makeProduct()
>>> product.security_contact = factory.makePerson(
... displayname='Product Security Contact')
>>> distribution = factory.makeDistribution()
>>> distribution.security_contact = factory.makePerson(
... displayname='Distribution Security Contact')
>>> reporter = factory.makePerson(displayname=u'Bug Reporter')
>>> bug = factory.makeBug(product=product, owner=reporter)
>>> bug.addTask(owner=reporter, target=distribution)
<BugTask ...>
>>> old_state = Snapshot(bug, providing=IBug)
>>> bug.setSecurityRelated(True, getUtility(ILaunchBag).user)
True
>>> notify(ObjectModifiedEvent(bug, old_state, ['security_related']))
>>> for subscriber_name in sorted(
... s.displayname for s in bug.getDirectSubscribers()):
... print subscriber_name
Bug Reporter
Distribution Security Contact
Product Security Contact
|