Merge ~ilasc/launchpad:add-vulnerability-orm into launchpad:master

Proposed by Ioana Lasc
Status: Merged
Approved by: Ioana Lasc
Approved revision: bbf4153e065a63d05fa15e0f5fe4d29e01ddc836
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~ilasc/launchpad:add-vulnerability-orm
Merge into: launchpad:master
Diff against target: 747 lines (+678/-0)
6 files modified
lib/lp/bugs/configure.zcml (+45/-0)
lib/lp/bugs/interfaces/vulnerability.py (+255/-0)
lib/lp/bugs/model/tests/test_vulnerability.py (+121/-0)
lib/lp/bugs/model/vulnerability.py (+191/-0)
lib/lp/security.py (+12/-0)
lib/lp/testing/factory.py (+54/-0)
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+415966@code.launchpad.net

Commit message

Add Vulnerability and VulnerabilityActivity

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

Be careful to ensure that non-nullable DB columns have `required=True` in their interface and `allow_none=False` in their model. I think I caught them all, but it might be helpful for you to do another pass after applying my suggestions.

Aside from that, most of my suggestions are relatively small, except for replacing `BugVulnerability` with `XRef` and my comments on an earlier thread about sorting out permissions on `Vulnerability`.

review: Approve
Revision history for this message
Ioana Lasc (ilasc) wrote :

