Source code for octopoes.models.ooi.findings

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] @classmethod def format_reference_human_readable(cls, reference: Reference) -> str: return reference.tokenized.id
[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] @classmethod def format_reference_human_readable(cls, reference: Reference) -> str: parts = reference.natural_key.split("|") finding_type = parts.pop() ooi_reference = Reference.from_str("|".join(parts)) return f"{finding_type} @ {ooi_reference.human_readable}"
[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"}
[docs] @classmethod def format_reference_human_readable(cls, reference: Reference) -> str: return f"Muted {reference.natural_key}"