#!/usr/bin/env python3
"""Public, bounded adversarial mutation matrix for authz-web."""
import copy, hashlib, json, pathlib, shutil, subprocess, sys, tempfile
ROOT = pathlib.Path(__file__).resolve().parent

def canonical(v): return json.dumps(v, sort_keys=True, separators=(",", ":"))
def sha(v):
    b = v if isinstance(v, bytes) else canonical(v).encode()
    return "sha256:" + hashlib.sha256(b).hexdigest()
def load(p, n): return json.loads((p / n).read_text())
def save(p, n, v): (p / n).write_text(json.dumps(v, indent=2) + "\n")
def rows(p): return [json.loads(x) for x in (p / "cases.jsonl").read_text().splitlines() if x]
def save_rows(p, rs, rehash=True):
    (p / "cases.jsonl").write_text("\n".join(canonical(x) for x in rs) + "\n")
    if rehash:
        m=load(p,"manifest.json");m["casesSha256"]=sha((p/"cases.jsonl").read_bytes());save(p,"manifest.json",m)
def response_rehash(r):
    e=r["expected"];r["oracle"]["responseBindingSha256"]=sha({"status":e["status"],"envelope":e["envelope"],"outcome":e["outcome"],"headers":e["headers"],"effects":e["effects"]})

def mutate(p, attack):
    rs=rows(p); by={r["id"]:r for r in rs}
    if attack=="ADV-01": by["AW-001"]["request"]["method"]="POST"
    elif attack=="ADV-02":
        r=by["AW-002"];r["expected"]["status"]=200;response_rehash(r)
    elif attack=="ADV-03":
        r=by["AW-044"];r["expected"]["effects"]["signerCalls"]=1;response_rehash(r)
    elif attack=="ADV-04": by["AW-065"]["expected"]["eventResults"][1]["mutationCommitted"]=True
    elif attack=="ADV-05":
        a=load(p,"principals.json");a["objects"]["conversionA"]["ownerId"]=a["principals"]["userB"]["id"];save(p,"principals.json",a)
        by["AW-037"]["oracle"]["targets"][0]["ownerId"]=a["principals"]["userB"]["id"]
    elif attack=="ADV-06": by["AW-094"]["expected"]["auditRecords"]=[]
    elif attack=="ADV-07": by["AW-003"]["request"]["headers"]["Authorization"]="Bearer valid_bearer_user_a"
    elif attack=="ADV-08": by["AW-067"]["expected"]["rateAssertion"]["limit"]=11
    elif attack=="ADV-09":
        c=load(p,"contract-registry.json");c["contracts"]["HTTP-007"]["exactRevision"]="attacker";c["registrySha256"]=sha(c["contracts"]);save(p,"contract-registry.json",c)
    elif attack=="ADV-10": by["AW-124"]["oracle"]["preflight"]["category"]="reject_header"
    elif attack=="ADV-11":
        s=load(p,"signed-inventory-baseline.json");s["inventory"]["nodes"].pop();s["inventorySha256"]=sha(s["inventory"]);save(p,"signed-inventory-baseline.json",s)
        m=load(p,"manifest.json");m["loggingInventory"]=s["inventory"];m["loggingInventorySha256"]=sha(s["inventory"]);save(p,"manifest.json",m)
    elif attack=="ADV-12":
        rs[0]["description"],rs[1]["description"]=rs[1]["description"],rs[0]["description"]
        m=load(p,"manifest.json");m["semanticKeys"]={r["id"]:r["description"] for r in rs};m["semanticRegistrySha256"]=sha(m["semanticKeys"]);save(p,"manifest.json",m)
    elif attack=="ADV-13":
        for r in rs:r["expected"]["transitions"]=[]
    save_rows(p,rs)

def main():
    attacks=[x["id"] for x in load(ROOT,"negative-matrix.json")["attacks"]]
    failed=[]
    for attack in attacks:
        with tempfile.TemporaryDirectory(prefix="authz-web-attack-") as td:
            work=pathlib.Path(td);shutil.copytree(ROOT,work/"fixture",dirs_exist_ok=True);fixture=work/"fixture";mutate(fixture,attack)
            run=subprocess.run([sys.executable,str(fixture/"validate.py")],text=True,capture_output=True)
            if run.returncode==0: failed.append(attack)
            else: print(f"ATTACK_REJECTED {attack}")
    if failed:
        print("FAIL attacks accepted="+",".join(failed));return 1
    print(f"PASS adversarial={len(attacks)} all=rejected")
    return 0
if __name__=="__main__": raise SystemExit(main())
