Source code for swh.vulns.osv.model

# Copyright (C) 2026  The Software Heritage developers
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU General Public License version 3, or any later version
# See top-level LICENSE file for more information

from typing import Literal, Optional, TypeVar

import attr
from attrs_strict import AttributeTypeError, type_validator

from swh.model.swhids import CoreSWHID

ModelType = TypeVar("ModelType", bound="BaseModel")


[docs] class BaseModel: __slots__ = ()
[docs] def to_dict(self): """Wrapper of `attr.asdict` that can be overridden by subclasses that have special handling of some of the fields.""" return attr.asdict(self)
[docs] @classmethod def from_dict(cls, d): """""" return cls(**d)
[docs] def evolve(self: ModelType, **kwargs) -> ModelType: """Alias to call :func:`attr.evolve` on this object, returning a new object.""" return attr.evolve(self, **kwargs) # type: ignore[misc]
[docs] def check(self) -> None: """Performs internal consistency checks, and raises an error if one fails.""" # without the type-ignore comment below, attr >= 22.1.0 causes mypy to report: # Argument 1 has incompatible type "BaseModel"; expected "AttrsInstance" attr.validate(self) # type: ignore[arg-type]
VulnerabilityEventType = Literal["introduced", "fixed", "last_affected", "limit"] VulnerabilitySeverityType = Literal["CVSS_V2", "CVSS_V3", "CVSS_V4", "Ubuntu"]
[docs] @attr.s(frozen=True, slots=True) class OSVVulnerabilityEvent(BaseModel): """Model that represents an OSV vulnerability event for a software origin. See https://ossf.github.io/osv-schema/ for more details about the OSV database schema. """ vulnerability_id = attr.ib(type=str, validator=[type_validator()]) """OSV vulnerability identifier""" event_type = attr.ib(type=VulnerabilityEventType, validator=[type_validator()]) """Type of vulneraibility event""" origin_url = attr.ib(type=str, validator=[type_validator()]) """Software origin URL""" swhid = attr.ib(type=Optional[CoreSWHID], default=None) """SWHID of software objects (either release or revision) related to the vulnerability event""" version = attr.ib(type=Optional[str], validator=[type_validator()], default=None) """Optional software version associated to the SWHID""" vulnerability_severity = attr.ib( type=Optional[VulnerabilitySeverityType], validator=[type_validator()], default=None, ) """Severity of vulnerability"""
[docs] @swhid.validator def check_swhid(self, attribute, value): if value is None: return if value.__class__ is not CoreSWHID: raise AttributeTypeError(value, attribute)
[docs] def to_dict(self): d = super().to_dict() d["swhid"] = str(CoreSWHID(**d["swhid"])) if d["swhid"] else None return d
[docs] @classmethod def from_dict(cls, d): d["swhid"] = CoreSWHID.from_string(d["swhid"]) if d["swhid"] else None return cls(**d)