"""
InsAIts SDK - CVE Signatures & Attack Pattern Registry
=======================================================
Structured CVE and attack signature data for InsAIts runtime detectors.
This module turns the MCP security documentation into importable Python data
that detection logic can reference directly.

USAGE:
    from insa_its.cve_signatures import (
        CVE_REGISTRY,
        ATTACK_PATTERNS,
        OWASP_MCP_TOP10,
        OWASP_AGENTIC_TOP10,
        get_patterns_for_detector,
        get_owasp_coverage,
    )

IMPORTANT -- OPEN/PRIVATE BOUNDARY:
    This file contains ATTACK DESCRIPTIONS and PATTERN CATEGORIES (open).
    Actual detection THRESHOLDS and trained MODEL WEIGHTS are in
    insaits_private_config.py (gitignored -- never commit).

Sources: JFrog, Oligo Security, Invariant Labs, Adversa AI, OWASP, Tenable,
         AuthZed. All CVEs verified March 2026.
"""

from dataclasses import dataclass, field
from enum import Enum
from typing import List, Optional


# ─── Enums ───────────────────────────────────────────────────────────────────

class CVESeverity(str, Enum):
    CRITICAL = "CRITICAL"
    HIGH     = "HIGH"
    MEDIUM   = "MEDIUM"
    LOW      = "LOW"
    INFO     = "INFO"


class AttackLayer(str, Enum):
    CONTENT      = "content"       # Message content — InsAIts primary domain
    PROTOCOL     = "protocol"      # TCP/OAuth — outside InsAIts scope
    SUPPLY_CHAIN = "supply_chain"  # Install/update — partial InsAIts coverage
    INFRASTRUCTURE = "infrastructure"  # OS/network — outside InsAIts scope


class DetectorStatus(str, Enum):
    SHIPPED   = "shipped_v3"
    PRIORITY1 = "build_priority_1"
    PRIORITY2 = "build_priority_2"
    PRIORITY3 = "build_priority_3"
    PRIORITY4 = "build_priority_4"
    PRIORITY5 = "build_priority_5"
    PRIORITY6 = "build_priority_6"
    PRIORITY7 = "build_priority_7"
    PRIORITY8 = "build_priority_8"
    OUT_OF_SCOPE = "out_of_scope"


# ─── Data Classes ─────────────────────────────────────────────────────────────

@dataclass
class CVEEntry:
    cve_id: str
    component: str
    cvss: float
    severity: CVESeverity
    description: str
    attack_layer: AttackLayer
    insaits_detector: Optional[str]      # Detector class name, None if out of scope
    detector_status: DetectorStatus
    detection_notes: str
    source: str
    fix: str
    verified_march_2026: bool = True


@dataclass
class AttackPattern:
    """
    A named attack pattern with indicators InsAIts can look for at runtime.
    The actual detection logic and thresholds live in the detector classes
    and insaits_private_config.py — not here.
    """
    name: str
    category: str
    severity: CVESeverity
    attack_layer: AttackLayer
    description: str
    owasp_ids: List[str]
    insaits_detector: Optional[str]
    detector_status: DetectorStatus

    # Observable indicators — what InsAIts looks for (categories, not thresholds)
    observable_indicators: List[str]

    # What mcp-scan covers (for competitive positioning)
    mcp_scan_coverage: str

    # Source references
    sources: List[str] = field(default_factory=list)


@dataclass
class OWASPRisk:
    risk_id: str
    framework: str  # "MCP_TOP10" or "AGENTIC_TOP10"
    name: str
    description: str
    insaits_coverage: str  # "YES", "PARTIAL", "NO"
    insaits_detector: Optional[str]
    coverage_notes: str


# ─── CVE Registry ─────────────────────────────────────────────────────────────

CVE_REGISTRY: List[CVEEntry] = [

    # ── CRITICAL ──────────────────────────────────────────────────────────────

    CVEEntry(
        cve_id="CVE-2025-6514",
        component="mcp-remote npm package",
        cvss=9.6,
        severity=CVESeverity.CRITICAL,
        description=(
            "RCE via OS command injection when connecting to untrusted MCP server. "
            "Affects ALL LLM clients using mcp-remote for OAuth. "
            "558,000+ downloads compromised. 437,000+ developer environments exposed — "
            "attackers gained access to environment variables, credentials, "
            "and internal repositories."
        ),
        attack_layer=AttackLayer.PROTOCOL,
        insaits_detector="BehavioralFingerprintDetector",
        detector_status=DetectorStatus.PRIORITY2,
        detection_notes=(
            "Pre-connection — InsAIts cannot prevent this. "
            "Post-exploitation: behavioral anomaly detection catches unusual "
            "tool calling patterns that follow credential compromise."
        ),
        source="research.jfrog.com/vulnerabilities/mcp-remote-command-injection-rce-jfsa-2025-001290844",
        fix="Update mcp-remote to v0.1.16",
    ),

    CVEEntry(
        cve_id="CVE-2025-49596",
        component="Anthropic MCP Inspector",
        cvss=9.4,
        severity=CVESeverity.CRITICAL,
        description=(
            "RCE via DNS rebinding combined with 0.0.0.0 flaw. "
            "Malicious website executes arbitrary code on developer machine "
            "without user interaction. SSH keys, cloud credentials, "
            "entire filesystem exposed."
        ),
        attack_layer=AttackLayer.INFRASTRUCTURE,
        insaits_detector="BehavioralFingerprintDetector",
        detector_status=DetectorStatus.PRIORITY2,
        detection_notes=(
            "Network layer — InsAIts cannot prevent DNS rebinding. "
            "Post-exploitation behavioral drift is detectable."
        ),
        source="oligo.security/blog/critical-rce-vulnerability-in-anthropic-mcp-inspector-cve-2025-49596",
        fix="Update MCP Inspector to v0.14.1",
    ),

    CVEEntry(
        cve_id="CVE-2025-52882",
        component="Multiple MCP servers",
        cvss=8.8,
        severity=CVESeverity.HIGH,
        description=(
            "Authentication bypass — OAuth token confusion allows privilege escalation. "
            "Confused deputy problem: server uses another user's credentials."
        ),
        attack_layer=AttackLayer.PROTOCOL,
        insaits_detector="ToolCallFrequencyAnomalyDetector",
        detector_status=DetectorStatus.PRIORITY5,
        detection_notes=(
            "Agent calling tools outside its registered permission scope "
            "is detectable as behavioral frequency anomaly."
        ),
        source="adversa.ai/mcp-security-top-25-mcp-vulnerabilities",
        fix="Implement OAuth 2.1 with JWKS",
    ),

    CVEEntry(
        cve_id="CVE-2025-53109",
        component="Anthropic Filesystem MCP Server",
        cvss=8.4,
        severity=CVESeverity.HIGH,
        description=(
            "Sandbox escape — arbitrary file access outside permitted directories "
            "via symlink bypass. Full host filesystem exposure."
        ),
        attack_layer=AttackLayer.INFRASTRUCTURE,
        insaits_detector=None,
        detector_status=DetectorStatus.OUT_OF_SCOPE,
        detection_notes="OS-level symlink traversal — outside message content monitoring scope.",
        source="cymulate.com (search CVE-2025-53109)",
        fix="Patch applied",
    ),

    CVEEntry(
        cve_id="CVE-2025-53110",
        component="Anthropic Filesystem MCP Server",
        cvss=7.3,
        severity=CVESeverity.HIGH,
        description=(
            "Containment bypass — companion to CVE-2025-53109. "
            "Chained for complete RCE via malicious .git/config files."
        ),
        attack_layer=AttackLayer.INFRASTRUCTURE,
        insaits_detector=None,
        detector_status=DetectorStatus.OUT_OF_SCOPE,
        detection_notes="Chained with CVE-2025-53109. Same scope limitation.",
        source="cymulate.com (search CVE-2025-53110)",
        fix="Patch applied",
    ),

    CVEEntry(
        cve_id="CVE-2025-68145",
        component="Anthropic mcp-server-git",
        cvss=8.0,
        severity=CVESeverity.HIGH,
        description="Path validation bypass — first in chain of 3 chained CVEs in Anthropic's own git server.",
        attack_layer=AttackLayer.CONTENT,
        insaits_detector="ToolCallFrequencyAnomalyDetector",
        detector_status=DetectorStatus.PRIORITY5,
        detection_notes="Anomalous path arguments in git tool calls detectable as argument injection pattern.",
        source="vineethsai.github.io/vulnerablemcp",
        fix="Patch applied",
    ),

    CVEEntry(
        cve_id="CVE-2025-68143",
        component="Anthropic mcp-server-git",
        cvss=8.0,
        severity=CVESeverity.HIGH,
        description="Unrestricted git_init — can turn .ssh directory into a git repo, exposing SSH keys.",
        attack_layer=AttackLayer.CONTENT,
        insaits_detector="ToolCallFrequencyAnomalyDetector",
        detector_status=DetectorStatus.PRIORITY5,
        detection_notes="git_init called on .ssh or other system directories is detectable as out-of-baseline tool call.",
        source="vineethsai.github.io/vulnerablemcp",
        fix="Patch applied",
    ),

    CVEEntry(
        cve_id="CVE-2025-68144",
        component="Anthropic mcp-server-git",
        cvss=8.0,
        severity=CVESeverity.HIGH,
        description="Argument injection in git_diff — final link in RCE chain when combined with CVE-2025-68145 and CVE-2025-68143.",
        attack_layer=AttackLayer.CONTENT,
        insaits_detector="ToolCallFrequencyAnomalyDetector",
        detector_status=DetectorStatus.PRIORITY5,
        detection_notes="Anomalous argument structure in git_diff calls detectable as argument injection.",
        source="vineethsai.github.io/vulnerablemcp",
        fix="Patch applied",
    ),

    CVEEntry(
        cve_id="CVE-2025-54135",
        component="Cursor IDE (CurXecute)",
        cvss=8.5,
        severity=CVESeverity.HIGH,
        description=(
            "Prompt injection via MCP-connected Slack modifies global mcp.json config. "
            "Commands execute immediately without user approval."
        ),
        attack_layer=AttackLayer.CONTENT,
        insaits_detector="PromptInjectionDetector",
        detector_status=DetectorStatus.SHIPPED,
        detection_notes=(
            "Indirect prompt injection arriving via Slack tool result. "
            "Content that arrives via tool result and changes agent config behavior "
            "is detectable as semantic goal divergence."
        ),
        source="tenable.com/blog/faq-cve-2025-54135-cve-2025-54136",
        fix="Cursor patched",
    ),

    CVEEntry(
        cve_id="CVE-2025-54136",
        component="Cursor IDE (MCPoison)",
        cvss=8.5,
        severity=CVESeverity.HIGH,
        description=(
            "MCP trust bypass — approving a server with project-specific mcp.json "
            "enables persistent code execution without user approval."
        ),
        attack_layer=AttackLayer.SUPPLY_CHAIN,
        insaits_detector="BehavioralFingerprintDetector",
        detector_status=DetectorStatus.PRIORITY2,
        detection_notes="Behavioral shift after new server approval detectable as fingerprint change.",
        source="tenable.com/blog/faq-cve-2025-54135-cve-2025-54136",
        fix="Cursor patched",
    ),

    CVEEntry(
        cve_id="CVE-2025-15000",  # placeholder — check exact ID
        component="mcp-remote npm (general)",
        cvss=7.5,
        severity=CVESeverity.HIGH,
        description="558,000+ download supply chain. See CVE-2025-6514 for primary entry.",
        attack_layer=AttackLayer.SUPPLY_CHAIN,
        insaits_detector="BehavioralFingerprintDetector",
        detector_status=DetectorStatus.PRIORITY2,
        detection_notes="Post-install behavioral anomaly detection.",
        source="pypi.org/project/mcp-scan",
        fix="Update mcp-remote",
    ),
]


