from __future__ import annotations
from enum import Enum
from functools import total_ordering
from typing import Annotated, Literal
from pydantic import AnyUrl, StringConstraints
from octopoes.models import OOI, Reference
from octopoes.models.persistence import ReferenceField
severity_order = ["unknown", "pending", "recommendation", "low", "medium", "high", "critical"]
[docs]
@total_ordering
class RiskLevelSeverity(Enum):
CRITICAL = "critical"
HIGH = "high"
MEDIUM = "medium"
LOW = "low"
RECOMMENDATION = "recommendation"
# pending = KAT still has to run the boefje to determine the risk level
PENDING = "pending"
# unknown = the third party has been contacted, but third party has not determined the risk level (yet)
UNKNOWN = "unknown"
def __gt__(self, other: RiskLevelSeverity) -> bool:
return severity_order.index(self.value) > severity_order.index(other.value)
def __str__(self) -> str:
return self.value
[docs]
class FindingType(OOI):
id: str
description: str | None = None
source: AnyUrl | None = None
impact: str | None = None
recommendation: str | None = None
risk_score: float | None = None
risk_severity: RiskLevelSeverity | None = None
_natural_key_attrs = ["id"]
_traversable = False
[docs]
class ADRFindingType(FindingType):
object_type: Literal["ADRFindingType"] = "ADRFindingType"
[docs]
class CVEFindingType(FindingType):
object_type: Literal["CVEFindingType"] = "CVEFindingType"
id: Annotated[str, StringConstraints(strip_whitespace=True, to_upper=True)]
[docs]
class CWEFindingType(FindingType):
object_type: Literal["CWEFindingType"] = "CWEFindingType"
id: Annotated[str, StringConstraints(strip_whitespace=True, to_upper=True)]
[docs]
class CAPECFindingType(FindingType):
object_type: Literal["CAPECFindingType"] = "CAPECFindingType"
id: Annotated[str, StringConstraints(strip_whitespace=True, to_upper=True)]
[docs]
class RetireJSFindingType(FindingType):
object_type: Literal["RetireJSFindingType"] = "RetireJSFindingType"
[docs]
class SnykFindingType(FindingType):
object_type: Literal["SnykFindingType"] = "SnykFindingType"
id: Annotated[str, StringConstraints(strip_whitespace=True, to_upper=True)]
[docs]
class KATFindingType(FindingType):
object_type: Literal["KATFindingType"] = "KATFindingType"
[docs]
class Finding(OOI):
object_type: Literal["Finding"] = "Finding"
finding_type: Reference = ReferenceField(FindingType)
ooi: Reference = ReferenceField(OOI)
proof: str | None = None
description: str | None = None
reproduce: str | None = None
@property
def natural_key(self) -> str:
return f"{str(self.ooi)}|{self.finding_type.natural_key}"
_reverse_relation_names = {"ooi": "findings", "finding_type": "instances"}
[docs]
class MutedFinding(OOI):
object_type: Literal["MutedFinding"] = "MutedFinding"
finding: Reference = ReferenceField(Finding)
reason: str | None = None
_natural_key_attrs = ["finding"]
_reverse_relation_names = {"finding": "mutes"}