cros_extract_deps: emit a CPython Git identifier For dev-lang/python, add a Git identifier with the release tag commit so AutoVM matches PSF advisories. BUG=b/536013518 TEST=presubmit RELEASE_NOTE=None Change-Id: I624143be162073766980ab35747f0d6fc8b8c1cb
diff --git a/scripts/cros_extract_deps.py b/scripts/cros_extract_deps.py index f16cce7..bf68b68 100644 --- a/scripts/cros_extract_deps.py +++ b/scripts/cros_extract_deps.py
@@ -124,6 +124,20 @@ ) _PORTAGE_SUFFIX_RE = re.compile(r"_(alpha|beta|pre|rc|p)(\d*)") +# Upstream CPython repository, as used by the OSV PSF advisories. +_CPYTHON_REPO = "https://github.com/python/cpython" +# Commit of each CPython release tag (refs/tags/v<version>^{}). A Git +# identifier with this commit lets AutoVM match PSF advisories in OSV. +# Add the new version here when dev-lang/python is uprevved. +CPYTHON_TAG_COMMITS = { + "3.6.15": "b74b1f36993a4e1700869133f3be13dd60ef4e40", + "3.8.19": "469ede4dbc3ef4146803424a9a3a1e16391f5013", + "3.8.20": "39b2f82717a69dde7212bc39b673b0f55c99e6a3", + "3.11.15": "2340a037f7450e70fccfe411e6531afb4d57a312", + "3.13.13": "01104ce1beb3135c2e0c01ec835b994c1f55a1c0", +} +_CPYTHON_VERSION_RE = re.compile(r"(\d+\.\d+\.\d+)(?:_p\d*)?(?:-r\d+)?") + # PyPI packages installed from outside the dev-python category. _PYPI_PACKAGES_OUTSIDE_DEV_PYTHON = frozenset(("app-admin/awscli",)) @@ -171,11 +185,27 @@ return "PyPI:%s:%s" % (name, pep440_version) +def _GetCPythonIdentifier(version): + """Build a Git identifier for CPython, or return None if unknown.""" + match = _CPYTHON_VERSION_RE.fullmatch(version) + commit = CPYTHON_TAG_COMMITS.get(match.group(1)) if match else None + if not commit: + logging.warning( + "No CPython tag commit for dev-lang/python-%s; " + "add it to CPYTHON_TAG_COMMITS", + version, + ) + return None + return "Git:%s:%s" % (_CPYTHON_REPO, commit) + + def GetCPEFromCPV(category, package, version): """Look up the CPE for a specified Portage package. Besides CPEs, this also returns "PyPI:<name>:<version>" identifiers for PyPI packages (see _IsPyPIPackage) that have a pypi remote-id. + For dev-lang/python it also returns a "Git:<repo>:<commit>" + identifier. Args: category: The Portage package's category, e.g. "net-misc" @@ -228,6 +258,10 @@ # This is livable so long as you do some fuzzy version number # comparison in your vulnerability monitoring, between what-we-have # and what-the-advisory-says-is-affected. + if category == "dev-lang" and package == "python": + identifier = _GetCPythonIdentifier(version) + if identifier: + cpes.append(identifier) return cpes
diff --git a/scripts/cros_extract_deps_unittest.py b/scripts/cros_extract_deps_unittest.py index f0282c2..4b117c8 100644 --- a/scripts/cros_extract_deps_unittest.py +++ b/scripts/cros_extract_deps_unittest.py
@@ -187,6 +187,31 @@ [], cros_extract_deps.GetCPEFromCPV("app-misc", "foo", "1.0") ) + def testGetCPEFromCPVCPython(self): + """Check that dev-lang/python gets a Git commit identifier.""" + stdout = "Remote-ID: cpe:/a:python:python ID: cpe\n" + self.rc.AddCmdResult( + ["equery", "m", "-U", "dev-lang/python"], stdout=stdout + ) + self.assertEqual( + [ + "cpe:/a:python:python:3.13.13p1", + "Git:https://github.com/python/cpython:" + "01104ce1beb3135c2e0c01ec835b994c1f55a1c0", + ], + cros_extract_deps.GetCPEFromCPV("dev-lang", "python", "3.13.13_p1"), + ) + + def testGetCPEFromCPVCPythonUnknownVersion(self): + """Check that an unknown CPython version emits no Git identifier.""" + stdout = "Remote-ID: cpe:/a:python:python ID: cpe\n" + self.rc.AddCmdResult( + ["equery", "m", "-U", "dev-lang/python"], stdout=stdout + ) + self.assertEqual( + ["cpe:/a:python:python:1.2.3"], + cros_extract_deps.GetCPEFromCPV("dev-lang", "python", "1.2.3"), + ) class PortageToPep440Test(cros_test_lib.TestCase):