# ─── Attack Patterns (No CVE — But Documented and Exploited) ──────────────────

ATTACK_PATTERNS: List[AttackPattern] = [

    AttackPattern(
        name="Tool Poisoning",
        category="content_injection",
        severity=CVESeverity.CRITICAL,
        attack_layer=AttackLayer.CONTENT,
        description=(
            "Hidden instructions embedded in tool descriptions that the LLM follows "
            "but users cannot see. Does not require tool invocation — loading the "
            "tool description into context is sufficient for the model to follow "
            "hidden instructions."
        ),
        owasp_ids=["MCP03"],
        insaits_detector="ToolDescriptionDivergenceDetector",
        detector_status=DetectorStatus.PRIORITY1,
        observable_indicators=[
            "Semantic divergence between tool stated_purpose and runtime behavior",
            "Agent following instructions not present in visible conversation",
            "Tool output inconsistent with tool description",
            "Agent goals shifting after tool context is loaded",
            "Embedding distance between tool name/description and actual instructions above threshold",
        ],
        mcp_scan_coverage="Static only — mcp-scan catches this at install time by reading tool descriptions. Does NOT catch behavioral changes mid-session or post-update.",
        sources=["invariantlabs-ai.github.io/docs/mcp-scan", "OWASP MCP Top 10 MCP03"],
    ),

    AttackPattern(
        name="Rug Pull Attack",
        category="supply_chain",
        severity=CVESeverity.HIGH,
        attack_layer=AttackLayer.SUPPLY_CHAIN,
        description=(
            "Tool or MCP server changes its behavior after initial user approval "
            "and trust has been established. The malicious update arrives as a "
            "legitimate package update, bypassing the initial security review."
        ),
        owasp_ids=["MCP04"],
        insaits_detector="BehavioralFingerprintDetector",
        detector_status=DetectorStatus.PRIORITY2,
        observable_indicators=[
            "Tool behavioral fingerprint changes significantly after package update",
            "Tool call patterns outside established baseline post-update",
            "Semantic drift in tool responses compared to pre-update baseline",
            "New tool calls appearing in session that were not in pre-update baseline",
        ],
        mcp_scan_coverage="Hash-based static check at install time — catches file changes but not semantic behavioral changes.",
        sources=["invariantlabs-ai.github.io/docs/mcp-scan", "OWASP MCP Top 10 MCP04"],
    ),

    AttackPattern(
        name="Hallucination Chain Propagation",
        category="inter_agent_semantic",
        severity=CVESeverity.HIGH,
        attack_layer=AttackLayer.CONTENT,
        description=(
            "Agent A produces an uncertain or incorrect claim. Agent B treats the "
            "uncertainty as established fact. Agent C cites it as a source. By the "
            "time it surfaces, the original error is multiple hops back and invisible. "
            "Does not produce an error — the pipeline runs to completion with bad output."
        ),
        owasp_ids=["ASI03", "ASI08"],
        insaits_detector="HallucinationChainDetector",
        detector_status=DetectorStatus.SHIPPED,
        observable_indicators=[
            "Confidence level increasing across agent hops for the same claim",
            "Hedging language ('approximately', 'might be') disappearing in downstream agents",
            "Claim becoming more specific as it propagates (17% range becomes exactly 17%)",
            "Citation of earlier agent output as external source",
        ],
        mcp_scan_coverage="Not covered — mcp-scan does not monitor inter-agent message content.",
        sources=["InsAIts V3 core feature", "OWASP ASI03"],
    ),

    AttackPattern(
        name="WhatsApp History Exfiltration",
        category="exfiltration",
        severity=CVESeverity.CRITICAL,
        attack_layer=AttackLayer.CONTENT,
        description=(
            "Poisoned MCP tool combined with legitimate whatsapp-mcp server to "
            "silently exfiltrate entire WhatsApp message history. The attack "
            "uses tool poisoning to redirect legitimate tool output to attacker."
        ),
        owasp_ids=["MCP03", "ASI06"],
        insaits_detector="ExfiltrationPatternDetector",
        detector_status=DetectorStatus.PRIORITY6,
        observable_indicators=[
            "Tool output being routed to unexpected destinations",
            "Large data payloads in tool results being forwarded",
            "Unusual BCC or forwarding patterns in communication tools",
            "Data volume in outbound tool calls inconsistent with session task",
        ],
        mcp_scan_coverage="Partial — tool description poisoning catchable statically. Runtime exfiltration routing not covered.",
        sources=["invariantlabs-ai.github.io/docs/mcp-scan"],
    ),

    AttackPattern(
        name="Postmark Clone / Service Impersonation",
        category="supply_chain",
        severity=CVESeverity.HIGH,
        attack_layer=AttackLayer.SUPPLY_CHAIN,
        description=(
            "First malicious MCP server found in the wild. Impersonated the "
            "legitimate Postmark email service. Secretly BCC'd every agent-sent "
            "email to attacker. Cited in OWASP Agentic Top 10."
        ),
        owasp_ids=["MCP04", "ASI06"],
        insaits_detector="ExfiltrationPatternDetector",
        detector_status=DetectorStatus.PRIORITY6,
        observable_indicators=[
            "Email tool calls with unexpected BCC recipients",
            "Tool semantic description inconsistent with observed behavior",
            "Output routing patterns not matching tool stated purpose",
            "Unexpected external endpoints in tool call arguments",
        ],
        mcp_scan_coverage="Tool description divergence catchable statically. Runtime BCC insertion not covered.",
        sources=["OWASP Agentic AI Top 10"],
    ),

    AttackPattern(
        name="Slopsquatting / Package Hallucination",
        category="supply_chain",
        severity=CVESeverity.HIGH,
        attack_layer=AttackLayer.SUPPLY_CHAIN,
        description=(
            "126 malicious packages registered under names that AI assistants "
            "hallucinate when recommending packages. Agents install them autonomously "
            "when they generate install commands for packages that don't exist. "
            "Called 'slopsquatting' — squatting on AI model slop outputs."
        ),
        owasp_ids=["MCP04"],
        insaits_detector="IncompleteCodeDetector",
        detector_status=DetectorStatus.SHIPPED,
        observable_indicators=[
            "Package names in generated code not matching any known registry entry",
            "Install commands for packages with no prior session reference",
            "Hallucinated package names with plausible but non-existent names",
        ],
        mcp_scan_coverage="Not covered — install-time check cannot catch packages that don't exist yet.",
        sources=["OWASP Agentic AI Top 10"],
    ),

    AttackPattern(
        name="Indirect Prompt Injection via Tool Results",
        category="content_injection",
        severity=CVESeverity.HIGH,
        attack_layer=AttackLayer.CONTENT,
        description=(
            "Malicious instructions embedded in external data that the agent reads "
            "via tools — web pages, emails, documents, database records. The agent "
            "follows the embedded instructions as if they were from the user."
        ),
        owasp_ids=["MCP03", "ASI02"],
        insaits_detector="PromptInjectionDetector",
        detector_status=DetectorStatus.SHIPPED,
        observable_indicators=[
            "Agent behavior changing after tool result is processed",
            "Instructions in tool results that override session goals",
            "Semantic goal divergence following external data retrieval",
            "Agent citing tool result content as authoritative instructions",
        ],
        mcp_scan_coverage="Not covered — happens at runtime after deployment.",
        sources=["OWASP MCP03", "Widespread — no single CVE"],
    ),

    AttackPattern(
        name="Cross-Agent Semantic Drift",
        category="inter_agent_semantic",
        severity=CVESeverity.MEDIUM,
        attack_layer=AttackLayer.CONTENT,
        description=(
            "Topic or meaning shifts gradually across a multi-agent conversation "
            "without explicit acknowledgment. Agent A's task definition becomes "
            "subtly different by Agent D. Particularly common in long sessions "
            "or pipelines with many hops."
        ),
        owasp_ids=["ASI01", "ASI07"],
        insaits_detector="SemanticDriftDetector",
        detector_status=DetectorStatus.SHIPPED,
        observable_indicators=[
            "Cosine similarity between early and late messages below threshold",
            "EWMA of embedding direction showing consistent drift",
            "Task description evolving across agent turns",
            "Agent outputs becoming semantically distant from original user request",
        ],
        mcp_scan_coverage="Not covered.",
        sources=["InsAIts V3 core feature"],
    ),

    AttackPattern(
        name="Jargon Drift / Cross-LLM Argot",
        category="inter_agent_semantic",
        severity=CVESeverity.MEDIUM,
        attack_layer=AttackLayer.CONTENT,
        description=(
            "Agents develop private shorthand or terminology that humans cannot "
            "understand. Can be emergent (accidental) or deliberate (early-stage "
            "covert channel). Facebook 2017 negotiation bots are the canonical "
            "documented example."
        ),
        owasp_ids=["ASI07"],
        insaits_detector="JargonDriftDetector",
        detector_status=DetectorStatus.SHIPPED,
        observable_indicators=[
            "Out-of-vocabulary terms appearing with increasing frequency",
            "Term frequency distribution diverging from model baseline",
            "Same concepts referred to by different terms across agents",
            "Vocabulary convergence between two supposedly independent agents",
        ],
        mcp_scan_coverage="Not covered.",
        sources=["InsAIts V3 core feature", "Facebook FAIR 2017 emergent communication research"],
    ),
]


