#!/usr/bin/env python3 """Preflight fixture tests for Musechain task 240 (idea: preflight checks). Tests the preflight checker (charter_risk_checks.py, task 239) against a fixture set of THREE distinct deployed Musechain contract records: 1. community-needs-board 0x295b52211d83fc2b6e985d0c895542a84cfcd8c9 (muse 18) — dynamic-input writes (submitRequest/setLinkedWork/resolveRequest) 2. outreach-trial-board-v2 0xa1ed5eb9a443457e28e20184bd7887dd749c6fe8 (muse 18) — safe zero-value writes; string inputs bounded in source 3. muse-contract-review 0x90c495851da1e56916f756477003b2b7e2edd719 (muse 10) — review-workflow contract; one dynamic input, otherwise clean Every expected finding is ASSERTED here, not eyeballed. Findings JSON is written to findings/ so a reviewer can diff expected vs actual. Reproduce: python3 test_preflight_fixtures.py # runs all assertions python3 charter_risk_checks.py --abi fixtures//abi.json \ --source fixtures//source.sol --verified # single fixture Platform-wide claims (no payable entries anywhere, no unverified records, no missing sources) are asserted against platform-scan-2026-10-02.json, a snapshot of all 14 GET /v1/contracts records taken 2026-10-02. Contract addresses in the fixtures let a reviewer re-pull the records live. """ import json import os import re import sys HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, HERE) from charter_risk_checks import check # noqa: E402 ACCESS_RE = re.compile( r"\b(onlyOwner|onlyowner|onlyRole|onlyAdmin|authorized|onlyOperator|onlyGuardian|requiresAuth)\b", re.I, ) VALUE_RE = re.compile(r"\bmsg\.value\b|\.call\s*\{\s*value\s*:|\.(transfer|send)\s*\(") PASS = [] FAIL = [] def ok(name, cond, detail=""): (PASS if cond else FAIL).append(name) print(("PASS " if cond else "FAIL ") + name + (f" — {detail}" if detail else "")) def load_fixture(name): d = os.path.join(HERE, "fixtures", name) abi = json.load(open(os.path.join(d, "abi.json"))) src = open(os.path.join(d, "source.sol")).read() return abi, src def sigs(fs, rule): return sorted(f["signature"] for f in fs if f["check"] == rule) def main(): meta = json.load(open(os.path.join(HERE, "fixture-meta.json"))) scan = json.load(open(os.path.join(HERE, "platform-scan-2026-10-02.json"))) # --- A. Fixture set: >=3 distinct deployed records, verified, sourced --- addrs = [meta[n]["address"] for n in meta] ok("A1 three distinct deployed records", len(set(addrs)) == 3, ", ".join(addrs)) ok("A2 all fixture records verified=True", all(meta[n]["verified"] for n in meta)) fixtures = {n: load_fixture(n) for n in meta} ok("A3 all fixture sources non-empty", all(len(src) > 500 for _, src in fixtures.values())) # --- B. Platform-wide: payable/value unavailable, everything verified+sourced --- ok("B1 14 contract records scanned", len(scan) == 14) ok("B2 zero payable entries across all 14 ABIs", sum(s["payable_entries"] for s in scan) == 0, "payable/value fixtures are NOT available on-platform (platform screens them)") ok("B3 zero unverified records", all(s["verified"] for s in scan), "the unverified case does not exist on-platform; R5 is exercised synthetically (E2)") ok("B4 zero records with missing source", all(s["source_present"] for s in scan), "the unavailable-source case does not exist on-platform") # --- C. R3 dynamic inputs: expected warns, exact signatures asserted --- cnb = check(*fixtures["community-needs-board"], True) ok("C1 cnb: exactly 4 R3 findings", len(sigs(cnb, "R3 unbounded_input")) == 4, str(sigs(cnb, "R3 unbounded_input"))) ok("C2 cnb: submitRequest(string,string) flagged twice", sigs(cnb, "R3 unbounded_input").count("submitRequest(string,string)") == 2) ok("C3 cnb: resolveRequest+setLinkedWork flagged", "resolveRequest(uint256,string)" in sigs(cnb, "R3 unbounded_input") and "setLinkedWork(uint256,string)" in sigs(cnb, "R3 unbounded_input")) _, cnb_src = fixtures["community-needs-board"] ok("C4 cnb: source enforces length caps (documented mitigation)", "MAX_TITLE_LEN" in cnb_src and "MAX_DESCRIPTION_LEN" in cnb_src and "MAX_LINKED_WORK_LEN" in cnb_src) otb = check(*fixtures["outreach-trial-board-v2"], True) otb_r3 = sigs(otb, "R3 unbounded_input") ok("C5 otbv2: 5 R3 findings (ABI-level string inputs)", len(otb_r3) == 5, str(otb_r3)) ok("C6 otbv2: createTrial(string x4) + updateNextAction flagged", otb_r3.count("createTrial(string,string,string,string)") == 4 and "updateNextAction(uint256,string)" in otb_r3) _, otb_src = fixtures["outreach-trial-board-v2"] ok("C7 otbv2: source enforces bounded string lengths (documented mitigation)", "MAX_ROUTE" in otb_src and "MAX_INVITATION" in otb_src and "MAX_DATE" in otb_src and bool(re.search(r"require\(bytes\(\w+\)\.length\s*<=", otb_src))) mcr = check(*fixtures["muse-contract-review"], True) mcr_r3 = sigs(mcr, "R3 unbounded_input") ok("C8 mcr: exactly 1 R3 finding (submitReview note)", mcr_r3 == ["submitReview(address,bool,string)"], str(mcr_r3)) # --- D. R1/R2/R4/R6: assert ABSENCE across all three fixtures --- for name in meta: fs = check(*fixtures[name], True) others = [f for f in fs if f["check"] in ("R1 payable_entry", "R2 value_bearing_src", "R4 restricted_access", "R6 selfdestruct_src", "R6 delegatecall_src")] ok(f"D1 {name}: no R1/R2/R4/R6 findings", others == [], str(others)[:100]) ok("D2 no value-bearing source patterns in fixture sources", not any(VALUE_RE.search(src) for _, src in fixtures.values())) ok("D3 no access-control modifiers in fixture sources (restricted writes: none on-platform)", not any(ACCESS_RE.search(src) for _, src in fixtures.values()), "R4 has no on-platform target; rule covered by checker's own unit fixtures (task 239)") # --- E. R5: verified => silent; unverified flag => finding fires --- for name in meta: fs = check(*fixtures[name], True) ok(f"E1 {name}: no R5 finding when verified=True", not any(f["check"] == "R5 missing_verify" for f in fs)) cnb_unver = check(*fixtures["community-needs-board"], False) ok("E2 R5 fires when verified=False (synthetic unverified case)", any(f["check"] == "R5 missing_verify" and f["severity"] == "info" for f in cnb_unver)) # --- F. Emit findings JSON (documented expected output) --- fdir = os.path.join(HERE, "findings") os.makedirs(fdir, exist_ok=True) for name in meta: fs = check(*fixtures[name], True) json.dump(fs, open(os.path.join(fdir, f"{name}.verified.json"), "w"), indent=2) json.dump(check(*fixtures["community-needs-board"], False), open(os.path.join(fdir, "community-needs-board.unverified.json"), "w"), indent=2) ok("F1 findings JSON written for all fixtures", True, fdir) print(f"\n{len(PASS)} passed, {len(FAIL)} failed") return 1 if FAIL else 0 if __name__ == "__main__": sys.exit(main())