Thanks Colin, this might be worth another look now that BugVulnerability was replaced with XRef.

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/bugs/configure.zcml b/lib/lp/bugs/configure.zcml
index 3064699..5a033b1 100644
--- a/lib/lp/bugs/configure.zcml
+++ b/lib/lp/bugs/configure.zcml
@@ -593,6 +593,51 @@
593 interface="lp.bugs.interfaces.cve.ICveSet"/>593 interface="lp.bugs.interfaces.cve.ICveSet"/>
594 </securedutility>594 </securedutility>
595595
596 <!-- Vulnerability -->
597 <class class="lp.bugs.model.vulnerability.Vulnerability">
598 <require
599 permission="launchpad.View"
600 interface="lp.bugs.interfaces.vulnerability.IVulnerabilityView
601 lp.bugs.interfaces.vulnerability.IVulnerabilityEditableAttributes" />
602 <require
603 permission="launchpad.Edit"
604 interface="lp.bugs.interfaces.vulnerability.IVulnerabilityEdit"
605 set_schema="lp.bugs.interfaces.vulnerability.IVulnerabilityEditableAttributes" />
606
607 <!-- IBugLinkTarget -->
608 <allow
609 attributes="
610 bugs"/>
611 <require
612 permission="launchpad.Edit"
613 attributes="
614 linkBug
615 unlinkBug"/>
616 </class>
617 <class class="lp.bugs.model.vulnerability.VulnerabilitySet">
618 <allow interface="lp.bugs.interfaces.vulnerability.IVulnerabilitySet" />
619 </class>
620 <securedutility
621 class="lp.bugs.model.vulnerability.VulnerabilitySet"
622 provides="lp.bugs.interfaces.vulnerability.IVulnerabilitySet">
623 <allow interface="lp.bugs.interfaces.vulnerability.IVulnerabilitySet" />
624 </securedutility>
625
626 <!-- VulnerabilityActivity -->
627 <class class="lp.bugs.model.vulnerability.VulnerabilityActivity">
628 <require
629 permission="launchpad.View"
630 interface="lp.bugs.interfaces.vulnerability.IVulnerabilityActivity" />
631 </class>
632 <class class="lp.bugs.model.vulnerability.VulnerabilityActivitySet">
633 <allow interface="lp.bugs.interfaces.vulnerability.IVulnerabilityActivitySet" />
634 </class>
635 <securedutility
636 class="lp.bugs.model.vulnerability.VulnerabilityActivitySet"
637 provides="lp.bugs.interfaces.vulnerability.IVulnerabilityActivitySet">
638 <allow interface="lp.bugs.interfaces.vulnerability.IVulnerabilityActivitySet" />
639 </securedutility>
640
596 <!-- BugSubscription -->641 <!-- BugSubscription -->
597642
598 <class643 <class
diff --git a/lib/lp/bugs/interfaces/vulnerability.py b/lib/lp/bugs/interfaces/vulnerability.py
599new file mode 100644644new file mode 100644
index 0000000..8fa01da
--- /dev/null
+++ b/lib/lp/bugs/interfaces/vulnerability.py
@@ -0,0 +1,255 @@
1# Copyright 2022 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Vulnerability interfaces."""
5
6__all__ = [
7 'IVulnerability',
8 'IVulnerabilityActivity',
9 'IVulnerabilityActivitySet',
10 'IVulnerabilitySet',
11 'VulnerabilityChange',
12 'VulnerabilityStatus'
13 ]
14
15from lazr.enum import (
16 DBEnumeratedType,
17 DBItem,
18 )
19from lazr.restful.fields import Reference
20from zope.interface import Interface
21from zope.schema import (
22 Choice,
23 Datetime,
24 Int,
25 TextLine,
26 )
27
28from lp import _
29from lp.app.enums import InformationType
30from lp.app.interfaces.informationtype import IInformationType
31from lp.bugs.interfaces.bugtask import BugTaskImportance
32from lp.bugs.interfaces.cve import ICve
33from lp.registry.interfaces.distribution import IDistribution
34from lp.registry.interfaces.person import IPerson
35
36
37class VulnerabilityChange(DBEnumeratedType):
38 """Type of change in vulnerability
39
40 We use this enum to track changes occurring in
41 data stored in the vulnerability table.
42 """
43
44 STATUS = DBItem(0, """
45 Status
46
47 The status of the vulnerability changed.
48 """)
49
50 DESCRIPTION = DBItem(1, """
51 Description
52
53 The description of the vulnerability changed.
54 """)
55
56 NOTES = DBItem(2, """
57 Notes
58
59 The notes on the vulnerability changed.
60 """)
61
62 MITIGATION = DBItem(3, """
63 Mitigation
64
65 Mitigation for this vulnerability changed.
66 """)
67
68 IMPORTANCE = DBItem(4, """
69 Importance
70
71 The importance assigned for this vulnerability changed.
72 """)
73
74 IMPORTANCE_EXPLANATION = DBItem(5, """
75 Importance explanation
76
77 The importance explanation changed for this vulnerability.
78 """)
79
80 PRIVACY = DBItem(6, """
81 Privacy
82
83 The privacy for this vulnerability changed.
84 """)
85
86
87class VulnerabilityStatus(DBEnumeratedType):
88 """Vulnerability status"""
89
90 NEEDS_TRIAGE = DBItem(0, """
91 Needs triage
92
93 Not looked at yet.
94 """)
95
96 ACTIVE = DBItem(1, """
97 Active
98
99 The vulnerability is active.
100 """)
101
102 IGNORED = DBItem(2, """
103 Ignored
104
105 The vulnerability is currently ignored.
106 """)
107
108 RETIRED = DBItem(3, """
109 Retired
110
111 This vulnerability is now retired.
112 """)
113
114
115class IVulnerabilityView(Interface):
116 """`IVulnerability` attributes that require launchpad.View."""
117
118 id = Int(title=_("ID"), required=True, readonly=True)
119
120 distribution = Reference(IDistribution, title=_("Distribution"),
121 required=True, readonly=True)
122
123 cve = Reference(ICve, title=_('External CVE reference corresponding'
124 ' to this vulnerability, if any.'),
125 required=False, readonly=True)
126
127 date_created = Datetime(
128 title=_("The date this vulnerability was made public."),
129 required=True, readonly=True)
130
131 creator = Reference(
132 title=_('Person'), schema=IPerson, required=True, readonly=True)
133
134
135class IVulnerabilityEditableAttributes(Interface):
136 """`IVulnerability` attributes that can be edited.
137
138 These attributes need launchpad.View to see, and launchpad.Edit to change.
139 """
140
141 status = Choice(
142 title=_('Result of the report'), readonly=True,
143 required=True, vocabulary=VulnerabilityStatus)
144
145 description = TextLine(
146 title=_("A short description of the vulnerability."), required=False,
147 readonly=False)
148
149 notes = TextLine(
150 title=_("Free-form notes for this vulnerability."), required=False,
151 readonly=False)
152
153 mitigation = TextLine(
154 title=_("Explains why we're ignoring a vulnerability."),
155 required=False, readonly=False)
156
157 importance = Choice(title=_('Importance used to indicate work priority,'
158 ' not severity'),
159 vocabulary=BugTaskImportance, required=True,
160 default=BugTaskImportance.UNDECIDED, readonly=True)
161
162 importance_explanation = TextLine(
163 title=_("Used to explain why our importance differs "
164 "from somebody else's CVSS score."),
165 required=False, readonly=False)
166
167 information_type = Choice(
168 title=_("Information type"), vocabulary=InformationType,
169 required=True, readonly=False, default=InformationType.PUBLIC,
170 description=_(
171 "Indicates privacy of the vulnerability."))
172
173 date_made_public = Datetime(
174 title=_("The date this vulnerability was made public."),
175 required=False, readonly=False)
176
177
178class IVulnerabilityEdit(Interface):
179 """`IVulnerability` attributes that require launchpad.Edit."""
180
181
182class IVulnerability(IVulnerabilityView,
183 IVulnerabilityEditableAttributes,
184 IVulnerabilityEdit, IInformationType):
185 """Contract describing a vulnerability."""
186
187
188class IVulnerabilitySet(Interface):
189 """The set of all vulnerabilities."""
190
191 def new(distribution, status, importance,
192 creator, information_type=InformationType.PUBLIC, cve=None,
193 description=None, notes=None, mitigation=None,
194 importance_explanation=None, date_made_public=None):
195 """Return a new vulnerability.
196
197 :param distribution: The distribution for the vulnerability.
198 :param status: The status of the vulnerability.
199 :param importance: Indicates work priority, not severity.
200 :param creator: The user that created the vulnerability.
201 :param information_type: The privacy of the vulnerability.
202 :param cve: A `Cve` for which the vulnerability is being created.
203 :param description: The description of the vulnerability.
204 :param notes: The notes for the vulnerability.
205 :param mitigation: A short summary of the result.
206 :param importance_explanation: Used to explain why our importance
207 differs from somebody else's CVSS score.
208 :param date_made_public: The date this vulnerability was made public.
209 """
210
211
212class IVulnerabilityActivity(Interface):
213 """`IVulnerabilityActivity` attributes that require launchpad.View."""
214
215 id = Int(title=_("ID"), required=True, readonly=True)
216
217 vulnerability = Reference(IVulnerability, title=_('Vulnerability'),
218 required=True, readonly=True)
219
220 date_changed = Datetime(
221 title=_("When activity last changed for this vulnerability."),
222 required=True, readonly=True)
223
224 changer = Reference(IPerson, title=_("Changer"), required=True,
225 readonly=True,
226 description=_("The person that made the changes."))
227
228 what_changed = Choice(
229 title=_('Indicates what field changed for the vulnerability.'),
230 readonly=True,
231 required=True, vocabulary=VulnerabilityChange)
232
233 old_value = TextLine(
234 title=_("Indicates the value prior to the change."), required=False,
235 readonly=True)
236
237 new_value = TextLine(
238 title=_("Indicates the current value."), required=False,
239 readonly=True)
240
241
242class IVulnerabilityActivitySet(Interface):
243 """The set of all activities for a certain vulnerability."""
244
245 def new(vulnerability, changer, what_changed=None,
246 old_value=None, new_value=None):
247 """Return a new vulnerability activity.
248
249 :param vulnerability: The vulnerability for this activity.
250 :param changer: The `Person` that performed the activity.
251 :param what_changed: The 'VulnerabilityChange' that occurred
252 for this vulnerability.
253 :param old_value: Indicates the value prior to the change.
254 :param new_value: Indicates the current value.
255 """
diff --git a/lib/lp/bugs/model/tests/test_vulnerability.py b/lib/lp/bugs/model/tests/test_vulnerability.py
0new file mode 100644256new file mode 100644
index 0000000..a9d7bc2
--- /dev/null
+++ b/lib/lp/bugs/model/tests/test_vulnerability.py
@@ -0,0 +1,121 @@
1# Copyright 2022 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Tests for the vulnerability and related models."""
5from zope.component import getUtility
6
7from lp.bugs.interfaces.vulnerability import (
8 IVulnerabilitySet,
9 VulnerabilityChange,
10 )
11from lp.services.webapp.authorization import check_permission
12from lp.testing import (
13 admin_logged_in,
14 anonymous_logged_in,
15 person_logged_in,
16 TestCaseWithFactory,
17 verifyObject,
18 )
19from lp.testing.layers import DatabaseFunctionalLayer
20
21
22class TestVulnerability(TestCaseWithFactory):
23
24 layer = DatabaseFunctionalLayer
25
26 def setUp(self):
27 super().setUp()
28 self.distribution = self.factory.makeDistribution()
29 self.vulnerability = self.factory.makeVulnerability(
30 distribution=self.distribution)
31
32 def test_random_user(self):
33 with person_logged_in(self.factory.makePerson()):
34 self.assertTrue(
35 check_permission("launchpad.View", self.vulnerability))
36 self.assertFalse(
37 check_permission("launchpad.Edit", self.vulnerability))
38
39 def test_admin(self):
40 with admin_logged_in():
41 self.assertTrue(
42 check_permission("launchpad.View", self.vulnerability))
43 self.assertTrue(
44 check_permission("launchpad.Edit", self.vulnerability))
45
46 def test_non_admin(self):
47 with person_logged_in(self.distribution.owner):
48 self.assertTrue(
49 check_permission("launchpad.View", self.vulnerability))
50 self.assertTrue(
51 check_permission("launchpad.Edit", self.vulnerability))
52
53 def test_anonymous(self):
54 with anonymous_logged_in():
55 self.assertFalse(
56 check_permission("launchpad.View", self.vulnerability))
57 self.assertFalse(
58 check_permission("launchpad.Edit", self.vulnerability))
59
60
61class TestVulnerabilityActivity(TestCaseWithFactory):
62
63 layer = DatabaseFunctionalLayer
64
65 def test_vulnerability_activity_changes(self):
66 vulnerability = self.factory.makeVulnerability()
67 changer = self.factory.makePerson()
68 activity = self.factory.makeVulnerabilityActivity(
69 vulnerability=vulnerability, changer=None)
70 with person_logged_in(changer):
71 self.assertTrue(VulnerabilityChange.DESCRIPTION,
72 activity.what_changed)
73
74
75class TestVulnerabilitySet(TestCaseWithFactory):
76
77 layer = DatabaseFunctionalLayer
78
79 def test_VulnerabilitySet_implements_IVulnerabilitySet(self):
80 vulnerabilitySet = getUtility(IVulnerabilitySet)
81 self.assertTrue(verifyObject(IVulnerabilitySet, vulnerabilitySet))
82
83 def test_bugVulnerabilityCount(self):
84 # vulnerability3 linked bugs will not be reflected
85 # in computations of linked bugs on
86 # vulnerability 1 and 2
87
88 vulnerability1 = self.factory.makeVulnerability()
89 vulnerability2 = self.factory.makeVulnerability()
90 vulnerability3 = self.factory.makeVulnerability()
91 bug1 = self.factory.makeBug()
92 bug2 = self.factory.makeBug()
93 initial_number = len(vulnerability1.bugs)
94 with admin_logged_in():
95 vulnerability1.linkBug(bug1)
96 vulnerability3.linkBug(bug1)
97 vulnerability3.linkBug(bug2)
98
99 self.assertEqual(
100 initial_number + 1,
101 len(vulnerability1.bugs))
102
103 with admin_logged_in():
104 vulnerability2.linkBug(bug2)
105 self.assertEqual(
106 initial_number + 2,
107 (len(vulnerability1.bugs) + len(vulnerability2.bugs)))
108
109 with admin_logged_in():
110 vulnerability2.linkBug(bug1)
111 self.assertEqual(
112 initial_number + 3,
113 (len(vulnerability1.bugs) + len(vulnerability2.bugs)))
114
115 with admin_logged_in():
116 vulnerability1.unlinkBug(bug1)
117 vulnerability2.unlinkBug(bug2)
118 vulnerability2.unlinkBug(bug1)
119 self.assertEqual(
120 initial_number,
121 (len(vulnerability1.bugs) + len(vulnerability2.bugs)))
diff --git a/lib/lp/bugs/model/vulnerability.py b/lib/lp/bugs/model/vulnerability.py
0new file mode 100644122new file mode 100644
index 0000000..77ebcb3
--- /dev/null
+++ b/lib/lp/bugs/model/vulnerability.py
@@ -0,0 +1,191 @@
1# Copyright 2022 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4__all__ = [
5 'Vulnerability',
6 'VulnerabilitySet',
7 ]
8
9import operator
10
11import pytz
12from storm.locals import (
13 DateTime,
14 Int,
15 Reference,
16 Unicode,
17 )
18from zope.component import getUtility
19from zope.interface import implementer
20
21from lp.app.enums import InformationType
22from lp.bugs.interfaces.buglink import IBugLinkTarget
23from lp.bugs.interfaces.bugtask import BugTaskImportance
24from lp.bugs.interfaces.vulnerability import (
25 IVulnerability,
26 IVulnerabilityActivity,
27 IVulnerabilityActivitySet,
28 IVulnerabilitySet,
29 VulnerabilityChange,
30 VulnerabilityStatus,
31 )
32from lp.bugs.model.bug import Bug
33from lp.bugs.model.buglinktarget import BugLinkTargetMixin
34from lp.services.database import bulk
35from lp.services.database.constants import UTC_NOW
36from lp.services.database.enumcol import DBEnum
37from lp.services.database.interfaces import IStore
38from lp.services.database.stormbase import StormBase
39from lp.services.xref.interfaces import IXRefSet
40
41
42@implementer(IVulnerability, IBugLinkTarget)
43class Vulnerability(StormBase, BugLinkTargetMixin):
44 __storm_table__ = 'Vulnerability'
45
46 id = Int(primary=True)
47
48 distribution_id = Int(name="distribution", allow_none=False)
49 distribution = Reference(distribution_id, "Distribution.id")
50
51 cve_id = Int(name="cve", allow_none=True, default=None)
52 cve = Reference(cve_id, "Cve.id")
53
54 status = DBEnum(name='status', allow_none=False,
55 enum=VulnerabilityStatus)
56
57 description = Unicode(name='description', allow_none=True)
58
59 notes = Unicode(name='notes', allow_none=True)
60
61 mitigation = Unicode(name='mitigation', allow_none=True)
62
63 importance = DBEnum(
64 name='importance', allow_none=False,
65 enum=BugTaskImportance,
66 default=BugTaskImportance.UNDECIDED)
67
68 importance_explanation = Unicode(
69 name='importance_explanation', allow_none=True)
70
71 information_type = DBEnum(
72 enum=InformationType, default=InformationType.PUBLIC,
73 allow_none=False, name="information_type")
74
75 date_created = DateTime(
76 name='date_created', tzinfo=pytz.UTC, allow_none=False,
77 default=UTC_NOW)
78
79 date_made_public = DateTime(
80 name='date_made_public', tzinfo=pytz.UTC, allow_none=True)
81
82 creator_id = Int(name='creator', allow_none=False)
83 creator = Reference(creator_id, 'Person.id')
84
85 def __init__(self, distribution, status, importance,
86 creator, information_type=InformationType.PUBLIC, cve=None,
87 description=None, notes=None, mitigation=None,
88 importance_explanation=None, date_made_public=None):
89 super().__init__()
90 self.distribution = distribution
91 self.cve = cve
92 self.status = status
93 self.importance = importance
94 self.information_type = information_type
95 self.creator = creator
96 self.description = description
97 self.notes = notes
98 self.mitigation = mitigation
99 self.importance_explanation = importance_explanation
100 self.date_made_public = date_made_public
101 self.date_created = UTC_NOW
102
103 @property
104 def bugs(self):
105 bug_ids = [
106 int(id) for _, id in getUtility(IXRefSet).findFrom(
107 ('vulnerability', str(self.id)), types=['bug'])]
108 return list(sorted(
109 bulk.load(Bug, bug_ids), key=operator.attrgetter('id')))
110
111 def createBugLink(self, bug, props=None):
112 """See BugLinkTargetMixin."""
113 if props is None:
114 props = {}
115 getUtility(IXRefSet).create(
116 {('vulnerability', str(self.id)): {('bug', str(bug.id)): props}})
117
118 def deleteBugLink(self, bug):
119 """See BugLinkTargetMixin."""
120 getUtility(IXRefSet).delete(
121 {('vulnerability', str(self.id)): [('bug', str(bug.id))]})
122
123
124@implementer(IVulnerabilitySet)
125class VulnerabilitySet:
126
127 def new(self, distribution, status, importance,
128 creator, information_type=InformationType.PUBLIC, cve=None,
129 description=None, notes=None, mitigation=None,
130 importance_explanation=None, date_made_public=None):
131 """See `IVulnerabilitySet`."""
132 store = IStore(Vulnerability)
133 vulnerability = Vulnerability(distribution=distribution,
134 creator=creator, cve=cve,
135 status=status, description=description,
136 notes=notes, mitigation=mitigation,
137 importance=importance,
138 information_type=information_type,
139 importance_explanation=
140 importance_explanation,
141 date_made_public=date_made_public)
142 store.add(vulnerability)
143 return vulnerability
144
145
146@implementer(IVulnerabilityActivity)
147class VulnerabilityActivity(StormBase):
148 __storm_table__ = 'VulnerabilityActivity'
149
150 id = Int(primary=True)
151
152 vulnerability_id = Int(name="vulnerability", allow_none=False)
153 vulnerability = Reference(vulnerability_id, "Vulnerability.id")
154
155 changer_id = Int(name="changer", allow_none=False)
156 changer = Reference(changer_id, "Person.id")
157
158 date_changed = DateTime(
159 name='date_changed', tzinfo=pytz.UTC, allow_none=False)
160
161 what_changed = DBEnum(name='what_changed', allow_none=False,
162 enum=VulnerabilityChange)
163
164 old_value = Unicode(name='old_value', allow_none=True)
165
166 new_value = Unicode(name='new_value', allow_none=True)
167
168 def __init__(self, vulnerability, changer, what_changed=None,
169 old_value=None, new_value=None):
170 super().__init__()
171 self.vulnerability = vulnerability
172 self.changer = changer
173 self.what_changed = what_changed
174 self.old_value = old_value
175 self.new_value = new_value
176 self.date_changed = UTC_NOW
177
178
179@implementer(IVulnerabilityActivitySet)
180class VulnerabilityActivitySet:
181
182 def new(self, vulnerability, changer,
183 what_changed,
184 old_value=None, new_value=None):
185 """See `IVulnerabilityActivitySet`."""
186 store = IStore(VulnerabilityActivity)
187 activity = VulnerabilityActivity(vulnerability, changer,
188 what_changed,
189 old_value, new_value)
190 store.add(activity)
191 return activity
diff --git a/lib/lp/security.py b/lib/lp/security.py
index fbada1b..037f0d8 100644
--- a/lib/lp/security.py
+++ b/lib/lp/security.py
@@ -55,6 +55,7 @@ from lp.blueprints.model.specificationsubscription import (
55 )55 )
56from lp.bugs.interfaces.bugtarget import IOfficialBugTagTargetRestricted56from lp.bugs.interfaces.bugtarget import IOfficialBugTagTargetRestricted
57from lp.bugs.interfaces.structuralsubscription import IStructuralSubscription57from lp.bugs.interfaces.structuralsubscription import IStructuralSubscription
58from lp.bugs.interfaces.vulnerability import IVulnerability
58from lp.bugs.model.bugsubscription import BugSubscription59from lp.bugs.model.bugsubscription import BugSubscription
59from lp.bugs.model.bugtaskflat import BugTaskFlat60from lp.bugs.model.bugtaskflat import BugTaskFlat
60from lp.bugs.model.bugtasksearch import get_bug_privacy_filter61from lp.bugs.model.bugtasksearch import get_bug_privacy_filter
@@ -3797,3 +3798,14 @@ class EditCIBuild(AdminByBuilddAdmin):
3797 if auth_repository.checkAuthenticated(user):3798 if auth_repository.checkAuthenticated(user):
3798 return True3799 return True
3799 return super().checkAuthenticated(user)3800 return super().checkAuthenticated(user)
3801
3802
3803class EditVulnerability(AuthorizationBase):
3804 permission = 'launchpad.Edit'
3805 usedfor = IVulnerability
3806
3807 def checkAuthenticated(self, user):
3808 return (user.in_commercial_admin or user.in_admin or
3809 user.isOwner(self.obj.distribution) or
3810 user.isDriver(self.obj.distribution) or
3811 user.isBugSupervisor(self.obj.distribution))
diff --git a/lib/lp/testing/factory.py b/lib/lp/testing/factory.py
index 7c79147..1bbdfe1 100644
--- a/lib/lp/testing/factory.py
+++ b/lib/lp/testing/factory.py
@@ -83,6 +83,7 @@ from lp.bugs.interfaces.bug import (
83 IBugSet,83 IBugSet,
84 )84 )
85from lp.bugs.interfaces.bugtask import (85from lp.bugs.interfaces.bugtask import (
86 BugTaskImportance,
86 BugTaskStatus,87 BugTaskStatus,
87 IBugTaskSet,88 IBugTaskSet,
88 )89 )
@@ -95,6 +96,12 @@ from lp.bugs.interfaces.cve import (
95 CveStatus,96 CveStatus,
96 ICveSet,97 ICveSet,
97 )98 )
99from lp.bugs.interfaces.vulnerability import (
100 IVulnerabilityActivitySet,
101 IVulnerabilitySet,
102 VulnerabilityChange,
103 VulnerabilityStatus,
104 )
98from lp.bugs.model.bug import FileBugData105from lp.bugs.model.bug import FileBugData
99from lp.buildmaster.enums import (106from lp.buildmaster.enums import (
100 BuildBaseImageType,107 BuildBaseImageType,
@@ -5378,6 +5385,53 @@ class BareLaunchpadObjectFactory(ObjectFactory):
5378 IStore(build).flush()5385 IStore(build).flush()
5379 return build5386 return build
53805387
5388 def makeVulnerability(self, distribution=None, status=None,
5389 importance=None, creator=None,
5390 information_type=InformationType.PUBLIC, cve=None,
5391 description=None, notes=None, mitigation=None,
5392 importance_explanation=None, date_made_public=None):
5393 """Make a new `Vulnerability`."""
5394 if distribution is None:
5395 distribution = self.makeDistribution()
5396 if status is None:
5397 status = VulnerabilityStatus.NEEDS_TRIAGE
5398 if importance is None:
5399 importance = BugTaskImportance.UNDECIDED
5400 if creator is None:
5401 creator = self.makePerson()
5402 if importance_explanation is None:
5403 importance_explanation = self.getUniqueString(
5404 "vulnerability-importance-explanation")
5405 return getUtility(
5406 IVulnerabilitySet).new(
5407 distribution=distribution, cve=cve, status=status,
5408 importance=importance, creator=creator,
5409 information_type=information_type, description=description,
5410 notes=notes, mitigation=mitigation,
5411 importance_explanation=importance_explanation,
5412 date_made_public=date_made_public)
5413
5414 def makeVulnerabilityActivity(self, vulnerability=None, changer=None,
5415 what_changed=None, old_value=None,
5416 new_value=None):
5417 """Make a new `VulnerabilityActivity`."""
5418 if vulnerability is None:
5419 vulnerability = self.makeVulnerability()
5420 if changer is None:
5421 changer = self.makePerson()
5422 if what_changed is None:
5423 what_changed = VulnerabilityChange.DESCRIPTION
5424 if old_value is None:
5425 old_value = self.getUniqueString("old-value")
5426 if new_value is None:
5427 new_value = self.getUniqueString("new-value")
5428 return getUtility(
5429 IVulnerabilityActivitySet).new(vulnerability=vulnerability,
5430 changer=changer,
5431 what_changed=what_changed,
5432 old_value=old_value,
5433 new_value=new_value)
5434
53815435
5382# Some factory methods return simple Python types. We don't add5436# Some factory methods return simple Python types. We don't add
5383# security wrappers for them, as well as for objects created by5437# security wrappers for them, as well as for objects created by

Subscribers

People subscribed via source and target branches

to status/vote changes: