CoolFace
Apppublic

mihir2007/Cyber-Risk

sourceHugging Faceupdated 16d agoView on Hugging Face
0likes
models.py280 linesDownload Raw Back to root
1"""2models.py3---------4SQLAlchemy 2.0 declarative ORM models for the CRQ platform.5 6Tables7------8Asset            : enterprise asset inventory (hosts, gateways, databases...)9Vulnerability    : CVEs discovered on a given asset (many-to-one -> Asset)10SecurityControl  : catalogue of candidate security investments11SimulationRun    : historical audit log of every Monte Carlo / optimizer run12NetworkEdge      : attack graph topology edges persisting lateral movement paths13DataLineageFlow  : transitive data flows across nodes tracking provenance and leaks14"""15 16from __future__ import annotations17 18import datetime as dt19import json20from typing import Any, Optional21 22from sqlalchemy import (23    Boolean,24    DateTime,25    Float,26    ForeignKey,27    Index,28    Integer,29    String,30    Text,31)32from sqlalchemy.dialects.postgresql import JSONB33from sqlalchemy.orm import Mapped, mapped_column, relationship34from sqlalchemy.types import TypeDecorator35 36from database import Base37 38 39def _utcnow() -> dt.datetime:40    """Returns current UTC timestamp with timezone awareness."""41    return dt.datetime.now(dt.timezone.utc)42 43 44class SafeJSONB(TypeDecorator):45    """46    PostgreSQL native JSONB column type with transparent Python serialization.47 48    Accepts either Python objects (dicts, lists) or JSON strings on write,49    and returns a valid JSON string on read for compatibility with Pydantic50    schemas and REST consumers while storing native JSONB in PostgreSQL.51    """52 53    impl = JSONB54    cache_ok = True55 56    def load_dialect_impl(self, dialect: Any) -> Any:57        if dialect.name == "postgresql":58            return dialect.type_descriptor(JSONB())59        return dialect.type_descriptor(Text())60 61    def process_bind_param(self, value: Any, dialect: Any) -> Any:62        if value is None:63            return None64        if dialect.name == "postgresql":65            if isinstance(value, str):66                try:67                    return json.loads(value)68                except (ValueError, TypeError):69                    return value70            return value71        else:72            if not isinstance(value, str):73                return json.dumps(value)74            return value75 76    def process_result_value(self, value: Any, dialect: Any) -> Any:77        if value is None:78            return None79        if isinstance(value, (dict, list)):80            return json.dumps(value)81        return str(value)82 83 84class Asset(Base):85    """An enterprise IT/OT asset that carries business and cyber risk."""86 87    __tablename__ = "assets"88 89    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)90    hostname: Mapped[str] = mapped_column(String(128), unique=True, index=True, nullable=False)91 92    # e.g. 'Core Banking Switch', 'UPI Gateway', 'Customer DB', 'Internal ERP',93    #      'Staff Portal', 'Office POS'94    asset_type: Mapped[str] = mapped_column(String(64), nullable=False)95 96    # 'Critical' | 'Medium' | 'Low'97    tier: Mapped[str] = mapped_column(String(16), nullable=False, index=True)98 99    business_unit: Mapped[str] = mapped_column(String(64), nullable=False)100 101    # Revenue lost per minute of downtime, in INR (₹).102    revenue_per_minute: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)103 104    pii_records_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)105    financial_records_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)106 107    is_rbi_regulated: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)108    is_sebi_regulated: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)109 110    # Graph-topology hint: number of network hops from public internet edge111    network_hops_from_internet: Mapped[int] = mapped_column(Integer, nullable=False, default=3)112 113    # Classification taxonomy: 'Core IT', 'AI/ML Model Pipeline', 'Third-Party SaaS',114    # 'Third-Party Vendor API', 'Cloud Infrastructure', 'Data Repository'115    classification_type: Mapped[str] = mapped_column(String(64), default="Internal IT", nullable=False)116 117    # ISO compliance applicability flags118    is_iso27001_regulated: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)119    is_iso42001_regulated: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)120 121    # Third-party vendor risk fields122    is_third_party: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)123    vendor_name: Mapped[Optional[str]] = mapped_column(String(128), nullable=True)124    vendor_risk_tier: Mapped[Optional[str]] = mapped_column(String(32), nullable=True)  # 'Tier-1 Critical', 'Tier-2 High', 'Tier-3 Standard'125    soc2_attestation: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)126 127    # Data residency and cross-border transfer mechanics128    data_residency_country: Mapped[str] = mapped_column(String(8), default="IN", nullable=False)129    cross_border_transfer_enabled: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)130    destination_countries: Mapped[Optional[str]] = mapped_column(String(256), nullable=True)131    transfer_legal_mechanism: Mapped[Optional[str]] = mapped_column(String(64), default="None", nullable=True)132    is_rbi_localization_compliant: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)133 134    vulnerabilities: Mapped[list[Vulnerability]] = relationship(135        "Vulnerability",136        back_populates="asset",137        cascade="all, delete-orphan",138        lazy="selectin",139    )140 141    def __repr__(self) -> str:142        return f"<Asset id={self.id} hostname={self.hostname!r} tier={self.tier}>"143 144 145class Vulnerability(Base):146    """A single CVE observed on a given asset."""147 148    __tablename__ = "vulnerabilities"149 150    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)151    asset_id: Mapped[int] = mapped_column(152        Integer, ForeignKey("assets.id", ondelete="CASCADE"), nullable=False, index=True153    )154 155    cve_id: Mapped[str] = mapped_column(String(32), index=True, nullable=False)156    cvss_score: Mapped[float] = mapped_column(Float, nullable=False)  # 0.0 - 10.0157    epss_score: Mapped[float] = mapped_column(Float, nullable=False)  # 0.0 - 1.0158    cisa_kev: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)159    patch_available: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)160    is_patched: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)161 162    asset: Mapped[Asset] = relationship("Asset", back_populates="vulnerabilities")163 164    def __repr__(self) -> str:165        return f"<Vulnerability {self.cve_id} asset_id={self.asset_id} cvss={self.cvss_score}>"166 167 168class SecurityControl(Base):169    """A candidate security investment considered by the budget optimizer."""170 171    __tablename__ = "security_controls"172 173    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)174    code: Mapped[str] = mapped_column(String(32), unique=True, index=True, nullable=False)175    name: Mapped[str] = mapped_column(String(128), nullable=False)176 177    # 'All' | 'Critical' | 'Medium'178    target_tier: Mapped[str] = mapped_column(String(16), nullable=False, default="All", index=True)179 180    cost_inr: Mapped[float] = mapped_column(Float, nullable=False)181    likelihood_reduction: Mapped[float] = mapped_column(Float, nullable=False)  # 0.0 - 1.0182    is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)183 184    def __repr__(self) -> str:185        return f"<SecurityControl {self.code} cost={self.cost_inr} reduction={self.likelihood_reduction}>"186 187 188class SimulationRun(Base):189    """Historical audit record of an enterprise risk quantification run."""190 191    __tablename__ = "simulation_runs"192 193    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)194    timestamp: Mapped[dt.datetime] = mapped_column(195        DateTime(timezone=True), default=_utcnow, nullable=False, index=True196    )197 198    total_eal_inr: Mapped[float] = mapped_column(Float, nullable=False)199    var_95_inr: Mapped[float] = mapped_column(Float, nullable=False)200    var_99_inr: Mapped[float] = mapped_column(Float, nullable=False)201 202    allocated_budget_inr: Mapped[Optional[float]] = mapped_column(Float, nullable=True)203    selected_controls_json: Mapped[Optional[str]] = mapped_column(SafeJSONB, nullable=True)204 205    def __repr__(self) -> str:206        return f"<SimulationRun id={self.id} EAL={self.total_eal_inr:.2f} @ {self.timestamp}>"207 208 209class NetworkEdge(Base):210    """211    Topology edge representing a network route or lateral attack vector212    between infrastructure nodes (e.g. perimeter gateway, switch, host).213    """214 215    __tablename__ = "network_edges"216 217    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)218    source_node: Mapped[str] = mapped_column(String(128), nullable=False, index=True)219    target_node: Mapped[str] = mapped_column(String(128), nullable=False, index=True)220    weight: Mapped[float] = mapped_column(Float, nullable=False, default=1.0)221    protocol: Mapped[str] = mapped_column(String(32), nullable=False, default="HTTPS")222    is_segmented: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)223 224    # Consent and delegation flags for data provenance & lineage225    is_authorized_flow: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)226    consent_recorded: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)227    flow_type: Mapped[str] = mapped_column(String(64), default="Direct API", nullable=False)  # 'Direct API', 'Sub-Processor Delegation', 'Background Sync', 'Unauthorized Pivot'228    data_tags: Mapped[Optional[str]] = mapped_column(String(256), nullable=True)  # e.g. "Asset-A:PII,Asset-A:FINANCIAL"229 230    __table_args__ = (231        Index("ix_network_edges_source_target", "source_node", "target_node"),232    )233 234    def __repr__(self) -> str:235        return (236            f"<NetworkEdge id={self.id} {self.source_node} -> {self.target_node} "237            f"weight={self.weight} proto={self.protocol} segmented={self.is_segmented} "238            f"auth={self.is_authorized_flow} consent={self.consent_recorded}>"239        )240 241 242class DataLineageFlow(Base):243    """244    Represents transitive data flows across nodes (A -> B -> C), tracking245    originating asset provenance, consent, and potential unauthorized sub-processing.246    """247 248    __tablename__ = "data_lineage_flows"249 250    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)251    origin_asset_id: Mapped[int] = mapped_column(252        Integer, ForeignKey("assets.id", ondelete="CASCADE"), nullable=False, index=True253    )254    intermediary_asset_id: Mapped[Optional[int]] = mapped_column(255        Integer, ForeignKey("assets.id", ondelete="CASCADE"), nullable=True, index=True256    )257    destination_asset_id: Mapped[int] = mapped_column(258        Integer, ForeignKey("assets.id", ondelete="CASCADE"), nullable=False, index=True259    )260 261    is_authorized: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)262    has_user_consent: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)263    records_exposed_pii: Mapped[int] = mapped_column(Integer, default=0, nullable=False)264    records_exposed_financial: Mapped[int] = mapped_column(Integer, default=0, nullable=False)265    detection_status: Mapped[str] = mapped_column(266        String(32), default="Legitimate Flow", nullable=False267    )  # 'Legitimate Flow', 'Unauthorized Sub-Processing', 'Shadow Exfiltration Vector'268 269    origin_asset: Mapped[Asset] = relationship("Asset", foreign_keys=[origin_asset_id])270    intermediary_asset: Mapped[Optional[Asset]] = relationship("Asset", foreign_keys=[intermediary_asset_id])271    destination_asset: Mapped[Asset] = relationship("Asset", foreign_keys=[destination_asset_id])272 273    def __repr__(self) -> str:274        return (275            f"<DataLineageFlow id={self.id} origin={self.origin_asset_id} -> "276            f"inter={self.intermediary_asset_id} -> dest={self.destination_asset_id} "277            f"status='{self.detection_status}'>"278        )279 280