# ─── OWASP Frameworks ─────────────────────────────────────────────────────────

OWASP_MCP_TOP10: List[OWASPRisk] = [
    OWASPRisk("MCP01", "MCP_TOP10", "Token Mismanagement & Secret Exposure",
              "Hardcoded API keys, tokens in logs, insufficient rotation.",
              "YES", "CredentialPatternDetector",
              "PII/credential pattern scanner in message content — build priority #3"),
    OWASPRisk("MCP02", "MCP_TOP10", "Privilege Escalation & Excessive Permissions",
              "Agents granted broad scopes. OAuth token confusion. Confused deputy attacks.",
              "PARTIAL", "ToolCallFrequencyAnomalyDetector",
              "Behavioral anomalies from over-privileged agents detectable — build priority #5"),
    OWASPRisk("MCP03", "MCP_TOP10", "Tool Poisoning & Prompt Injection",
              "Hidden instructions in tool descriptions executed by LLM.",
              "YES", "ToolDescriptionDivergenceDetector",
              "Semantic divergence between stated tool purpose and runtime behavior — build priority #1"),
    OWASPRisk("MCP04", "MCP_TOP10", "Supply Chain Attacks",
              "Malicious packages, typosquatting, rug pulls, backdoored servers.",
              "PARTIAL", "BehavioralFingerprintDetector",
              "Behavioral changes post-installation detectable — build priority #2"),
    OWASPRisk("MCP05", "MCP_TOP10", "Command Injection & RCE",
              "Unsanitized inputs to exec/shell functions in MCP server code.",
              "NO", None,
              "Infrastructure security — outside InsAIts message monitoring scope"),
    OWASPRisk("MCP06", "MCP_TOP10", "Insecure Memory & Context Sharing",
              "Context windows shared across sessions expose sensitive data.",
              "YES", "ContextCollapseDetector",
              "Context collapse detector catches cross-session semantic bleed — shipped V3"),
    OWASPRisk("MCP07", "MCP_TOP10", "Insufficient AuthN & AuthZ",
              "No authentication on MCP endpoints. Default no-auth.",
              "NO", None,
              "Infrastructure layer — outside InsAIts scope"),
    OWASPRisk("MCP08", "MCP_TOP10", "Lack of Telemetry & Audit Trails",
              "No logs of tool invocations, context changes.",
              "YES", "TamperEvidentAuditLog",
              "SHA-256 chained JSONL audit log — direct match — shipped V3"),
    OWASPRisk("MCP09", "MCP_TOP10", "Shadow MCP Servers",
              "Unapproved MCP deployments outside governance.",
              "PARTIAL", "ShadowAgentDetector",
              "New agent detection — build priority #7"),
    OWASPRisk("MCP10", "MCP_TOP10", "Cross-Tenant Data Leakage",
              "Shared MCP servers expose one tenant's data to another.",
              "YES", "InformationFlowTracker",
              "Data appearing where it should not — build priority #4"),
]

