Guruprasad has proposed merging ~lgp171188/launchpad:vulnerability-links-in-cve-page-if-present into launchpad:master.
Commit message: Link to the linked vulnerabilities on the CVE page Requested reviews: Launchpad code reviewers (launchpad-reviewers) For more details, see: https://code.launchpad.net/~lgp171188/launchpad/+git/launchpad/+merge/430866 -- Your team Launchpad code reviewers is requested to review the proposed merge of ~lgp171188/launchpad:vulnerability-links-in-cve-page-if-present into launchpad:master.
diff --git a/lib/lp/bugs/browser/tests/test_vulnerability.py b/lib/lp/bugs/browser/tests/test_vulnerability.py index c0711c5..78564f6 100644 --- a/lib/lp/bugs/browser/tests/test_vulnerability.py +++ b/lib/lp/bugs/browser/tests/test_vulnerability.py @@ -530,3 +530,230 @@ class TestVulnerabilityListingPage(BrowserTestCase): ) ) self.assertBatches(distribution, link_matchers, True, 0, 5) + + +class TestVulnerabilitiesLinksOnCVEPage(BrowserTestCase): + """Test for the vulnerabilities links on the CVE page.""" + + layer = DatabaseFunctionalLayer + + def test_cve_page_when_no_linked_vulnerabilities(self): + cve = self.factory.makeCVE(sequence="2022-1234") + browser = self.getUserBrowser( + canonical_url(cve), + user=self.factory.makePerson(), + ) + self.assertThat( + browser.contents, + HTMLContains( + Tag( + "Linked vulnerabilities heading", + "h2", + text="Linked vulnerabilities", + ), + Tag( + "Linked vulnerabilities details", + "p", + text="No linked vulnerabilities", + ), + ), + ) + + def test_cve_page_when_linked_vulnerabilities_present(self): + cve = self.factory.makeCVE(sequence="2022-1234") + distribution1 = self.factory.makeDistribution() + distribution2 = self.factory.makeDistribution() + vulnerability1 = self.factory.makeVulnerability( + cve=cve, + distribution=distribution1, + ) + vulnerability2 = self.factory.makeVulnerability( + cve=cve, + distribution=distribution2, + ) + browser = self.getUserBrowser( + canonical_url(cve), user=self.factory.makePerson() + ) + login(ANONYMOUS) + self.assertThat( + browser.contents, + HTMLContains( + Tag( + "Linked vulnerabilities heading", + "h2", + text="Linked vulnerabilities", + ), + Tag( + "vulnerability1", + "a", + attrs={ + "href": canonical_url( + vulnerability1, force_local_path=True + ) + }, + text="{} vulnerability".format(distribution1.displayname), + ), + Tag( + "vulnerability2", + "a", + attrs={ + "href": canonical_url( + vulnerability2, force_local_path=True + ) + }, + text="{} vulnerability".format(distribution2.displayname), + ), + ), + ) + + def test_query_count_constant_vs_number_of_linked_vulnerabilities(self): + cve = self.factory.makeCVE(sequence="2022-1234") + + def create_vulnerability(): + login(ANONYMOUS) + self.factory.makeVulnerability( + cve=cve, distribution=self.factory.makeDistribution() + ) + + recorder1, recorder2 = record_two_runs( + lambda: self.getMainText(cve), + create_vulnerability, + 5, + ) + self.assertThat(recorder2, HasQueryCount.byEquality(recorder1)) + + def test_non_public_vulnerabilities_not_linked_unauthorized_users(self): + cve = self.factory.makeCVE(sequence="2022-1234") + distribution = self.factory.makeDistribution( + bug_sharing_policy=BugSharingPolicy.PROPRIETARY + ) + vulnerability = self.factory.makeVulnerability( + cve=cve, + distribution=distribution, + information_type=InformationType.PROPRIETARY, + ) + person = self.factory.makePerson() + browser = self.getUserBrowser(canonical_url(cve), user=person) + login(ANONYMOUS) + self.assertThat( + browser.contents, + HTMLContains( + Tag( + "Linked vulnerabilities heading", + "h2", + text="Linked vulnerabilities", + ), + Tag( + "Linked vulnerabilities details", + "p", + text="No linked vulnerabilities", + ), + ), + ) + distribution2 = self.factory.makeDistribution() + vulnerability2 = self.factory.makeVulnerability( + cve=cve, + distribution=distribution2, + ) + browser = self.getUserBrowser(canonical_url(cve), user=person) + login(ANONYMOUS) + self.assertThat( + browser.contents, + MatchesAll( + HTMLContains( + Tag( + "Linked vulnerabilities heading", + "h2", + text="Linked vulnerabilities", + ), + Tag( + "vulnerability2", + "a", + attrs={ + "href": canonical_url( + vulnerability2, + force_local_path=True, + ), + }, + text="{} vulnerability".format( + distribution2.displayname + ), + ), + ), + Not( + HTMLContains( + Tag( + "vulnerability", + "a", + attrs={ + "href": canonical_url( + removeSecurityProxy(vulnerability), + force_local_path=True, + ), + }, + text="{} vulnerability".format( + distribution.displayname + ), + ) + ) + ), + ), + ) + + def test_authorized_users_can_see_links_to_non_public_vulnerabilities( + self, + ): + cve = self.factory.makeCVE(sequence="2022-1234") + distribution1 = self.factory.makeDistribution() + distribution2 = self.factory.makeDistribution( + bug_sharing_policy=BugSharingPolicy.PROPRIETARY + ) + vulnerability1 = self.factory.makeVulnerability( + cve=cve, distribution=distribution1 + ) + vulnerability2 = self.factory.makeVulnerability( + cve=cve, + distribution=distribution2, + information_type=InformationType.PROPRIETARY, + ) + person_with_access = self.factory.makePerson() + grant_access_to_non_public_vulnerability( + vulnerability2, person_with_access + ) + browser = self.getUserBrowser( + canonical_url(cve), user=person_with_access + ) + with person_logged_in(person_with_access): + self.assertThat( + browser.contents, + HTMLContains( + Tag( + "Linked vulnerabilities heading", + "h2", + text="Linked vulnerabilities", + ), + Tag( + "vulnerability1", + "a", + attrs={ + "href": canonical_url( + vulnerability1, + force_local_path=True, + ), + }, + ), + Tag( + "vulnerability2", + "a", + attrs={ + "href": canonical_url( + vulnerability2, + force_local_path=True, + ), + }, + text="{} vulnerability".format( + distribution2.displayname + ), + ), + ), + ) diff --git a/lib/lp/bugs/interfaces/cve.py b/lib/lp/bugs/interfaces/cve.py index 8795aa9..204dcd7 100644 --- a/lib/lp/bugs/interfaces/cve.py +++ b/lib/lp/bugs/interfaces/cve.py @@ -189,6 +189,9 @@ class ICve(Interface): def setCVSSVectorForAuthority(authority, vector_string): """Set the CVSS vector string from an authority.""" + def getVulnerabilitiesVisibleToUser(user): + """Return the linked vulnerabilities visible to the given user.""" + @exported_as_webservice_collection(ICve) class ICveSet(Interface): diff --git a/lib/lp/bugs/model/cve.py b/lib/lp/bugs/model/cve.py index 8a9f327..86e9cae 100644 --- a/lib/lp/bugs/model/cve.py +++ b/lib/lp/bugs/model/cve.py @@ -20,12 +20,18 @@ from lp.bugs.interfaces.cve import CveStatus, ICve, ICveSet from lp.bugs.model.bug import Bug from lp.bugs.model.buglinktarget import BugLinkTargetMixin from lp.bugs.model.cvereference import CveReference +from lp.bugs.model.vulnerability import ( + Vulnerability, + get_vulnerability_privacy_filter, +) from lp.services.database import bulk from lp.services.database.constants import UTC_NOW +from lp.services.database.decoratedresultset import DecoratedResultSet from lp.services.database.enumcol import DBEnum from lp.services.database.interfaces import IStore from lp.services.database.stormbase import StormBase from lp.services.database.stormexpr import fti_search +from lp.services.webapp.interfaces import ILaunchBag from lp.services.xref.interfaces import IXRefSet from lp.services.xref.model import XRef @@ -47,9 +53,6 @@ class Cve(StormBase, BugLinkTargetMixin): references = ReferenceSet( id, "CveReference.cve_id", order_by="CveReference.id" ) - vulnerabilities = ReferenceSet( - id, "Vulnerability.cve_id", order_by="Vulnerability.id" - ) date_made_public = DateTime(tzinfo=pytz.UTC, allow_none=True) discovered_by = Unicode(allow_none=True) @@ -108,6 +111,32 @@ class Cve(StormBase, BugLinkTargetMixin): sorted(bulk.load(Bug, bug_ids), key=operator.attrgetter("id")) ) + def getVulnerabilitiesVisibleToUser(self, user): + """See `ICve`.""" + vulnerabilities = Store.of(self).find( + Vulnerability, + Vulnerability.cve == self, + get_vulnerability_privacy_filter(user), + ) + vulnerabilities.order_by(Desc(Vulnerability.date_created)) + + def preload_distributions(rows): + from lp.registry.model.distribution import Distribution + + bulk.load_related(Distribution, rows, ["distribution_id"]) + + return DecoratedResultSet( + vulnerabilities, + pre_iter_hook=preload_distributions, + ) + + @property + def vulnerabilities(self): + """See `ICve`.""" + return self.getVulnerabilitiesVisibleToUser( + getUtility(ILaunchBag).user + ) + # CveReference's def createReference(self, source, content, url=None): """See ICveReference.""" diff --git a/lib/lp/bugs/templates/cve-portlet-bugs2.pt b/lib/lp/bugs/templates/cve-portlet-bugs2.pt index 579544d..e0c8a6e 100644 --- a/lib/lp/bugs/templates/cve-portlet-bugs2.pt +++ b/lib/lp/bugs/templates/cve-portlet-bugs2.pt @@ -25,5 +25,13 @@ </li> </ul> -</div> + <h2>Linked vulnerabilities</h2> + <ul tal:condition="python: context.vulnerabilities.count() > 0"> + <li tal:repeat="vulnerability context/vulnerabilities"> + <img src="/@@/cve" /> <a tal:attributes="href vulnerability/fmt:url" tal:content="string:${vulnerability/distribution/displayname} vulnerability"></a> + </li> + </ul> + <p tal:condition="python: context.vulnerabilities.count() == 0"><i>No linked vulnerabilities</i></p> + +</div>
_______________________________________________ Mailing list: https://launchpad.net/~launchpad-reviewers Post to : launchpad-reviewers@lists.launchpad.net Unsubscribe : https://launchpad.net/~launchpad-reviewers More help : https://help.launchpad.net/ListHelp