launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #32607
Re: [Merge] ~enriqueesanchz/launchpad:soss-import-parsing into launchpad:master
Hi, thanks for the review. Addressed the comments :)
> and we might want to re-use them
Well, It seems that the sec team does not have a standard for this types of fields so it makes sense to only use this for now inside the `SOSSRecord` class. If in the future (I hope so) we can reuse it, we will change it.
> That being said, I really like how tidy this looks - it represents exactly a SOSSRecord with all that's nested within it, and I don't need to go and find the definition anything anywhere else. So IMO there is no need to overcomplicate.
Agree :)
Diff comments:
> 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
done :)
> @@ -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
good catch, thanks! <3
> +
> +# 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"])
As I saw in the cves:
- Candidate, Description, CVSS and PublicDate are not mandatory.
- References, Notes can be empty using [], but they are always shown.
- Priority-Reason, Assigned-To can be empty using "", but they are always shown.
- Other fields are mandatory.
As I have ongoing conversations with the stakeholders, there can be little changes on this. I'll put a comment as valuable info.
Optional fields are also shown using typing `Optional[]`.
> +
> + 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,
> + )
--
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.
References