OWASP_AGENTIC_TOP10: List[OWASPRisk] = [
    OWASPRisk("ASI01", "AGENTIC_TOP10", "Goal Hijacking",
              "Agent's objective replaced mid-task by injected instructions.",
              "YES", "SemanticDriftDetector",
              "Context collapse + semantic drift detectors catch goal divergence — shipped V3"),
    OWASPRisk("ASI02", "AGENTIC_TOP10", "Prompt Manipulation",
              "Inputs craft prompts that override agent behavior.",
              "YES", "PromptInjectionDetector",
              "Prompt injection detector — shipped V3"),
    OWASPRisk("ASI03", "AGENTIC_TOP10", "Memory Poisoning",
              "Corrupting agent memory/context with false information.",
              "YES", "HallucinationChainDetector",
              "Hallucination chain detector catches false information propagation — shipped V3"),
    OWASPRisk("ASI04", "AGENTIC_TOP10", "Supply Chain Vulnerabilities",
              "Malicious tools introduced into agent environment.",
              "PARTIAL", "BehavioralFingerprintDetector",
              "Behavioral change post-installation detectable — build priority #2"),
    OWASPRisk("ASI05", "AGENTIC_TOP10", "Unexpected Code Execution",
              "Agent executes unintended code through tool abuse.",
              "PARTIAL", "ToolCallFrequencyAnomalyDetector",
              "Tool call anomaly detection — build priority #5"),
    OWASPRisk("ASI06", "AGENTIC_TOP10", "Sensitive Data Exposure",
              "PII, credentials, secrets exposed in agent communications.",
              "YES", "CredentialPatternDetector",
              "PII pattern scanner in message content — build priority #3"),
    OWASPRisk("ASI07", "AGENTIC_TOP10", "Inter-Agent Communication Abuse",
              "Malicious instructions passed between agents.",
              "YES", "SemanticDriftDetector",
              "InsAIts primary design target — shipped V3"),
    OWASPRisk("ASI08", "AGENTIC_TOP10", "Cascading Failures",
              "One agent's failure propagates and amplifies.",
              "YES", "HallucinationChainDetector",
              "Hallucination chain + circuit breaker — shipped V3"),
    OWASPRisk("ASI09", "AGENTIC_TOP10", "Rogue Agent Behavior",
              "Agent acts outside intended boundaries.",
              "YES", "POOMDPBeliefTracker",
              "POMDP belief tracker + circuit breaker — shipped V3"),
    OWASPRisk("ASI10", "AGENTIC_TOP10", "Governance & Accountability Gaps",
              "No audit trail. No compliance evidence.",
              "YES", "TamperEvidentAuditLog",
              "SHA-256 tamper-evident audit log — direct match — shipped V3"),
]


