launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #32602
[Merge] ~enriqueesanchz/launchpad:soss-import-parsing into launchpad:master
Enrique Sánchez has proposed merging ~enriqueesanchz/launchpad:soss-import-parsing into launchpad:master.
Commit message:
Add soss-cve-tracker import parsing layer
The parsing layer also validates the data
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~enriqueesanchz/launchpad/+git/launchpad/+merge/487018
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~enriqueesanchz/launchpad:soss-import-parsing into launchpad:master.
diff --git a/lib/lp/bugs/scripts/soss/__init__.py b/lib/lp/bugs/scripts/soss/__init__.py
new file mode 100644
index 0000000..13dc61a
--- /dev/null
+++ b/lib/lp/bugs/scripts/soss/__init__.py
@@ -0,0 +1,4 @@
+# Copyright 2025 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+from lp.bugs.scripts.soss.sossrecord import SOSSRecord # noqa: F401
diff --git a/lib/lp/bugs/scripts/soss/sossrecord.py b/lib/lp/bugs/scripts/soss/sossrecord.py
new file mode 100644
index 0000000..466606b
--- /dev/null
+++ b/lib/lp/bugs/scripts/soss/sossrecord.py
@@ -0,0 +1,139 @@
+# Copyright 2025 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+import re
+from dataclasses import dataclass
+from datetime import datetime
+from enum import Enum
+from typing import Any, Dict, List, Optional
+
+import yaml
+
+# From `soss-cve-tracker/git-hooks/check-cve-syntax`
+VALID_CHANNEL_REGEX = re.compile(r"^(focal|jammy|noble):[^/]+/stable$")
+
+
+@dataclass
+class SOSSRecord:
+
+ class PriorityEnum(Enum):
+ NEEDS_TRIAGE = "Needs-triage"
+ NEGLIGIBLE = "Negligible"
+ LOW = "Low"
+ MEDIUM = "Medium"
+ HIGH = "High"
+ CRITICAL = "Critical"
+
+ class PackageStatusEnum(Enum):
+ IGNORED = "ignored"
+ NEEDS_TRIAGE = "needs-triage"
+ RELEASED = "released"
+ NOT_AFFECTED = "not-affected"
+ DEFERRED = "deferred"
+ NEEDED = "needed"
+
+ class PackageTypeEnum(Enum):
+ CONDA = "conda"
+ PYTHON = "python"
+ UNPACKAGED = "unpackaged"
+ MAVEN = "maven"
+ RUST = "rust"
+
+ @dataclass
+ class Channel:
+ value: str
+
+ def __post_init__(self):
+ if not VALID_CHANNEL_REGEX.match(self.value):
+ raise ValueError(f"Invalid channel format: {self.value}")
+
+ @dataclass
+ class CVSS:
+ source: str
+ vector: str
+ base_score: float
+ base_severity: float
+
+ BASE_SEVERITIES = {"LOW", "MEDIUM", "HIGH", "CRITICAL"}
+
+ def __post_init__(self):
+ if not (0.0 <= self.base_score <= 10.0):
+ raise ValueError(f"Invalid base score: {self.base_score}")
+ if self.base_severity not in self.BASE_SEVERITIES:
+ raise ValueError(
+ f"Invalid base severity: {self.base_severity}"
+ )
+
+ @dataclass
+ class Package:
+ name: str
+ channel: "SOSSRecord.Channel"
+ repositories: List[str]
+ status: "SOSSRecord.PackageStatusEnum"
+ note: str
+
+ references: List[str]
+ notes: List[str]
+ priority: PriorityEnum
+ priority_reason: str
+ assigned_to: str
+ packages: Dict[PackageTypeEnum, List[Package]]
+ candidate: Optional[str] = None
+ description: Optional[str] = None
+ cvss: Optional[List[CVSS]] = None
+ public_date: Optional[datetime] = None
+
+ @classmethod
+ def from_yaml(cls, yaml_str: str) -> "SOSSRecord":
+ raw: Dict[str, Any] = yaml.safe_load(yaml_str)
+ return cls.from_dict(raw)
+
+ @classmethod
+ def from_dict(cls, raw: Dict[str, Any]) -> "SOSSRecord":
+ priority = SOSSRecord.PriorityEnum(raw["Priority"])
+
+ packages = {}
+ for enum_key, pkgs in raw.get("Packages", {}).items():
+ package_type = SOSSRecord.PackageTypeEnum(enum_key.lower())
+ package_list = [
+ SOSSRecord.Package(
+ name=package["Name"],
+ channel=SOSSRecord.Channel(package["Channel"]),
+ repositories=package["Repositories"],
+ status=SOSSRecord.PackageStatusEnum(
+ package["Status"].lower()
+ ),
+ note=package["Note"],
+ )
+ for package in pkgs
+ ]
+ packages[package_type] = package_list
+
+ cvss_list = [
+ SOSSRecord.CVSS(
+ cvss["source"],
+ cvss["vector"],
+ cvss["baseScore"],
+ cvss["baseSeverity"],
+ )
+ for cvss in raw.get("CVSS", [])
+ ]
+
+ public_date_str = raw.get("PublicDate")
+ public_date = (
+ datetime.fromisoformat(public_date_str)
+ if public_date_str
+ else None
+ )
+
+ return cls(
+ references=raw.get("References", []),
+ notes=raw.get("Notes", []),
+ priority=priority,
+ priority_reason=raw.get("Priority-Reason", ""),
+ assigned_to=raw.get("Assigned-To", ""),
+ packages=packages,
+ candidate=raw.get("Candidate"),
+ description=raw.get("Description"),
+ cvss=cvss_list,
+ public_date=public_date,
+ )
diff --git a/lib/lp/bugs/scripts/soss/tests/__init__.py b/lib/lp/bugs/scripts/soss/tests/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/lp/bugs/scripts/soss/tests/__init__.py
diff --git a/lib/lp/bugs/scripts/soss/tests/sampledata/CVE-2025-1979-full b/lib/lp/bugs/scripts/soss/tests/sampledata/CVE-2025-1979-full
new file mode 100644
index 0000000..c8d8b2c
--- /dev/null
+++ b/lib/lp/bugs/scripts/soss/tests/sampledata/CVE-2025-1979-full
@@ -0,0 +1,78 @@
+References:
+- https://github.com/ray-project/ray/commit/64a2e4010522d60b90c389634f24df77b603d85d
+- https://github.com/ray-project/ray/issues/50266
+- https://github.com/ray-project/ray/pull/50409
+- https://security.snyk.io/vuln/SNYK-PYTHON-RAY-8745212
+- https://ubuntu.com/security/notices/SSN-148-1.json?show_hidden=true
+Notes:
+- This is a sample soss cve with all the fields filled for testing
+- sample note 2
+Priority: Low
+Priority-Reason: 'Unrealistic exploitation scenario. Logs are stored locally and not
+ transferred between agents, so local log access is the only conceivable method to
+ view the password for the redis instance (i.e., no possibility of MitM to access
+ the logs). Given the requirement for priviledged system access to access log files
+ the real "danger" posed by the vulnerability is quite low, and that is reflected
+ in this priority assignment. '
+Assigned-To: octagalland
+Packages:
+ unpackaged:
+ - Name: vllm
+ Channel: noble:0.7.3/stable
+ Repositories:
+ - soss-src-stable-local
+ Status: needed
+ Note: ''
+ python:
+ - Name: ray
+ Channel: jammy:2.22.0/stable
+ Repositories:
+ - nvidia-pb3-python-stable-local
+ Status: released
+ Note: 2.22.0+soss.1
+ - Name: pyyaml
+ Channel: jammy:2.22.0/stable
+ Repositories:
+ - nvidia-pb3-python-stable-local
+ Status: not-affected
+ Note: ''
+ maven:
+ - Name: vllm
+ Channel: noble:0.7.3/stable
+ Repositories:
+ - soss-src-stable-local
+ Status: needs-triage
+ Note: ''
+ conda:
+ - Name: ray
+ Channel: jammy:1.17.0/stable
+ Repositories:
+ - nvidia-pb3-python-stable-local
+ Status: not-affected
+ Note: 2.22.0+soss.1
+ rust:
+ - Name: ray
+ Channel: focal:0.27.0/stable
+ Repositories:
+ - nvidia-pb3-python-stable-local
+ Status: deferred
+ Note: 2.22.0+soss.1
+Candidate: CVE-2025-1979
+Description: "Versions of the package ray before 2.43.0 are vulnerable to Insertion\
+ \ of Sensitive Information into Log File where the redis password is being logged\
+ \ in the standard logging. If the redis password is passed as an argument, it will\
+ \ be logged and could potentially leak the password.\r\rThis is only exploitable\
+ \ if:\r\r1) Logging is enabled;\r\r2) Redis is using password authentication;\r\r\
+ 3) Those logs are accessible to an attacker, who can reach that redis instance.\r\
+ \r**Note:**\r\rIt is recommended that anyone who is running in this configuration\
+ \ should update to the latest version of Ray, then rotate their redis password."
+CVSS:
+- source: report@xxxxxxx
+ vector: CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:C/C:H/I:L/A:N
+ baseScore: 6.4
+ baseSeverity: MEDIUM
+- source: security-advisories@xxxxxxxxxx
+ vector: CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
+ baseScore: 9.0
+ baseSeverity: CRITICAL
+PublicDate: '2025-03-06T05:15:16.213'
diff --git a/lib/lp/bugs/scripts/soss/tests/test_sossrecord.py b/lib/lp/bugs/scripts/soss/tests/test_sossrecord.py
new file mode 100644
index 0000000..a26cad7
--- /dev/null
+++ b/lib/lp/bugs/scripts/soss/tests/test_sossrecord.py
@@ -0,0 +1,275 @@
+# Copyright 2025 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+from datetime import datetime
+from pathlib import Path
+
+from lp.bugs.scripts.soss import SOSSRecord
+from lp.testing import TestCase
+
+
+class TestSOSSRecord(TestCase):
+ maxDiff = None
+
+ def setUp(self, *args, **kwargs):
+ super().setUp(*args, **kwargs)
+
+ # This is a synthetic SOSSRecord using all the features
+ self.soss_record = SOSSRecord(
+ references=[
+ "https://github.com/ray-project/ray/commit/"
+ "64a2e4010522d60b90c389634f24df77b603d85d",
+ "https://github.com/ray-project/ray/issues/50266",
+ "https://github.com/ray-project/ray/pull/50409",
+ "https://security.snyk.io/vuln/SNYK-PYTHON-RAY-8745212",
+ "https://ubuntu.com/security/notices/SSN-148-1.json?"
+ "show_hidden=true",
+ ],
+ notes=[
+ "This is a sample soss cve with all the fields filled for "
+ "testing",
+ "sample note 2",
+ ],
+ priority=SOSSRecord.PriorityEnum.LOW,
+ priority_reason=(
+ "Unrealistic exploitation scenario. Logs are stored locally "
+ "and not transferred between agents, so local log access is "
+ "the only conceivable method to view the password for the "
+ "redis instance (i.e., no possibility of MitM to access the "
+ "logs). Given the requirement for priviledged system access "
+ 'to access log files the real "danger" posed by the '
+ "vulnerability is quite low, and that is reflected in this "
+ "priority assignment. "
+ ),
+ assigned_to="octagalland",
+ packages={
+ SOSSRecord.PackageTypeEnum.UNPACKAGED: [
+ SOSSRecord.Package(
+ name="vllm",
+ channel=SOSSRecord.Channel("noble:0.7.3/stable"),
+ repositories=["soss-src-stable-local"],
+ status=SOSSRecord.PackageStatusEnum.NEEDED,
+ note="",
+ )
+ ],
+ SOSSRecord.PackageTypeEnum.PYTHON: [
+ SOSSRecord.Package(
+ name="ray",
+ channel=SOSSRecord.Channel("jammy:2.22.0/stable"),
+ repositories=["nvidia-pb3-python-stable-local"],
+ status=SOSSRecord.PackageStatusEnum.RELEASED,
+ note="2.22.0+soss.1",
+ ),
+ SOSSRecord.Package(
+ name="pyyaml",
+ channel=SOSSRecord.Channel("jammy:2.22.0/stable"),
+ repositories=["nvidia-pb3-python-stable-local"],
+ status=SOSSRecord.PackageStatusEnum.NOT_AFFECTED,
+ note="",
+ ),
+ ],
+ SOSSRecord.PackageTypeEnum.MAVEN: [
+ SOSSRecord.Package(
+ name="vllm",
+ channel=SOSSRecord.Channel("noble:0.7.3/stable"),
+ repositories=["soss-src-stable-local"],
+ status=SOSSRecord.PackageStatusEnum.NEEDS_TRIAGE,
+ note="",
+ )
+ ],
+ SOSSRecord.PackageTypeEnum.CONDA: [
+ SOSSRecord.Package(
+ name="ray",
+ channel=SOSSRecord.Channel("jammy:1.17.0/stable"),
+ repositories=["nvidia-pb3-python-stable-local"],
+ status=SOSSRecord.PackageStatusEnum.NOT_AFFECTED,
+ note="2.22.0+soss.1",
+ )
+ ],
+ SOSSRecord.PackageTypeEnum.RUST: [
+ SOSSRecord.Package(
+ name="ray",
+ channel=SOSSRecord.Channel("focal:0.27.0/stable"),
+ repositories=["nvidia-pb3-python-stable-local"],
+ status=SOSSRecord.PackageStatusEnum.DEFERRED,
+ note="2.22.0+soss.1",
+ )
+ ],
+ },
+ candidate="CVE-2025-1979",
+ description=(
+ "Versions of the package ray before 2.43.0 are vulnerable to "
+ "Insertion of Sensitive Information into Log File where the "
+ "redis password is being logged in the standard logging. If "
+ "the redis password is passed as an argument, it will be "
+ "logged and could potentially leak the password.\r\rThis is "
+ "only exploitable if:\r\r1) Logging is enabled;\r\r2) Redis "
+ "is using password authentication;\r\r3) Those logs are "
+ "accessible to an attacker, who can reach that redis instance."
+ "\r\r**Note:**\r\rIt is recommended that anyone who is "
+ "running in this configuration should update to the latest "
+ "version of Ray, then rotate their redis password."
+ ),
+ cvss=[
+ SOSSRecord.CVSS(
+ source="report@xxxxxxx",
+ vector=("CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:C/C:H/I:L/A:N"),
+ base_score=6.4,
+ base_severity="MEDIUM",
+ ),
+ SOSSRecord.CVSS(
+ source="security-advisories@xxxxxxxxxx",
+ vector=("CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H"),
+ base_score=9.0,
+ base_severity="CRITICAL",
+ ),
+ ],
+ public_date=datetime.fromisoformat("2025-03-06T05:15:16.213"),
+ )
+
+ self.soss_record_dict = {
+ "References": [
+ "https://github.com/ray-project/ray/commit/"
+ "64a2e4010522d60b90c389634f24df77b603d85d",
+ "https://github.com/ray-project/ray/issues/50266",
+ "https://github.com/ray-project/ray/pull/50409",
+ "https://security.snyk.io/vuln/SNYK-PYTHON-RAY-8745212",
+ "https://ubuntu.com/security/notices/SSN-148-1.json?"
+ "show_hidden=true",
+ ],
+ "Notes": [
+ "This is a sample soss cve with all the fields filled for "
+ "testing",
+ "sample note 2",
+ ],
+ "Priority": "Low",
+ "Priority-Reason": (
+ "Unrealistic exploitation scenario. Logs are stored locally "
+ "and not transferred between agents, so local log access is "
+ "the only conceivable method to view the password for the "
+ "redis instance (i.e., no possibility of MitM to access the "
+ "logs). Given the requirement for priviledged system access "
+ 'to access log files the real "danger" posed by the '
+ "vulnerability is quite low, and that is reflected in this "
+ "priority assignment. "
+ ),
+ "Assigned-To": "octagalland",
+ "Packages": {
+ "unpackaged": [
+ {
+ "Name": "vllm",
+ "Channel": "noble:0.7.3/stable",
+ "Repositories": ["soss-src-stable-local"],
+ "Status": "needed",
+ "Note": "",
+ }
+ ],
+ "python": [
+ {
+ "Name": "ray",
+ "Channel": "jammy:2.22.0/stable",
+ "Repositories": ["nvidia-pb3-python-stable-local"],
+ "Status": "released",
+ "Note": "2.22.0+soss.1",
+ },
+ {
+ "Name": "pyyaml",
+ "Channel": "jammy:2.22.0/stable",
+ "Repositories": ["nvidia-pb3-python-stable-local"],
+ "Status": "not-affected",
+ "Note": "",
+ },
+ ],
+ "maven": [
+ {
+ "Name": "vllm",
+ "Channel": "noble:0.7.3/stable",
+ "Repositories": ["soss-src-stable-local"],
+ "Status": "needs-triage",
+ "Note": "",
+ }
+ ],
+ "conda": [
+ {
+ "Name": "ray",
+ "Channel": "jammy:1.17.0/stable",
+ "Repositories": ["nvidia-pb3-python-stable-local"],
+ "Status": "not-affected",
+ "Note": "2.22.0+soss.1",
+ }
+ ],
+ "rust": [
+ {
+ "Name": "ray",
+ "Channel": "focal:0.27.0/stable",
+ "Repositories": ["nvidia-pb3-python-stable-local"],
+ "Status": "deferred",
+ "Note": "2.22.0+soss.1",
+ }
+ ],
+ },
+ "Candidate": "CVE-2025-1979",
+ "Description": (
+ "Versions of the package ray before 2.43.0 are vulnerable to "
+ "Insertion of Sensitive Information into Log File where the "
+ "redis password is being logged in the standard logging. If "
+ "the redis password is passed as an argument, it will be "
+ "logged and could potentially leak the password.\r\rThis is "
+ "only exploitable if:\r\r1) Logging is enabled;\r\r2) Redis "
+ "is using password authentication;\r\r3) Those logs are "
+ "accessible to an attacker, who can reach that redis instance."
+ "\r\r**Note:**\r\rIt is recommended that anyone who is "
+ "running in this configuration should update to the latest "
+ "version of Ray, then rotate their redis password."
+ ),
+ "CVSS": [
+ {
+ "source": "report@xxxxxxx",
+ "vector": ("CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:C/C:H/I:L/A:N"),
+ "baseScore": 6.4,
+ "baseSeverity": "MEDIUM",
+ },
+ {
+ "source": "security-advisories@xxxxxxxxxx",
+ "vector": ("CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H"),
+ "baseScore": 9.0,
+ "baseSeverity": "CRITICAL",
+ },
+ ],
+ "PublicDate": "2025-03-06T05:15:16.213",
+ }
+
+ def test_from_dict(self):
+ soss_record = SOSSRecord.from_dict(self.soss_record_dict)
+ self.assertEqual(self.soss_record, soss_record)
+
+ def test_from_dict_bad_cvss(self):
+ self.soss_record_dict["CVSS"][0]["baseScore"] = -1.5
+ self.assertRaises(
+ ValueError, SOSSRecord.from_dict, self.soss_record_dict
+ )
+
+ self.soss_record_dict["CVSS"][0]["baseScore"] = 10.1
+ self.assertRaises(
+ ValueError, SOSSRecord.from_dict, self.soss_record_dict
+ )
+
+ self.soss_record_dict["CVSS"][0]["baseScore"] = 5
+ self.soss_record_dict["CVSS"][0]["baseSeverity"] = "foo"
+ self.assertRaises(
+ ValueError, SOSSRecord.from_dict, self.soss_record_dict
+ )
+
+ def test_from_dict_bad_channel(self):
+ self.soss_record_dict["Packages"]["unpackaged"][0]["Channel"] = "bar"
+ self.assertRaises(
+ ValueError, SOSSRecord.from_dict, self.soss_record_dict
+ )
+
+ def test_from_yaml(self):
+ load_from = Path(__file__).parent / "sampledata" / "CVE-2025-1979-full"
+
+ soss_record = None
+ with open(load_from) as f:
+ soss_record = SOSSRecord.from_yaml(f)
+
+ self.assertEqual(self.soss_record, soss_record)
Follow ups