launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #32348
[Merge] ~enriqueesanchz/launchpad:cve-cvss-fix into launchpad:master
Enrique Sánchez has proposed merging ~enriqueesanchz/launchpad:cve-cvss-fix into launchpad:master.
Commit message:
Add CVSS formats and fix CVSS removing
Support multiple CVSS formats from the same authority
Change tests to fit CVSS vector_string is now a list
Old version was not removing CVSS when removed from file,
now we always create a new CVSS field
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~enriqueesanchz/launchpad/+git/launchpad/+merge/483925
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~enriqueesanchz/launchpad:cve-cvss-fix into launchpad:master.
diff --git a/lib/lp/bugs/model/cve.py b/lib/lp/bugs/model/cve.py
index dd19e0d..8e06697 100644
--- a/lib/lp/bugs/model/cve.py
+++ b/lib/lp/bugs/model/cve.py
@@ -7,6 +7,7 @@ __all__ = [
]
import operator
+from collections import defaultdict
from datetime import timezone
from storm.databases.postgres import JSON
@@ -164,11 +165,12 @@ class Cve(StormBase, BugLinkTargetMixin):
{("cve", self.sequence): [("bug", str(bug.id))]}
)
- def setCVSSVectorForAuthority(self, authority, vector_string):
+ def setCVSSVectorForAuthority(self, cvss):
"""See ICveReference."""
- if self._cvss is None:
- self._cvss = {}
- self._cvss[authority] = vector_string
+ self._cvss = defaultdict(list)
+ for c in cvss:
+ self._cvss[c.authority].append(c.vector_string)
+ self._cvss = dict(self._cvss)
@implementer(ICveSet)
diff --git a/lib/lp/bugs/scripts/tests/test_uct.py b/lib/lp/bugs/scripts/tests/test_uct.py
index f029f28..8a9b1fc 100644
--- a/lib/lp/bugs/scripts/tests/test_uct.py
+++ b/lib/lp/bugs/scripts/tests/test_uct.py
@@ -1,6 +1,7 @@
# Copyright 2022 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
+from collections import defaultdict
from datetime import datetime, timezone
from pathlib import Path
from typing import List
@@ -821,8 +822,13 @@ class TestUCTImporterExporter(TestCaseWithFactory):
self.assertEqual([bug], vulnerability.bugs)
def checkLaunchpadCve(self, lp_cve: CveModel, cve: CVE):
+ cvss = defaultdict(list)
+ for c in cve.cvss:
+ cvss[c.authority].append(c.vector_string)
+ cvss = dict(cvss)
+
self.assertDictEqual(
- {cvss.authority: cvss.vector_string for cvss in cve.cvss},
+ cvss,
lp_cve.cvss,
)
self.assertEqual(cve.discovered_by, lp_cve.discovered_by)
diff --git a/lib/lp/bugs/scripts/uct/uctexport.py b/lib/lp/bugs/scripts/uct/uctexport.py
index 3745874..b3b2416 100644
--- a/lib/lp/bugs/scripts/uct/uctexport.py
+++ b/lib/lp/bugs/scripts/uct/uctexport.py
@@ -245,7 +245,8 @@ class UCTExporter:
authority=authority,
vector_string=vector_string,
)
- for authority, vector_string in lp_cve.cvss.items()
+ for authority in lp_cve.cvss
+ for vector_string in lp_cve.cvss[authority]
],
patch_urls=patch_urls,
)
diff --git a/lib/lp/bugs/scripts/uct/uctimport.py b/lib/lp/bugs/scripts/uct/uctimport.py
index 845b060..01fe79d 100644
--- a/lib/lp/bugs/scripts/uct/uctimport.py
+++ b/lib/lp/bugs/scripts/uct/uctimport.py
@@ -477,8 +477,5 @@ class UCTImporter:
:param lp_cve: LP's `CVE` model to be updated
:param cve: `CVE` with information from UCT
"""
- for cvss in cve.cvss:
- lp_cve.setCVSSVectorForAuthority(
- cvss.authority, cvss.vector_string
- )
+ lp_cve.setCVSSVectorForAuthority(cve.cvss)
lp_cve.discovered_by = cve.discovered_by
diff --git a/lib/lp/bugs/tests/test_cve.py b/lib/lp/bugs/tests/test_cve.py
index 62c0de2..fdfdefb 100644
--- a/lib/lp/bugs/tests/test_cve.py
+++ b/lib/lp/bugs/tests/test_cve.py
@@ -12,6 +12,7 @@ from zope.security.proxy import removeSecurityProxy
from lp.bugs.interfaces.bugtasksearch import BugTaskSearchParams
from lp.bugs.interfaces.cve import CveStatus, ICveSet
+from lp.bugs.scripts.uct.models import CVSS
from lp.testing import (
TestCaseWithFactory,
login_person,
@@ -170,7 +171,7 @@ class TestCve(TestCaseWithFactory):
def test_cveset_new_method_parameters(self):
today = datetime.now(tz=timezone.utc)
- cvss = {"nvd": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"}
+ cvss = {"nvd": ["CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"]}
cve = getUtility(ICveSet).new(
sequence="2099-1234",
description="A critical vulnerability",
@@ -240,12 +241,18 @@ class TestCve(TestCaseWithFactory):
self.assertEqual({}, unproxied_cve.cvss)
cve.setCVSSVectorForAuthority(
- authority="nvd",
- vector_string="CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
+ [
+ CVSS(
+ authority="nvd",
+ vector_string=(
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"
+ ),
+ )
+ ]
)
self.assertEqual(
- {"nvd": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"},
+ {"nvd": ["CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"]},
unproxied_cve.cvss,
)
@@ -254,21 +261,27 @@ class TestCve(TestCaseWithFactory):
sequence="2099-1234",
description="A critical vulnerability",
cvestate=CveStatus.CANDIDATE,
- cvss={"nvd": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"},
+ cvss={"nvd": ["CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"]},
)
unproxied_cve = removeSecurityProxy(cve)
self.assertEqual(
- {"nvd": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"},
+ {"nvd": ["CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"]},
unproxied_cve.cvss,
)
cve.setCVSSVectorForAuthority(
- authority="nvd",
- vector_string="CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
+ [
+ CVSS(
+ authority="nvd",
+ vector_string=(
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N"
+ ),
+ )
+ ]
)
self.assertEqual(
- {"nvd": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N"},
+ {"nvd": ["CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N"]},
unproxied_cve.cvss,
)
@@ -277,23 +290,76 @@ class TestCve(TestCaseWithFactory):
sequence="2099-1234",
description="A critical vulnerability",
cvestate=CveStatus.CANDIDATE,
- cvss={"nvd": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"},
+ cvss={"nvd": ["CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"]},
)
unproxied_cve = removeSecurityProxy(cve)
self.assertEqual(
- {"nvd": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"},
+ {"nvd": ["CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"]},
unproxied_cve.cvss,
)
cve.setCVSSVectorForAuthority(
- authority="nist",
- vector_string="CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
+ [
+ CVSS(
+ authority="nist",
+ vector_string=(
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N"
+ ),
+ ),
+ CVSS(
+ authority="nvd",
+ vector_string=(
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"
+ ),
+ ),
+ ]
)
self.assertEqual(
{
- "nvd": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
- "nist": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
+ "nvd": ["CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"],
+ "nist": ["CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N"],
+ },
+ unproxied_cve.cvss,
+ )
+
+ def test_setCVSSVectorForAuthority_remove_one_when_initial_value_set(self):
+ cve = self.factory.makeCVE(
+ sequence="2099-1234",
+ description="A critical vulnerability",
+ cvestate=CveStatus.CANDIDATE,
+ cvss={
+ "nvd": [
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
+ ]
+ },
+ )
+ unproxied_cve = removeSecurityProxy(cve)
+ self.assertEqual(
+ {
+ "nvd": [
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
+ ]
+ },
+ unproxied_cve.cvss,
+ )
+
+ cve.setCVSSVectorForAuthority(
+ [
+ CVSS(
+ authority="nvd",
+ vector_string=(
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"
+ ),
+ ),
+ ]
+ )
+
+ self.assertEqual(
+ {
+ "nvd": ["CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"],
},
unproxied_cve.cvss,
)
Follow ups