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)