9472fe91fa
Aegis CI / lint-and-test (push) Has been cancelled
- Remove ANN (type annotations) and D (docstrings) from ruff select; not feasible to add thousands of missing annotations/docstrings across the codebase - Add I001 and E501 to ignore: comment-interleaved import style and SQLAlchemy FK definitions naturally exceed line limits - Fix F811 duplicate import blocks in main.py, models/__init__.py, routers (campaigns, system, tests, evidence) and services (test_workflow, test_crud, campaign_service, schemas/test) - Add missing Evidence/IntelItem/Technique/Test/TestTemplate/User imports to models/__init__.py (were only in duplicate block) - Fix F821: add missing JWTError import in auth.py - Fix F401 unused imports across 15+ files (jira_service, sso_service, notification_service, playbook_service, tempo_service, models, schemas, routers: admin_config, attack_paths, executive_dashboard, knowledge, ownership, risk_intelligence, sso, api_keys, email_service) - Fix F841 unused variables: owned_technique_ids (executive_dashboard_service), severity (jira_service), priority_order (revalidation_queue_service) - Fix F541 f-strings without placeholders in system.py and attck_evaluations_service - Fix F601 duplicate dict key G0067 in threat_actor_import_service - Fix E701 multiple-statements-on-one-line in risk_intelligence_service - Fix E741 ambiguous variable name l -> lvl in risk_intelligence_service - Fix N806 uppercase vars in functions: technique.py, heatmap_service.py; add noqa for compliance_import_service.py large unused constant dicts - Fix W293 whitespace on blank lines in tests/conftest.py
69 lines
3.6 KiB
Python
69 lines
3.6 KiB
Python
"""Phase 13: Executive Dashboard — PostureSnapshot model."""
|
||
|
||
import uuid
|
||
from datetime import datetime
|
||
|
||
from sqlalchemy import (
|
||
Column, Date, DateTime, Float, ForeignKey,
|
||
Index, Integer, UniqueConstraint,
|
||
)
|
||
from sqlalchemy.dialects.postgresql import UUID, JSONB
|
||
from sqlalchemy.orm import relationship
|
||
|
||
from app.database import Base
|
||
|
||
|
||
class PostureSnapshot(Base):
|
||
"""
|
||
Daily point-in-time capture of the organisation's security posture.
|
||
|
||
Aggregates data from all phases (coverage, risk, ownership, knowledge,
|
||
attack-paths) into a single row that can be trended over time.
|
||
"""
|
||
|
||
__tablename__ = "posture_snapshots"
|
||
|
||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||
snapshot_date = Column(Date, nullable=False) # one per calendar day
|
||
|
||
# ── Coverage ──────────────────────────────────────────────────────────────
|
||
total_techniques = Column(Integer, nullable=False, default=0)
|
||
validated_count = Column(Integer, nullable=False, default=0)
|
||
partial_count = Column(Integer, nullable=False, default=0)
|
||
not_covered_count = Column(Integer, nullable=False, default=0)
|
||
coverage_pct = Column(Float, nullable=False, default=0.0) # 0–100
|
||
|
||
# ── Risk ─────────────────────────────────────────────────────────────────
|
||
avg_risk_score = Column(Float, nullable=False, default=0.0)
|
||
critical_count = Column(Integer, nullable=False, default=0)
|
||
high_count = Column(Integer, nullable=False, default=0)
|
||
medium_count = Column(Integer, nullable=False, default=0)
|
||
low_count = Column(Integer, nullable=False, default=0)
|
||
|
||
# ── Operations ────────────────────────────────────────────────────────────
|
||
open_queue_items = Column(Integer, nullable=False, default=0)
|
||
orphan_techniques = Column(Integer, nullable=False, default=0)
|
||
|
||
# ── Knowledge ─────────────────────────────────────────────────────────────
|
||
playbook_count = Column(Integer, nullable=False, default=0)
|
||
lesson_count = Column(Integer, nullable=False, default=0)
|
||
|
||
# ── MTTD (from attack-path executions completed in last 30 d) ────────────
|
||
mttd_avg_seconds = Column(Float, nullable=True) # None if no data
|
||
executions_30d = Column(Integer, nullable=False, default=0)
|
||
detection_rate_30d = Column(Float, nullable=True) # avg across executions
|
||
|
||
# ── Meta ─────────────────────────────────────────────────────────────────
|
||
created_by = Column(
|
||
UUID(as_uuid=True), ForeignKey("users.id", ondelete="SET NULL"), nullable=True
|
||
)
|
||
created_at = Column(DateTime, default=datetime.utcnow)
|
||
extra = Column(JSONB, nullable=True) # full breakdown / by-tactic
|
||
|
||
creator = relationship("User", foreign_keys=[created_by])
|
||
|
||
__table_args__ = (
|
||
UniqueConstraint("snapshot_date", name="uq_posture_snapshot_date"),
|
||
Index("ix_posture_snapshots_date", "snapshot_date"),
|
||
)
|