"""A deterministic, synthetic validation loop inspired by Mandiant AVDH.

This is a teaching artefact, not a vulnerability scanner. It deliberately
starts with six over-reported hypotheses, applies a threat-model gate, and
then uses harmless binary oracles to confirm or disprove the remaining cases.
"""

from __future__ import annotations

import json
import re
import subprocess
import sys
from dataclasses import asdict, dataclass
from typing import Callable


MARKER = "AVDH_ORACLE_CONFIRMED"


@dataclass(frozen=True)
class Hypothesis:
    name: str
    vulnerability_class: str
    exposure: str
    expected: str
    oracle: Callable[[], bool]


def vulnerable_command_oracle() -> bool:
    """Execute a harmless marker through a deliberately unsafe shell string."""
    payload = f"safe & echo {MARKER}" if sys.platform == "win32" else f"safe; printf {MARKER}"
    result = subprocess.run(
        f"echo {payload}",
        shell=True,
        capture_output=True,
        text=True,
        timeout=2,
        check=False,
    )
    return MARKER in result.stdout


def sanitised_command_oracle() -> bool:
    payload = f"safe & echo {MARKER}" if sys.platform == "win32" else f"safe; printf {MARKER}"
    if not re.fullmatch(r"[a-z0-9.-]{1,64}", payload):
        return False
    result = subprocess.run(
        ["echo", payload],
        shell=False,
        capture_output=True,
        text=True,
        timeout=2,
        check=False,
    )
    return MARKER in result.stdout


def vulnerable_idor_oracle() -> bool:
    request_user = "bob"
    invoice_owner = "alice"
    endpoint_returns_invoice = True
    return request_user != invoice_owner and endpoint_returns_invoice


def authorised_invoice_oracle() -> bool:
    request_user = "bob"
    invoice_owner = "alice"
    endpoint_returns_invoice = request_user == invoice_owner
    return request_user != invoice_owner and endpoint_returns_invoice


def unreachable_oracle() -> bool:
    raise AssertionError("The threat-model gate must prevent this oracle from running")


HYPOTHESES = (
    Hypothesis("public-shell-concatenation", "command injection", "public", "confirmed", vulnerable_command_oracle),
    Hypothesis("allowlisted-shell-argument", "command injection", "public", "disproven", sanitised_command_oracle),
    Hypothesis("admin-maintenance-shell", "command injection", "admin-only", "rejected", unreachable_oracle),
    Hypothesis("dead-debug-shell", "command injection", "dead-code", "rejected", unreachable_oracle),
    Hypothesis("invoice-without-owner-check", "broken object-level authorisation", "public", "confirmed", vulnerable_idor_oracle),
    Hypothesis("invoice-with-owner-check", "broken object-level authorisation", "public", "disproven", authorised_invoice_oracle),
)


def validate(hypothesis: Hypothesis) -> dict[str, str]:
    if hypothesis.exposure != "public":
        return {
            "name": hypothesis.name,
            "class": hypothesis.vulnerability_class,
            "outcome": "rejected",
            "reason": f"threat model marks entry point {hypothesis.exposure}",
        }

    proved = hypothesis.oracle()
    return {
        "name": hypothesis.name,
        "class": hypothesis.vulnerability_class,
        "outcome": "confirmed" if proved else "disproven",
        "reason": "binary oracle reproduced impact" if proved else "binary oracle did not reproduce impact",
    }


def main() -> int:
    findings = [validate(hypothesis) for hypothesis in HYPOTHESES]
    expected = [hypothesis.expected for hypothesis in HYPOTHESES]
    observed = [finding["outcome"] for finding in findings]
    if observed != expected:
        print(json.dumps({"error": "benchmark mismatch", "expected": expected, "observed": observed}, indent=2))
        return 1

    counts = {outcome: observed.count(outcome) for outcome in ("confirmed", "disproven", "rejected")}
    result = {
        "scope": "six hand-built synthetic cases; not a production benchmark",
        "candidate_hypotheses": len(HYPOTHESES),
        "counts": counts,
        "precision_before_validation": round(counts["confirmed"] / len(HYPOTHESES), 3),
        "precision_after_validation": 1.0 if counts["confirmed"] else 0.0,
        "findings": findings,
    }
    print(json.dumps(result, indent=2))
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