# ─── Query Helpers ────────────────────────────────────────────────────────────

def get_patterns_for_detector(detector_name: str) -> List[AttackPattern]:
    """Return all attack patterns that a given detector should catch."""
    return [p for p in ATTACK_PATTERNS if p.insaits_detector == detector_name]


def get_cves_for_detector(detector_name: str) -> List[CVEEntry]:
    """Return all CVEs that a given detector addresses."""
    return [c for c in CVE_REGISTRY if c.insaits_detector == detector_name]


def get_owasp_coverage(framework: str = "ALL") -> List[OWASPRisk]:
    """Return OWASP risks, optionally filtered by framework."""
    all_risks = OWASP_MCP_TOP10 + OWASP_AGENTIC_TOP10
    if framework == "ALL":
        return all_risks
    return [r for r in all_risks if r.framework == framework]


def get_build_priorities() -> List[AttackPattern]:
    """Return attack patterns ordered by build priority (not yet shipped)."""
    priority_order = [
        DetectorStatus.PRIORITY1, DetectorStatus.PRIORITY2,
        DetectorStatus.PRIORITY3, DetectorStatus.PRIORITY4,
        DetectorStatus.PRIORITY5, DetectorStatus.PRIORITY6,
        DetectorStatus.PRIORITY7, DetectorStatus.PRIORITY8,
    ]
    result = []
    for priority in priority_order:
        result.extend([p for p in ATTACK_PATTERNS if p.detector_status == priority])
    return result


