This data class expresses the contract between data sources and the import runner.
| 650 | |
| 651 | @dataclasses.dataclass(order=True) |
| 652 | class AdvisoryDataV2: |
| 653 | """ |
| 654 | This data class expresses the contract between data sources and the import runner. |
| 655 | """ |
| 656 | |
| 657 | advisory_id: str = "" |
| 658 | aliases: List[str] = dataclasses.field(default_factory=list) |
| 659 | summary: Optional[str] = "" |
| 660 | affected_packages: List[AffectedPackageV2] = dataclasses.field(default_factory=list) |
| 661 | references: List[ReferenceV2] = dataclasses.field(default_factory=list) |
| 662 | patches: List[PatchData] = dataclasses.field(default_factory=list) |
| 663 | date_published: Optional[datetime.datetime] = None |
| 664 | weaknesses: List[int] = dataclasses.field(default_factory=list) |
| 665 | severities: List[VulnerabilitySeverity] = dataclasses.field(default_factory=list) |
| 666 | url: Optional[str] = None |
| 667 | original_advisory_text: Optional[str] = None |
| 668 | |
| 669 | def __post_init__(self): |
| 670 | if not self.advisory_id: |
| 671 | raise ValueError("advisory_id is required for AdvisoryDataV2") |
| 672 | if self.advisory_id and self.advisory_id in self.aliases: |
| 673 | raise ValueError( |
| 674 | f"advisory_id {self.advisory_id} should not be present in aliases {self.aliases}" |
| 675 | ) |
| 676 | if self.summary: |
| 677 | self.summary = clean_summary(self.summary) |
| 678 | |
| 679 | def to_dict(self): |
| 680 | return { |
| 681 | "advisory_id": self.advisory_id, |
| 682 | "aliases": self.aliases, |
| 683 | "summary": self.summary, |
| 684 | "affected_packages": [pkg.to_dict() for pkg in self.affected_packages], |
| 685 | "references": [ref.to_dict() for ref in self.references], |
| 686 | "patches": [patch.to_dict() for patch in self.patches], |
| 687 | "severities": [sev.to_dict() for sev in self.severities], |
| 688 | "date_published": self.date_published.isoformat() if self.date_published else None, |
| 689 | "weaknesses": self.weaknesses, |
| 690 | "url": self.url if self.url else "", |
| 691 | } |
| 692 | |
| 693 | @classmethod |
| 694 | def from_dict(cls, advisory_data): |
| 695 | date_published = advisory_data["date_published"] |
| 696 | transformed = { |
| 697 | "advisory_id": advisory_data["advisory_id"], |
| 698 | "aliases": advisory_data["aliases"], |
| 699 | "summary": advisory_data["summary"], |
| 700 | "affected_packages": [ |
| 701 | AffectedPackageV2.from_dict(pkg) |
| 702 | for pkg in advisory_data["affected_packages"] |
| 703 | if pkg is not None |
| 704 | ], |
| 705 | "patches": [PatchData.from_dict(patch) for patch in advisory_data.get("patches", [])], |
| 706 | "references": [ReferenceV2.from_dict(ref) for ref in advisory_data["references"]], |
| 707 | "date_published": ( |
| 708 | datetime.datetime.fromisoformat(date_published) if date_published else None |
| 709 | ), |
no outgoing calls