def get_coverage_summary() -> dict:
    """Return a summary of InsAIts coverage across OWASP frameworks."""
    all_risks = OWASP_MCP_TOP10 + OWASP_AGENTIC_TOP10
    covered   = [r for r in all_risks if r.insaits_coverage == "YES"]
    partial   = [r for r in all_risks if r.insaits_coverage == "PARTIAL"]
    not_cover = [r for r in all_risks if r.insaits_coverage == "NO"]
    return {
        "total_risks":    len(all_risks),
        "fully_covered":  len(covered),
        "partial":        len(partial),
        "not_covered":    len(not_cover),
        "coverage_pct":   round((len(covered) + len(partial) * 0.5) / len(all_risks) * 100, 1),
        "covered_ids":    [r.risk_id for r in covered],
        "partial_ids":    [r.risk_id for r in partial],
        "gap_ids":        [r.risk_id for r in not_cover],
    }


def get_shipped_detectors() -> List[str]:
    """Return list of detector names already shipped in V3."""
    shipped = set()
    for p in ATTACK_PATTERNS:
        if p.detector_status == DetectorStatus.SHIPPED and p.insaits_detector:
            shipped.add(p.insaits_detector)
    for c in CVE_REGISTRY:
        if c.detector_status == DetectorStatus.SHIPPED and c.insaits_detector:
            shipped.add(c.insaits_detector)
    return sorted(shipped)


# ─── Quick sanity check ───────────────────────────────────────────────────────

if __name__ == "__main__":
    print("=== InsAIts CVE Signatures — Sanity Check ===\n")

    print(f"CVEs in registry:        {len(CVE_REGISTRY)}")
    print(f"Attack patterns:         {len(ATTACK_PATTERNS)}")
    print(f"OWASP risks tracked:     {len(OWASP_MCP_TOP10) + len(OWASP_AGENTIC_TOP10)}\n")

    summary = get_coverage_summary()
    print(f"Coverage summary:")
    print(f"  Fully covered:  {summary['fully_covered']}/{summary['total_risks']}")
    print(f"  Partial:        {summary['partial']}/{summary['total_risks']}")
    print(f"  Not covered:    {summary['not_covered']}/{summary['total_risks']}")
    print(f"  Coverage score: {summary['coverage_pct']}%\n")

    print(f"Shipped detectors (V3):  {get_shipped_detectors()}\n")

    print("Build priority queue:")
    for i, pattern in enumerate(get_build_priorities(), 1):
        print(f"  #{i}: {pattern.insaits_detector} — {pattern.name}")

    print("\n=== OK ===")
