From ef1e235fe59eafab920ead09cf7a974e7a0e1d32 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 4 Jun 2026 15:30:45 +0000 Subject: [PATCH 1/4] ci: add timeout-minutes to all runs-on jobs (Hypatia hardening) Adds timeout-minutes to every runs-on job across the workflow suite that lacked one (27 jobs, 12 files); reusable-workflow-call jobs are exempt (GitHub rejects job-level timeout-minutes there). Clears the bulk of the Hypatia missing_timeout_minutes findings. Lint/policy jobs 10m, build/ test/scan 20m, heavy chapel/e2e/release jobs 45m. https://claude.ai/code/session_01K2TJLeQSyz4tpydZ18aRcb --- .github/workflows/boj-build.yml | 1 + .github/workflows/cargo-audit.yml | 1 + .github/workflows/casket-pages.yml | 2 ++ .github/workflows/chapel-ci.yml | 8 ++++++++ .github/workflows/codeql.yml | 1 + .github/workflows/coverage.yml | 1 + .github/workflows/dependency-review.yml | 1 + .github/workflows/dogfood-gate.yml | 5 +++++ .github/workflows/instant-sync.yml | 1 + .github/workflows/release.yml | 3 +++ .github/workflows/scan-and-report.yml | 1 + .github/workflows/scorecard-enforcer.yml | 2 ++ 12 files changed, 27 insertions(+) diff --git a/.github/workflows/boj-build.yml b/.github/workflows/boj-build.yml index 3881d95..653a527 100644 --- a/.github/workflows/boj-build.yml +++ b/.github/workflows/boj-build.yml @@ -9,6 +9,7 @@ permissions: jobs: trigger-boj: runs-on: ubuntu-latest + timeout-minutes: 10 steps: - name: Checkout uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 diff --git a/.github/workflows/cargo-audit.yml b/.github/workflows/cargo-audit.yml index 5f57eed..fc5e02b 100644 --- a/.github/workflows/cargo-audit.yml +++ b/.github/workflows/cargo-audit.yml @@ -16,6 +16,7 @@ jobs: audit: name: Cargo Audit runs-on: ubuntu-latest + timeout-minutes: 20 steps: - name: Checkout code uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 diff --git a/.github/workflows/casket-pages.yml b/.github/workflows/casket-pages.yml index ad8fd14..fd280fb 100644 --- a/.github/workflows/casket-pages.yml +++ b/.github/workflows/casket-pages.yml @@ -18,6 +18,7 @@ concurrency: jobs: build: runs-on: ubuntu-latest + timeout-minutes: 20 steps: - name: Checkout uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 @@ -109,6 +110,7 @@ jobs: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest + timeout-minutes: 10 needs: build steps: - name: Deploy to GitHub Pages diff --git a/.github/workflows/chapel-ci.yml b/.github/workflows/chapel-ci.yml index 2ed1b98..7b68ddc 100644 --- a/.github/workflows/chapel-ci.yml +++ b/.github/workflows/chapel-ci.yml @@ -65,6 +65,7 @@ jobs: detect-relevant-changes: name: detect-relevant-changes runs-on: ubuntu-22.04 + timeout-minutes: 10 outputs: relevant: ${{ steps.f.outputs.relevant }} steps: @@ -99,6 +100,7 @@ jobs: needs: detect-relevant-changes if: needs.detect-relevant-changes.outputs.relevant == 'true' runs-on: ubuntu-22.04 + timeout-minutes: 45 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Chapel ${{ env.CHAPEL_VERSION }} @@ -121,6 +123,7 @@ jobs: needs: [detect-relevant-changes, chapel-parse-check] if: needs.detect-relevant-changes.outputs.relevant == 'true' runs-on: ubuntu-22.04 + timeout-minutes: 45 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Chapel ${{ env.CHAPEL_VERSION }} @@ -151,6 +154,7 @@ jobs: needs: [detect-relevant-changes, chapel-build] if: needs.detect-relevant-changes.outputs.relevant == 'true' runs-on: ubuntu-22.04 + timeout-minutes: 45 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Chapel ${{ env.CHAPEL_VERSION }} @@ -175,6 +179,7 @@ jobs: needs: [detect-relevant-changes, chapel-build] if: needs.detect-relevant-changes.outputs.relevant == 'true' runs-on: ubuntu-22.04 + timeout-minutes: 45 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Chapel ${{ env.CHAPEL_VERSION }} @@ -211,6 +216,7 @@ jobs: needs: detect-relevant-changes if: needs.detect-relevant-changes.outputs.relevant == 'true' runs-on: ubuntu-22.04 + timeout-minutes: 45 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: dtolnay/rust-toolchain@4be9e76fd7c4901c61fb841f559994984270fce7 # stable @@ -231,6 +237,7 @@ jobs: needs: [detect-relevant-changes, chapel-build] if: needs.detect-relevant-changes.outputs.relevant == 'true' runs-on: ubuntu-22.04 + timeout-minutes: 45 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: dtolnay/rust-toolchain@4be9e76fd7c4901c61fb841f559994984270fce7 # stable @@ -417,6 +424,7 @@ jobs: - chapel-multilocale if: always() runs-on: ubuntu-22.04 + timeout-minutes: 45 steps: - name: Aggregate chapel-ci results env: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index a499a0b..2b9bd06 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -23,6 +23,7 @@ permissions: jobs: analyze: runs-on: ubuntu-latest + timeout-minutes: 20 permissions: contents: read security-events: write diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 7ff2600..6bc0eec 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -14,6 +14,7 @@ jobs: coverage: name: Generate Coverage Report runs-on: ubuntu-latest + timeout-minutes: 20 steps: - name: Checkout code uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index dc19783..dcc75d6 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -13,6 +13,7 @@ jobs: review: name: Review Dependencies runs-on: ubuntu-latest + timeout-minutes: 10 steps: - name: Checkout code uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 diff --git a/.github/workflows/dogfood-gate.yml b/.github/workflows/dogfood-gate.yml index 06fe3b3..663d031 100644 --- a/.github/workflows/dogfood-gate.yml +++ b/.github/workflows/dogfood-gate.yml @@ -22,6 +22,7 @@ jobs: a2ml-validate: name: Validate A2ML manifests runs-on: ubuntu-latest + timeout-minutes: 10 steps: - name: Checkout repository @@ -66,6 +67,7 @@ jobs: k9-validate: name: Validate K9 contracts runs-on: ubuntu-latest + timeout-minutes: 10 steps: - name: Checkout repository @@ -128,6 +130,7 @@ jobs: empty-lint: name: Empty-linter (invisible characters) runs-on: ubuntu-latest + timeout-minutes: 10 steps: - name: Checkout repository @@ -192,6 +195,7 @@ jobs: groove-check: name: Groove manifest check runs-on: ubuntu-latest + timeout-minutes: 10 steps: - name: Checkout repository @@ -250,6 +254,7 @@ jobs: dogfood-summary: name: Dogfooding compliance summary runs-on: ubuntu-latest + timeout-minutes: 10 needs: [a2ml-validate, k9-validate, empty-lint, groove-check] if: always() diff --git a/.github/workflows/instant-sync.yml b/.github/workflows/instant-sync.yml index 228dc43..00531c1 100644 --- a/.github/workflows/instant-sync.yml +++ b/.github/workflows/instant-sync.yml @@ -14,6 +14,7 @@ permissions: jobs: dispatch: runs-on: ubuntu-latest + timeout-minutes: 10 steps: - name: Trigger Propagation uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4a929f0..28b6526 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,6 +18,7 @@ jobs: build: name: Build Release Binary runs-on: ubuntu-latest + timeout-minutes: 45 permissions: contents: read outputs: @@ -70,6 +71,7 @@ jobs: changelog: name: Generate Changelog runs-on: ubuntu-latest + timeout-minutes: 10 permissions: contents: read outputs: @@ -114,6 +116,7 @@ jobs: name: Create GitHub Release needs: [build, changelog] runs-on: ubuntu-latest + timeout-minutes: 45 permissions: contents: write steps: diff --git a/.github/workflows/scan-and-report.yml b/.github/workflows/scan-and-report.yml index 5c5fe5b..cb04a1a 100644 --- a/.github/workflows/scan-and-report.yml +++ b/.github/workflows/scan-and-report.yml @@ -21,6 +21,7 @@ permissions: jobs: scan: runs-on: ubuntu-latest + timeout-minutes: 20 steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 diff --git a/.github/workflows/scorecard-enforcer.yml b/.github/workflows/scorecard-enforcer.yml index 6933b78..c37d76c 100644 --- a/.github/workflows/scorecard-enforcer.yml +++ b/.github/workflows/scorecard-enforcer.yml @@ -23,6 +23,7 @@ permissions: jobs: scorecard: runs-on: ubuntu-latest + timeout-minutes: 20 permissions: security-events: write id-token: write # For OIDC @@ -61,6 +62,7 @@ jobs: # Check specific high-priority items check-critical: runs-on: ubuntu-latest + timeout-minutes: 10 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 From b4fb18e1471c7137a413930e27973814b2d638de Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 4 Jun 2026 15:30:45 +0000 Subject: [PATCH 2/4] fix(assay): drop flagged unwrap via let-else; proofs(storage): hexad round-trip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - assay::assimilate: replace `source.unwrap()` (flagged CWE-754 by Hypatia, though guarded) with a `let Some(source) = source else { ... }` that folds the already-handled None branch in. No behaviour change; zero unwrap. - storage: add proptest `hexad_json_roundtrip_is_identity` (PROOF-PROGRAMME §3.1, faithful form). The gateway octad projection is lossy by design, so the load-bearing integrity property is the on-disk serde round-trip used by write_*_hexad -> load_hexad_dir; this proves it is the identity on the hexad JSON representation. https://claude.ai/code/session_01K2TJLeQSyz4tpydZ18aRcb --- src/assay/mod.rs | 5 ++-- src/storage/mod.rs | 62 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/src/assay/mod.rs b/src/assay/mod.rs index 8344da8..06f251f 100644 --- a/src/assay/mod.rs +++ b/src/assay/mod.rs @@ -400,7 +400,7 @@ pub fn assimilate(config: AssimilateConfig) -> Result { ); // No source and module already in-tree → call-site rewiring only. - if source.is_none() { + let Some(source) = source else { if cand.port_present { let record = AssimilationRecord { schema_version: assay_schema_version(), @@ -427,8 +427,7 @@ pub fn assimilate(config: AssimilateConfig) -> Result { "{}: no replacement source; supply --proven or --from ", cand.id )); - } - let source = source.unwrap(); + }; if !source.is_file() { return Err(anyhow!( "{}: replacement source {} is not a file", diff --git a/src/storage/mod.rs b/src/storage/mod.rs index 7401bcf..3e8cad2 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -1351,6 +1351,7 @@ pub fn latest_reports(dir: &Path, count: usize) -> Result> { #[cfg(test)] mod tests { use super::*; + use proptest::prelude::*; #[test] fn test_uuid_from_timestamp() { @@ -1434,6 +1435,67 @@ mod tests { } } + // PROOF-PROGRAMME §3.1 (Hexad↔Octad), faithful form. + // + // The gateway projection `hexad_to_octad_request` is intentionally + // *lossy* — it flattens the semantic facet into a string metadata map + // and drops `id` / the optional sub-facets — so it is NOT an + // isomorphism. The property that actually underwrites on-disk integrity + // is the serde round-trip exercised by `write_*_hexad` → `load_hexad_dir`: + // a hexad serialised to its canonical JSON and read back must be + // identical. This proptest establishes that round-trip is the identity + // on the hexad's JSON representation (compared as `serde_json::Value`, + // so it is independent of key ordering). Equality is checked on the + // Value rather than the struct because `PanicAttackHexad` deliberately + // does not derive `PartialEq`. + proptest! { + #[test] + fn hexad_json_roundtrip_is_identity( + id in "[A-Za-z0-9:_./-]{0,48}", + program in "[A-Za-z0-9:_./-]{0,48}", + language in "[a-z]{0,12}", + version in "[0-9.]{1,8}", + total in 0usize..10_000, + crit in 0usize..10_000, + high in 0usize..10_000, + crashes in 0usize..10_000, + robustness in 0.0f64..=1.0, + cats in proptest::collection::vec("[A-Za-z]{1,16}", 0..6), + attest in proptest::option::of("[0-9a-f]{0,64}"), + ) { + let hexad = PanicAttackHexad { + schema: "panic-attack-hexad/1".to_string(), + id, + created_at: "2026-06-04T00:00:00Z".to_string(), + provenance: HexadProvenance { + tool: "panic-attack".to_string(), + version, + program_path: program, + language, + attestation_hash: attest, + }, + semantic: HexadSemantic { + total_weak_points: total, + critical_count: crit, + high_count: high, + total_crashes: crashes, + robustness_score: robustness, + categories: cats, + migration: None, + finding: None, + campaign: None, + crosslang: None, + }, + document: serde_json::json!({ "weak_points": [], "n": total }), + }; + let v1 = serde_json::to_value(&hexad).expect("serialise"); + let back: PanicAttackHexad = + serde_json::from_value(v1.clone()).expect("deserialise"); + let v2 = serde_json::to_value(&back).expect("re-serialise"); + prop_assert_eq!(v1, v2); + } + } + #[test] fn build_finding_id_stable_per_finding() { let wp = sample_weak_point("src/main.rs", 42, WeakPointCategory::UnsafeCode); From 94aed043876214c3cb5779618efebef5ea83d52f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 4 Jun 2026 17:14:18 +0000 Subject: [PATCH 3/4] docs: correct stale counts + surface assay/assimilate/aggregate - 20 -> 38 subcommands (CLAUDE.md, EXPLAINME, README) - 47 -> 49 languages (CLAUDE.md overview, EXPLAINME claim-1) - add aggregate/ + assay/ to the CLAUDE.md module tree - list assay/assimilate/aggregate among the CLI examples https://claude.ai/code/session_01K2TJLeQSyz4tpydZ18aRcb --- .claude/CLAUDE.md | 6 ++++-- EXPLAINME.adoc | 4 ++-- README.adoc | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index 693a587..bcd187f 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -2,7 +2,7 @@ ## Overview -Static analysis and bug signature detection tool. Scans source code for weak points (unwrap/expect, unsafe blocks, panic sites, error handling gaps, command injection, unsafe deserialization, FFI boundaries, atom exhaustion, and more) across 47 programming languages. +Static analysis and bug signature detection tool. Scans source code for weak points (unwrap/expect, unsafe blocks, panic sites, error handling gaps, command injection, unsafe deserialization, FFI boundaries, atom exhaustion, and more) across 49 programming languages. **Position in AmbientOps ecosystem**: Part of the hospital model, loosely affiliated. Sits alongside the Operating Room as a diagnostic tool for software health (while hardware-crash-team handles hardware health). Independent top-level repo, but feeds findings to the hospital's Records system via verisimdb. @@ -19,7 +19,7 @@ Static analysis and bug signature detection tool. Scans source code for weak poi ``` src/ -├── main.rs # CLI entry point (clap) — 20 subcommands +├── main.rs # CLI entry point (clap) — 38 subcommands ├── lib.rs # Library API ├── types.rs # Core types (AssailReport, WeakPoint, etc.) ├── assail/ # Static analysis engine @@ -55,6 +55,8 @@ src/ ├── amuck/ # Mutation combinations ├── abduct/ # Isolation + time-skew ├── adjudicate/ # Campaign verdict aggregation +├── aggregate/ # Fold external prover output into reports (BLAKE3-hashed, trust-tagged) +├── assay/ # Proven-library substitution survey + assimilate swap ├── axial/ # Reaction observation ├── bridge/ # Patch Bridge — CVE mitigation lifecycle (feature: http) │ ├── mod.rs # Triage orchestrator, core types (BridgeReport, AssessedCve) diff --git a/EXPLAINME.adoc b/EXPLAINME.adoc index 40eaff5..3d2bf86 100644 --- a/EXPLAINME.adoc +++ b/EXPLAINME.adoc @@ -9,7 +9,7 @@ The README makes claims. This file backs them up. === Claim 1: "49-language static analysis across multiple families" -**How it works:** The `src/assail/analyzer.rs` module implements a per-file language detector that identifies file extension and shebang, dispatching to language-specific pattern matchers in `src/assail/patterns.rs`. Each language family (C/C++, Python, JavaScript, Rust, Go, etc.) has dedicated regex-based weak point detectors (unwrap, panic, unsafe blocks, expect, eval, hardcoded secrets). The analyzer processes 47 distinct language patterns without requiring external parsers—pattern-based shallow analysis enables fast scanning across heterogeneous codebases. +**How it works:** The `src/assail/analyzer.rs` module implements a per-file language detector that identifies file extension and shebang, dispatching to language-specific pattern matchers in `src/assail/patterns.rs`. Each language family (C/C++, Python, JavaScript, Rust, Go, etc.) has dedicated regex-based weak point detectors (unwrap, panic, unsafe blocks, expect, eval, hardcoded secrets). The analyzer processes 49 distinct language patterns without requiring external parsers—pattern-based shallow analysis enables fast scanning across heterogeneous codebases. **Caveat:** Pattern-based analysis has false negatives (e.g., dead code paths triggering unwrap won't be caught) and false positives (commented-out panic sites flagged). Full AST-based analysis would eliminate these but scale poorly. For production, pair with specialized language linters (clippy, pylint) for each language. @@ -41,7 +41,7 @@ The README makes claims. This file backs them up. |=== | Path | Purpose -| `src/main.rs` | CLI entry: 20 subcommands (assail, assault, temporal, panll, groove, bridge, etc.) +| `src/main.rs` | CLI entry: 38 subcommands (assail, assault, assay, assimilate, aggregate, temporal, panll, groove, bridge, etc.) | `src/lib.rs` | Library API exposing all analysis engines | `src/assail/` | Static analysis (49 languages, 25 canonical PA-codes / 26 enum variants; `PA001` ⇒ `UncheckedAllocation` + `PA001b` ⇒ `UnboundedAllocation` SARIF subvariants) | `src/assail/analyzer.rs` | Per-file language detection and pattern matching dispatcher diff --git a/README.adoc b/README.adoc index 91be0f9..b1e9533 100644 --- a/README.adoc +++ b/README.adoc @@ -161,7 +161,7 @@ It is designed to be **trustworthy**. == CLI & Automation -`panic-attack` is a Clap-powered CLI. Each subcommand inherits a shared set of knobs so the UX is consistent whether developers run `assail`, `attack`, `assault`, `temporal`, `panll`, `groove`, or another entry point. +`panic-attack` is a Clap-powered CLI. Each subcommand inherits a shared set of knobs so the UX is consistent whether developers run `assail`, `attack`, `assault`, `assay`, `aggregate`, `temporal`, `panll`, `groove`, or another entry point. === Global options @@ -205,7 +205,7 @@ Current state: **v2.5.0** * 897 passing tests (per `cargo test --release`, 4 ignored; 901 runnable per `cargo test -- --list`) across unit / property / e2e / aspect / integration tiers * v2.5.5 cohort landed 2026-06-02: context-aware FP suppression via `test_context` / `comment_marker` / `ffi_kind` / `jit_context` modules — `// panic-attack: accepted` inline markers, automatic PanicPath suppression in test-only code, build.zig/build.rs auto-accept, Cranelift/LLVM/Wasm/JS JIT detection. See `PROOF-PROGRAMME.md` for the formal soundness landscape * 0 warnings -* 25+ CLI subcommands +* 38 CLI subcommands * 49 supported languages (25 canonical weak-point categories PA001–PA025; `PA001b` SARIF subvariant collapses two allocation flavors under the same canonical rule) See link:ROADMAP.adoc[ROADMAP.adoc] for full capability breakdown and milestones. From 37e73ea632e5be7943675617984c4d9e113f2b4e Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 4 Jun 2026 17:15:39 +0000 Subject: [PATCH 4/4] chore(6a2): complete the 6-a2ml metadata set + ANCHOR Fills .machine_readable/6a2/ to the echidna/standards shape: - new AGENTIC.a2ml (panicbot / Hypatia / echidnabot agent roles), NEUROSYM.a2ml (miniKanren engine as the symbolic layer + proof obligations), PLAYBOOK.a2ml (build/test/scan runbooks + 3 deployment modes), ANCHOR.a2ml (clade=diagnostic) - META.a2ml + ECOSYSTEM.a2ml fleshed out from stubs (architecture decisions, practices, hospital-model ecosystem position) - STATE.a2ml untouched https://claude.ai/code/session_01K2TJLeQSyz4tpydZ18aRcb --- .machine_readable/6a2/AGENTIC.a2ml | 188 +++++++++++++++++++++++ .machine_readable/6a2/ANCHOR.a2ml | 22 +++ .machine_readable/6a2/ECOSYSTEM.a2ml | 39 +++++ .machine_readable/6a2/META.a2ml | 75 ++++++++++ .machine_readable/6a2/NEUROSYM.a2ml | 214 +++++++++++++++++++++++++++ .machine_readable/6a2/PLAYBOOK.a2ml | 118 +++++++++++++++ 6 files changed, 656 insertions(+) create mode 100644 .machine_readable/6a2/AGENTIC.a2ml create mode 100644 .machine_readable/6a2/ANCHOR.a2ml create mode 100644 .machine_readable/6a2/NEUROSYM.a2ml create mode 100644 .machine_readable/6a2/PLAYBOOK.a2ml diff --git a/.machine_readable/6a2/AGENTIC.a2ml b/.machine_readable/6a2/AGENTIC.a2ml new file mode 100644 index 0000000..9256191 --- /dev/null +++ b/.machine_readable/6a2/AGENTIC.a2ml @@ -0,0 +1,188 @@ +# SPDX-License-Identifier: MPL-2.0 +# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) +# +# AGENTIC.a2ml — AI agent operational gating and scan dispatch +# Defines safety constraints, entropy budgets, and explicit-intent gating for AI agents +# working in panic-attack (static-analysis scanner with 49 language analyzers, +# 25 weak-point categories, and three deployment modes). + +[metadata] +version = "0.1.0" +last-updated = "2026-06-04" +spec-version = "AGENTIC-FORMAT v0.1.0" +authority = "Jonathan D.A. Jewell (hyperpolymath)" +conformance = "AGENTIC-FORMAT-SPEC-compliant" + +# === AGENT ROLES === +# The concrete AI agents that interact with panic-attack and how. + +[agent-roles.panicbot] +kind = "gitbot-fleet verifier bot" +invokes = "panic-attack assail --output-format json" +consumes = "JSON AssailReport" +emits = "Findings PA001–PA025 (WeakPoints translated to fleet Findings)" +directives = ".machine_readable/bot_directives/panicbot.scm" +translator = "gitbot-fleet/bots/panicbot/src/translator.rs" +filters = "Drops items with WeakPoint.suppressed = true (kanren + v2.5.5 context suppression)" +notes = "Read-only consumer of scan output; never edits the repo under scan. Surfaces test_context + suppression-reason in fleet-side reports." + +[agent-roles.hypatia] +kind = "neurosymbolic rule engine" +consumes = "JSON AssailReport via Elixir rules" +processes = "Findings as neurosymbolic facts; env-var watcher in src/diagnostics.rs" +planned = "Ingestion of test_context / ffi_kind / jit_context metadata; ContextSuppressed bucket distinct from RuleSuppressed" +notes = "Downstream rule processor; does not mutate panic-attack state." + +[agent-roles.echidnabot] +kind = "proof-verification bot (planned)" +purpose = "Proof verification of scan claims" +status = "planned" +reciprocal = "panic-attack scans echidna's Rust + Idris2 ABI for banned patterns (echidna NEUROSYM.a2ml proof-obligations.abi-integrity)" + +# === GATING POLICIES === +# Per-operation-class rules: when may an action proceed? + +[gating-policies] +file-read = { mode = "auto", entropy-cost = 1, description = "Reading source files for analysis" } +file-write = { mode = "auto", entropy-cost = 10, description = "Creating/modifying source files in the panic-attack repo" } +scan-execute = { mode = "auto", entropy-cost = 15, description = "Running assail/assault on a target (read-only over the target tree)" } +batch-scan = { mode = "auto", entropy-cost = 40, description = "assemblyline batch scan across a repo directory (rayon + BLAKE3)" } +network-external = { mode = "require-confirmation", entropy-cost = 50, description = "OSV API queries (Patch Bridge, http feature) or GitHub issue creation (notify)" } +state-mutation = { mode = "require-confirmation", entropy-cost = 30, description = "Writing the mitigation registry, verisimdb hexads, or suppression classifications" } +suppression-edit = { mode = "require-confirmation", entropy-cost = 35, description = "Editing audits/assail-classifications.a2ml (flips findings to suppressed)" } +credentials-read = { mode = "require-confirmation", entropy-cost = 75, description = "Accessing an Ed25519 signing key for attestation sealing" } +credentials-write = { mode = "require-explicit-intent", entropy-cost = 100, description = "Storing or transmitting credentials" } + +# === ENTROPY BUDGETS === +# Session-scoped risk tracking: accumulated cost per operation type, four threshold levels. + +[entropy-budgets.operations] +session-max = 500 +threshold-low = 100 +threshold-medium = 200 +threshold-high = 350 +threshold-critical = 500 + +scan-run = 15 # Cost to run a single assail scan +batch-scan-run = 40 # Cost to run an assemblyline batch scan +osv-api-call = 50 # Cost to query the OSV CVE feed +github-issue-create = 50 # Cost for the notify pipeline to open a GitHub issue +file-write-source = 10 # Cost per source file modification +file-write-critical = 75 # Cost per critical file (assail/analyzer.rs, kanren/*, types.rs) +suppression-registry-edit = 35 # Cost to add/modify a suppression classification +verisim-push = 30 # Cost to push a hexad to verisimdb +attestation-seal = 60 # Cost to Ed25519-seal an attestation chain +emergency-override = 200 # Cost to override a gating decision + +[entropy-budgets.thresholds] +low = "Routine operations (read, scan, small writes, single-target assail)" +medium = "Elevated risk (batch scan, source writes, suppression-registry edits)" +high = "Sensitive operations (external API, GitHub issue creation, attestation sealing)" +critical = "Reserved for emergency overrides only" + +# === RISK THRESHOLDS === + +[risk-thresholds.low] +entropy-range = [0, 100] +operations = ["file-read", "scan-execute", "scan-run"] +user-confirmation-required = false +audit-log-level = "info" +override-allowed = true + +[risk-thresholds.medium] +entropy-range = [101, 200] +operations = ["file-write", "batch-scan", "suppression-edit"] +user-confirmation-required = false +audit-log-level = "warning" +override-allowed = true +override-requires = "explicit-present-intent" + +[risk-thresholds.high] +entropy-range = [201, 350] +operations = ["network-external", "github-issue-create", "credentials-read", "attestation-seal"] +user-confirmation-required = true +audit-log-level = "critical" +override-allowed = true +override-requires = ["explicit-present-intent", "META-permit"] + +[risk-thresholds.critical] +entropy-range = [351, 500] +operations = ["credentials-write", "emergency-override"] +user-confirmation-required = true +audit-log-level = "critical" +override-allowed = false +never-override-actions = ["credential-exfiltration", "finding-falsification", "self-suppression", "attestation-forgery"] + +# === OVERRIDE PATHS === + +[override-paths.bypass-1] +condition = "User types: 'yes, I intend to '" +scope = "Any low/medium/high entropy operation" +requires = ["explicit-present-intent-in-current-turn", "META-permit"] +audit = "Must record user's exact intent phrase + timestamp" +never-applies-to = ["credential-exfiltration", "finding-falsification", "self-suppression"] + +[override-paths.bypass-2] +condition = "PLAYBOOK.a2ml explicitly schedules with user's prior written intent" +scope = "Pre-authorized scheduled operations (e.g. nightly Mass-panic batch scan)" +requires = ["META-permit", "PLAYBOOK entry with timestamp"] +audit = "Reference PLAYBOOK by section + date; log override use" +never-applies-to = ["credential-exfiltration", "finding-falsification", "self-suppression"] + +[override-paths.never-override] +actions = [ + "credential-exfiltration (storing secrets in clear text, sending creds to untrusted service)", + "finding-falsification (suppressing a real WeakPoint without a reviewable companion classification, or silencing analyzer errors)", + "self-suppression (editing source under scan to hide a finding instead of recording an audited classification in the separate registry)", + "attestation-forgery (sealing an attestation chain that does not bind the actual scan inputs)", + "corpus-skip-abuse (relocating real source under external_corpora/ / third_party/ / corpus/ to dodge scanning)" +] +reason = "These actions irreversibly compromise panic-attack's diagnostic trust model. No override path exists." + +# === DECISION RECORDING === + +[decision-recording] +enabled = true +backend = "attestation evidence chain (src/attestation/) + optional verisimdb hexads" +columns = [ + "timestamp (ISO 8601)", + "action_type (scan-execute, file-write, osv-api-call, suppression-edit, etc.)", + "target (path scanned or file written)", + "gate_result (allowed | denied)", + "entropy_cost (numeric)", + "entropy_after (session running total)", + "risk_threshold_applied (low | medium | high | critical)", + "user_intent_type (explicit-present | inferred-from-memory | none)", + "override_used (true | false)", + "audit_note (freetext; mandatory for denied/overridden actions)" +] +retention = "Bound to the attestation envelope; persisted to verisimdb where Mass-panic is active" + +# === AGENT PERMISSIONS & CONSTRAINTS === + +[agent-permissions] +can-edit-source = true +can-edit-tests = true +can-edit-docs = true +can-edit-config = true +can-create-files = true +can-run-scans = true +can-edit-suppression-registry = "with-confirmation" +can-push-verisimdb = "with-confirmation" + +[agent-constraints] +never-use = [ + "hardcoded secrets or credentials", + "AGPL license (use MPL-2.0)", + "state files in repository root (use .machine_readable/ only)", + "self-suppressing source edits (record audits in the separate classification registry instead)", + "compiler warnings in release or test builds (zero-warning policy)", + ".unwrap() in test harness code (return Result and use ?; production .unwrap() held ≤50)" +] +always-do = [ + "Check entropy budget before high-cost operations (network, GitHub issues, sealing)", + "Keep suppressions in audits/assail-classifications.a2ml — a reviewable companion change, never a single-side source edit", + "Run cargo test (--features http for bridge) before commit", + "Dogfood: scan panic-attack's own source after analyzer changes", + "Honour SPDX MPL-2.0 headers on every new file" +] diff --git a/.machine_readable/6a2/ANCHOR.a2ml b/.machine_readable/6a2/ANCHOR.a2ml new file mode 100644 index 0000000..20e8a0f --- /dev/null +++ b/.machine_readable/6a2/ANCHOR.a2ml @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: MPL-2.0 +# ⚓ ANCHOR: panic-attack +# Canonical authority and project recalibration trigger for panic-attack. +# Template from: https://github.com/hyperpolymath/standards +identity: "org.hyperpolymath.panic-attack.anchor" + +id: "org.hyperpolymath.panic-attack" +version: "1.0.0" +clade: "diagnostic" +status: "active" + +# SSG Configuration (Unified boj-server build) +ssg: + engine: "casket" + output_dir: "public" + boj_trigger: true + cartridge: "ssg-mcp" + +# Relationships +parents: + - "org.hyperpolymath.boj-server" + - "org.hyperpolymath.ambientops" diff --git a/.machine_readable/6a2/ECOSYSTEM.a2ml b/.machine_readable/6a2/ECOSYSTEM.a2ml index bab5fc1..4b3ac32 100644 --- a/.machine_readable/6a2/ECOSYSTEM.a2ml +++ b/.machine_readable/6a2/ECOSYSTEM.a2ml @@ -3,8 +3,47 @@ # Converted from ECOSYSTEM.scm on 2026-03-15 [metadata] +version = "0.2.0" +last-updated = "2026-06-04" project = "panic-attack" ecosystem = "hyperpolymath" +[project] +name = "Panic Attack" +full-name = "Panic Attack — static analysis and bug-signature detection tool" +purpose = """Static analysis and bug-signature detection. Scans source code for weak +points (unwrap/expect, unsafe blocks, panic sites, error-handling gaps, command +injection, unsafe deserialization, FFI boundaries, atom exhaustion, proof drift, +crypto misuse, supply-chain gaps, and more) across 49 language analyzers, emitting +25 weak-point categories (PA001–PA025). A miniKanren logic engine adds taint +analysis and cross-language vulnerability-chain reasoning.""" +role = "diagnostic-tool" + [position] +tier = "diagnostic" type = "component" +hospital-model-role = """Diagnostic tool for software health within the AmbientOps +hospital model. Loosely affiliated; sits alongside the Operating Room as a +software-health diagnostic, while hardware-crash-team is the hardware-health +sibling. Independent top-level repo that feeds findings into the hospital's +Records system via verisimdb.""" + +[related-projects] +projects = [ + { name = "verisimdb", relationship = "active-integration", notes = "Scan results persisted as hexads (document + semantic modalities). File I/O works; HTTP push via Chapel verisim-push subcommand under the http feature; full REST API integration planned." }, + { name = "hypatia", relationship = "active-consumer", notes = "Neurosymbolic rule engine consumes JSON AssailReport (Elixir rules) and processes findings. Env-var watcher in diagnostics.rs. Planned: ingestion of test_context/ffi_kind/jit_context metadata." }, + { name = "panll", relationship = "active-integration", notes = "Event-chain export for three-panel visualisation via the panll subcommand. Two dedicated panels: panic-attack (single-repo) and Mass Panic (assemblyline batch GUI)." }, + { name = "assemblyline", relationship = "core-subsystem", notes = "Batch scanning of repo directories with rayon parallelism and BLAKE3 fingerprinting; the engine of Mass-panic mode. Feeds the notify pipeline." }, + { name = "gitbot-fleet (panicbot)", relationship = "active-consumer", notes = "panicbot is the gitbot-fleet verifier bot. Invokes panic-attack assail --output-format json, translates WeakPoints to Findings PA001–PA025. Directives at .machine_readable/bot_directives/panicbot.scm; translator at gitbot-fleet/bots/panicbot/src/translator.rs." }, + { name = "echidna / echidnabot", relationship = "planned-integration", notes = "echidnabot will perform proof verification of scan claims (planned). echidna's own NEUROSYM.a2ml references panic-attack as its code-only banned-pattern scanner; the two are reciprocal — panic-attack scans echidna's Rust + Idris2 ABI." }, + { name = "hardware-crash-team", relationship = "sibling", notes = "Sibling diagnostic tool in the hospital model: hardware diagnostics vs panic-attack's software analysis." }, + { name = "ambientops", relationship = "parent-context", notes = "The hospital-model umbrella. See ambientops/.claude/CLAUDE.md for the overview. panic-attack is loosely affiliated, not a hard dependency." }, +] + +[integration-surface] +output-formats = ["text", "json"] +panicbot-invocation = "panic-attack assail --output-format json" +finding-codes = "PA001–PA025" +verisim-push = "panic-attack verisim-push --url --retry --fallback-dir (http feature)" +panll-export = "panic-attack panll" +attestation = "Optional Ed25519-signed intent→evidence→seal chain wrapped in an A2ML envelope" diff --git a/.machine_readable/6a2/META.a2ml b/.machine_readable/6a2/META.a2ml index c08d1fc..2373d54 100644 --- a/.machine_readable/6a2/META.a2ml +++ b/.machine_readable/6a2/META.a2ml @@ -7,3 +7,78 @@ project = "panic-attack" author = "Jonathan D.A. Jewell " license = "MPL-2.0" standard = "RSR 2026" +version = "2.5.5" +last-updated = "2026-06-04" +status = "active" + +[project-context] +name = "panic-attack" +full-name = "Panic Attack — static analysis and bug-signature detection tool" +completion-percentage = 99 +phase = "v2.5.5 — 25 detection categories + context-aware FP suppression + Chapel→VeriSimDB HTTP push" +notes = """ +Static analysis engine that scans source across 49 language analyzers for +25 weak-point categories (PA001–PA025). Renamed 2026-02-08: binary +panic-attacker→panic-attack, subcommand xray→assail, module src/xray→src/assail, +type XRayReport→AssailReport. Three self-contained deployment modes: +Standalone, Panicbot, Mass-panic. See .claude/CLAUDE.md for the full surface. +""" + +[dependencies] +primary = ["Rust"] +secondary = ["Chapel", "Idris2"] +build-features = ["http"] +notes = "Single-binary core has zero runtime deps (Standalone mode). The http feature enables Patch Bridge (OSV API). Chapel is planned for distributed Mass-panic orchestration. Idris2 ABI proofs (PatternCompleteness.idr, Stripping.idr) back the detection surface." + +# ───────────────────────────────────────────────────────────────────────────── +# Architecture decisions +# ───────────────────────────────────────────────────────────────────────────── + +[architecture.per-file-language-detection] +decision = "Each file is analyzed with its own language-specific pattern set, not a single project-wide language guess" +rationale = "Polyglot repositories are the norm; a Rust crate with embedded Zig FFI and shell build scripts needs each file matched to the right detector" +consequence = "49 language analyzers in src/assail/analyzer.rs; corpus directories (external_corpora/, third_party/, corpus/) are skipped to avoid scanning vendored code" + +[architecture.minikanren-logic-engine] +decision = "A miniKanren-inspired relational logic engine (src/kanren/) layers over the syntactic analyzers" +rationale = "Taint flows, cross-language vulnerability chains, and false-positive suppression are relational reasoning problems, not regex problems" +consequence = "TaintAnalyzer (source→sink), CrossLangAnalyzer (FFI/NIF/Port/subprocess boundaries), SearchStrategy (risk-weighted prioritisation), forward chaining + backward queries over a FactDB" + +[architecture.non-gameable-suppression] +decision = "Audited findings are flipped to suppressed=true only via a project-local registry file held SEPARATELY from the source under scan" +rationale = "If a source edit could self-suppress, adding a new unsafe block would be invisible; keeping the registry in a companion file makes every suppression a reviewable change" +consequence = "audits/assail-classifications.a2ml (preferred) or .panic-attack-classifications.a2ml (fallback); applied by analyze()/analyze_verbose() after the kanren structural pass. Reference: hyperpolymath/007" + +[architecture.context-aware-fp-suppression] +decision = "v2.5.5 adds content-aware classifiers (test_context, comment_marker, ffi_kind, jit_context) that run after the kanren rules" +rationale = "A PanicPath in a test, an unsafe block behind an audited FFI build script, or a transmute inside a JIT compiler are legitimate residuals that should not be reported as defects" +consequence = "apply_v255_context_suppression() flips test-only/doc PanicPath and BuildSystem/TestMock UnsafeFFI; inline `// panic-attack: accepted` markers honoured string-literal-aware" + +[architecture.three-phase-attestation] +decision = "Scan runs can be wrapped in a cryptographic attestation chain: intent → evidence → seal" +rationale = "Pipeline consumers need to bind a report to the exact inputs and commitments that produced it, optionally Ed25519-signed" +consequence = "src/attestation/ (intent.rs, evidence.rs rolling hash, seal.rs, chain.rs, envelope.rs A2ML wrapper)" + +[architecture.deployment-modes] +decision = "Three self-contained deployment modes share one binary; none requires the others" +rationale = "The same engine must run air-gapped on a USB stick, inside CI as a verifier bot, and at org scale across a repo directory" +consequence = "Standalone (zero-dep single binary), Panicbot (JSON for gitbot-fleet), Mass-panic (assemblyline rayon batch + BLAKE3 incremental + verisimdb hexads)" + +# ───────────────────────────────────────────────────────────────────────────── +# Development practices +# ───────────────────────────────────────────────────────────────────────────── + +[practices] +error-handling = "anyhow::Result throughout; production .unwrap() held to ≤50 and static-regex OnceLock closures use .expect()" +serialization = "serde derive on all public types for machine-readable JSON output" +warnings-policy = "Zero compiler warnings in release and test builds" +spdx = "MPL-2.0 SPDX header on every file" +panic-free-tests = "All 11 test files return Result and use ? (Clade A panic-free); 3 canonical patterns contributed to panic-free-tests-and-benches v1.2.0" +dogfooding = "Self-scan E2E test scans panic-attack's own source as a representative target" + +[readiness-tests] +location = "tests/readiness.rs" +grade-d = "Alpha — component runs without crashing on valid input" +grade-c = "Beta — component produces correct output on representative input" +grade-b = "RC — component handles edge cases and multiple input types" +runner = "just readiness / just readiness-summary" diff --git a/.machine_readable/6a2/NEUROSYM.a2ml b/.machine_readable/6a2/NEUROSYM.a2ml new file mode 100644 index 0000000..4d5bf16 --- /dev/null +++ b/.machine_readable/6a2/NEUROSYM.a2ml @@ -0,0 +1,214 @@ +# SPDX-License-Identifier: MPL-2.0 +# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) +# +# NEUROSYM.a2ml — Neurosymbolic semantics for static-analysis operations +# Formal definitions of operation semantics, proof obligations, and composition rules +# for the panic-attack miniKanren logic engine layered over 49 language analyzers. + +[metadata] +version = "0.1.0" +last-updated = "2026-06-04" +spec-version = "NEUROSYM-FORMAT v0.1.0" +authority = "Jonathan D.A. Jewell (hyperpolymath)" +conformance = "NEUROSYM-FORMAT-SPEC-compliant" + +# === ARCHITECTURE === +# Symbolic reasoning layer over the syntactic analyzers. + +[architecture] +neural-substrate = "Not a learned model — the 49 per-file language analyzers (src/assail/analyzer.rs + patterns.rs) provide the syntactic 'recognition' layer that surfaces candidate WeakPoints" +symbolic-engine = "miniKanren-inspired relational logic engine (src/kanren/, v2.0.0) reasons over candidates: Term, Substitution, unification, FactDB, forward chaining" +downstream-reasoner = "Hypatia neurosymbolic rule engine consumes the JSON AssailReport and applies Elixir rules" +principle = "Recognition (analyzers) is heuristic and may over-report; relational reasoning (kanren) refines, links, and suppresses; the registry records honest residuals" + +# === OPERATION DEFINITIONS === +# For every core operation: forward semantics, inverse, claim-type, entropy-contribution, pre/postconditions. + +[operation-definitions.analyze] +forward-semantics = "Run per-file language detection; apply that language's attack patterns; emit candidate WeakPoints (PA001–PA025) into an AssailReport" +inverse = "Discard the report; re-scan from clean source (always possible — analysis is pure over file contents)" +claim-type = "candidate (raw analyzer hits before kanren refinement and suppression)" +entropy-contribution = 15 +preconditions = [ + "target path readable", + "file language detectable (Latin-1 fallback for non-UTF-8)", + "path not under external_corpora/ / third_party/ / corpus/ (skipped)" +] +postconditions = [ + "AssailReport populated with WeakPoints carrying category, file, line, severity", + "each WeakPoint.suppressed = false (pending suppression passes)" +] + +[operation-definitions.taint-analysis] +forward-semantics = "TaintAnalyzer tracks data flow from sources (user input, network, deserialization) to sinks (eval, shell commands, SQL queries) via relational source→sink resolution" +inverse = "Drop derived taint facts; the underlying candidate WeakPoints remain" +claim-type = "derived (a taint chain is inferred, not directly observed)" +entropy-contribution = 25 +preconditions = [ + "FactDB seeded with source and sink facts from the analyzers", + "unification available over Term" +] +postconditions = [ + "source→sink chains materialised as new facts", + "CommandInjection / UnsafeDeserialization candidates corroborated or refuted" +] + +[operation-definitions.crosslang-reasoning] +forward-semantics = "CrossLangAnalyzer detects vulnerability chains crossing FFI / NIF / Port / subprocess boundaries between language families" +inverse = "Drop cross-language edges; per-language facts persist" +claim-type = "derived (boundary-crossing chain inferred from both sides)" +entropy-contribution = 30 +preconditions = [ + "facts present on both sides of a boundary (e.g. Rust unsafe extern + C callee)", + "boundary kind recognised (FFI/NIF/Port/subprocess)" +] +postconditions = [ + "UnsafeFFI chains linked across files/languages", + "crosslang flag available to the query DSL" +] + +[operation-definitions.forward-chaining] +forward-semantics = "Apply detection rules to existing facts to derive new vulnerability facts until fixpoint" +inverse = "Retract derived facts; base facts unchanged" +claim-type = "derived (rule consequence)" +entropy-contribution = 20 +preconditions = ["FactDB non-empty", "rule set loaded (src/signatures/rules.rs)"] +postconditions = ["closure of derivable facts computed", "bug signatures (use-after-free, deadlock, etc.) surfaced"] + +[operation-definitions.backward-query] +forward-semantics = "Given a target vulnerability type, find which files could cause it (relational backward search)" +inverse = "None needed — query is read-only over the FactDB" +claim-type = "derived (existential witness search)" +entropy-contribution = 10 +preconditions = ["FactDB populated", "goal vulnerability type well-formed"] +postconditions = ["witness file set returned (possibly empty)"] + +[operation-definitions.apply-suppression] +forward-semantics = "Run the kanren structural-suppression pass over candidate WeakPoints; flip structurally-explained findings to suppressed = true" +inverse = "Re-run analyze from source to recover the unsuppressed candidate set" +claim-type = "refined (structural suppression is sound w.r.t. the rule set, not absolute ground truth)" +entropy-contribution = 15 +preconditions = ["AssailReport populated by analyze", "suppression rules available"] +postconditions = [ + "structurally-explained candidates marked suppressed", + "apply_user_classifications runs next (registry overlay)", + "apply_v255_context_suppression runs after (test_context / comment_marker / ffi_kind / jit_context)" +] + +[operation-definitions.user-classification-overlay] +forward-semantics = "Load audits/assail-classifications.a2ml (or .panic-attack-classifications.a2ml fallback); flip matching audited findings to suppressed = true" +inverse = "Remove the registry overlay; suppression reverts to the kanren structural result" +claim-type = "audited (a human audit is recorded in a file SEPARATE from the source under scan)" +entropy-contribution = 5 +preconditions = [ + "registry file present and parseable (A2ML S-expression)", + "each classification names file + category + audit reference + rationale" +] +postconditions = [ + "audited residuals suppressed with a reviewable provenance", + "self-suppression impossible: a new unsafe block cannot hide itself without a companion registry edit" +] + +# === COMPOSITION RULES === + +[composition-rules.scan-pipeline] +pattern = "analyze ; apply_suppression ; apply_user_classifications ; apply_v255_context_suppression" +entropy-behavior = "sum (each pass contributes independently)" +claim-propagation = "monotone-refining (each pass can only suppress, never invent, a finding)" +example = "analyze() / analyze_verbose() full pipeline" +semantics = "Candidate set is produced once, then narrowed by successive sound suppression passes" + +[composition-rules.relational-derivation] +pattern = "taint-analysis || crosslang-reasoning || forward-chaining (over a shared FactDB)" +entropy-behavior = "sum (independent derivations accumulate facts)" +claim-propagation = "additive (derived facts corroborate candidates; none are removed)" +example = "kanren engine deriving CommandInjection + UnsafeFFI + bug signatures" +semantics = "All derivations read the same FactDB; results union into the report" + +[composition-rules.suppression-precedence] +pattern = "IF user-classification matches THEN audited-suppressed ELSE IF structural/context rule fires THEN rule-suppressed ELSE reported" +entropy-behavior = "no-cost (precedence is metadata, not execution)" +claim-propagation = "special (suppression status follows the highest-provenance match)" +example = "Registry audit beats structural suppression beats raw candidate" +semantics = "Audited classifications are the strongest suppression evidence because they are reviewable companion changes" + +# === PROOF OBLIGATIONS === + +[proof-obligations.suppression-non-gameable] +name = "A finding may only be suppressed via a change reviewable independently of the source under scan" +claim-type = "audited" +verification-method = "User-classification registry lives in audits/assail-classifications.a2ml, a separate file from the scanned source" +discharge-requirements = [ + "registry edit is a distinct, reviewable change from the source edit", + "each suppression names an audit reference (e.g. audits/audit-ffi-unsafe.md §1) + rationale" +] +failure-action = "report-finding (no valid companion classification ⇒ the WeakPoint is surfaced)" + +[proof-obligations.pattern-completeness] +name = "Every supported language × category pair has a defined, total detection arm" +claim-type = "verified" +verification-method = "Idris2 ABI proof src/abi/PatternCompleteness.idr (49 languages, 25 categories, all proven total)" +discharge-requirements = ["each (language, category) handled", "no partial match arms"] +failure-action = "abort-build (ABI type-check fails; idris2 totality not satisfied)" + +[proof-obligations.strip-idempotence] +name = "Comment-stripping used in detection is idempotent (strip(strip x) = strip x)" +claim-type = "verified (partial — Layer 1.0)" +verification-method = "Idris2 ABI proof src/abi/Stripping.idr (IsStrippedBody shape lemma + base cases Qed-closed)" +discharge-requirements = ["stripBodyProducesStrippedShape proven", "line-comment base cases closed"] +failure-action = "flag-open-obligation (slash-slash inductive closure remains open; tracked in PROOF-PROGRAMME row 1)" + +[proof-obligations.corpus-exclusion] +name = "Vendored corpus directories are never scanned as first-party code" +claim-type = "verified" +verification-method = "Directory skip in analyzer for external_corpora/ / third_party/ / corpus/" +discharge-requirements = ["path prefix match short-circuits analysis before pattern application"] +failure-action = "abort-operation (a corpus-skip-abuse relocation would otherwise hide real source — see AGENTIC never-override)" + +# === TYPE SYSTEM === + +[type-system.base-types] +WeakPoint = "{ category: Category, file: String, line: Int, severity: Severity, suppressed: Bool, test_context: TestContext option, ffi_kind: FfiKind option, jit_context: JitContext option }" +Category = "PA001..PA025 (UnsafeCode, PanicPath, CommandInjection, UnsafeDeserialization, AtomExhaustion, UnsafeFFI, PathTraversal, HardcodedSecret, ProofDrift, CryptoMisuse, SupplyChain, InputBoundary, MutationGap, ...)" +AssailReport = "{ target: String, weak_points: WeakPoint list, summary: Counts }" +Term = "{ ast: LogicTerm, var-bindings: Substitution }" +Fact = "{ predicate: String, args: Term list }" +TaintChain = "{ source: Site, sink: Site, path: Site list }" +UserClassification = "{ file: String, category: Category, audit: String, rationale: String }" +TestContext = "{ Rust | Python | Go | JsTs | Julia | Zig | Elixir | Docs }" +FfiKind = "{ BuildSystem | RuntimeAbi | TestMock | Unknown }" +JitContext = "{ Cranelift | Llvm | Wasm | Javascript | None }" + +[type-system.operation-signatures] +analyze = "Path → AssailReport (always succeeds; emits candidate set)" +taint-analysis = "FactDB → TaintChain list (always succeeds; possibly empty)" +crosslang-reasoning = "FactDB → CrossLangChain list (always succeeds; possibly empty)" +backward-query = "Category × FactDB → File set (always succeeds; existential search)" +apply-suppression = "AssailReport → AssailReport (idempotent; monotone-refining)" +apply-user-classifications = "AssailReport × RegistryFile → AssailReport (idempotent overlay)" + +[type-system.operations-that-never-fail] +operations = [ + "analyze (Latin-1 fallback handles non-UTF-8; unreadable files reported, not fatal)", + "backward-query (empty witness set is a valid answer)", + "apply-suppression (no-op if no rule fires)" +] + +# === SEARCH STRATEGY === +# Risk-weighted file prioritisation (src/kanren/strategy.rs). + +[search-strategy] +auto-selection = "SearchStrategy auto-selects per project characteristics" +strategies = ["RiskWeighted", "BoundaryFirst", "LanguageFamily", "BreadthFirst", "DepthFirst"] +boundary-first-when = "FFI/NIF/Port boundaries dominate the project" +risk-weighted-when = "general-purpose default; prioritises files by accumulated risk facts" + +# === HYPATIA INTEGRATION === + +[hypatia-config] +scan-enabled = true +report-format = "JSON (AssailReport)" +consumer = "Hypatia neurosymbolic rule engine (Elixir rules)" +finding-codes = "PA001–PA025" +env-watcher = "src/diagnostics.rs (self-check for Hypatia / gitbot-fleet)" +planned = "Ingestion of test_context / ffi_kind / jit_context; ContextSuppressed bucket distinct from RuleSuppressed" diff --git a/.machine_readable/6a2/PLAYBOOK.a2ml b/.machine_readable/6a2/PLAYBOOK.a2ml new file mode 100644 index 0000000..0cbb30a --- /dev/null +++ b/.machine_readable/6a2/PLAYBOOK.a2ml @@ -0,0 +1,118 @@ +# SPDX-License-Identifier: MPL-2.0 +# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) +# +# PLAYBOOK.a2ml — Operational playbook +# Runbooks, build/test/scan procedures, deployment modes. + +[metadata] +version = "1.0.0" +last-updated = "2026-06-04" + +[build] +with-patch-bridge = "cargo build --release --features http" +without-patch-bridge = "cargo build --release" +notes = "The http feature enables Patch Bridge (OSV API queries) and the verisim-push subcommand; it needs network at runtime, not build time. Standalone mode uses the no-feature build." + +[test] +all = "cargo test --features http" +core = "cargo test" +readiness = "just readiness" +readiness-summary = "just readiness-summary" +notes = "Zero compiler warnings in release + test builds. Test harness is panic-free (Clade A): all test files return Result and use ?." + +[install] +command = "cp target/release/panic-attack ~/.asdf/installs/rust/nightly/bin/" + +[scan] +single-target = "panic-attack assail /path/to/repo" +single-target-json = "panic-attack assail /path/to/repo --output report.json" +single-target-verbose = "panic-attack assail /path/to/repo --verbose" +json-stream = "panic-attack assail --output-format json # panicbot invocation" +stress-test = "panic-attack assault /path/to/target # 6-axis stress testing" +batch = "assemblyline batch scan across a repo directory (Mass-panic; rayon + BLAKE3 incremental)" +delta = "panic-attack diff # only report changes since last scan" +query = "panic-attack query # heads: category, rule-id, severity, repo, file, pr-state, since, crosslang, diff, and, or, not" +panll-export = "panic-attack panll # event-chain export for three-panel visualisation" + +[patch-bridge] +triage = "panic-attack bridge triage /path/to/project" +triage-json = "panic-attack bridge triage /path/to/project --output report.json" +triage-register = "panic-attack bridge triage /path/to/project --register" +triage-offline = "panic-attack bridge triage /path/to/project --offline" +status = "panic-attack bridge status /path/to/project" +requires = "--features http at build time" +flow = "Parse Cargo.lock → query OSV API → reachability scan (.rs imports) → classify (Mitigable/Unmitigable/Informational) → JSON report" + +[verisim-push] +command = "panic-attack verisim-push --url --retry --fallback-dir" +requires = "http Cargo feature" +semantics = "local-writes-authoritative + push-additive; local hexad written first, push is additive" + +# ───────────────────────────────────────────────────────────────────────────── +# Deployment modes — three self-contained modes; none requires the others +# ───────────────────────────────────────────────────────────────────────────── + +[deployment-modes.standalone] +context = "USB / laptop / air-gapped" +binary = "Single binary, zero runtime deps" +usage = "assail / assault individual targets" +build = "cargo build --release" + +[deployment-modes.panicbot] +context = "gitbot-fleet / CI" +behaviour = "Automated JSON scanning; PA001–PA025 codes; bot directives" +invocation = "panic-attack assail --output-format json" +directives = ".machine_readable/bot_directives/panicbot.scm" +translator = "gitbot-fleet/bots/panicbot/src/translator.rs" +suppression = "panicbot drops WeakPoints with suppressed = true" + +[deployment-modes.mass-panic] +context = "assemblyline + verisimdb + Chapel" +behaviour = "Org-scale batch scanning with incremental BLAKE3 fingerprinting, hexad persistence, delta reporting, notifications" +parallelism = "rayon (per-repo); Chapel coforall planned for distributed multi-machine (v3.0.0)" +persistence = "verisimdb hexads (document + semantic modalities); per-finding hexads gated by PANIC_ATTACK_STORE_FINDING_HEXADS=1" +notify = "assemblyline → markdown summaries → GitHub issues (src/notify.rs)" + +# ───────────────────────────────────────────────────────────────────────────── +# Runbooks +# ───────────────────────────────────────────────────────────────────────────── + +[incident-response] +steps = [ + "Check .machine_readable/6a2/STATE.a2ml for current status", + "Review recent commits and CI results (chapel-ci.yml aggregator gate)", + "Re-run cargo test --features http to confirm the regression", + "Dogfood: run panic-attack assail on panic-attack's own source", + "If a false positive: record an audited classification in audits/assail-classifications.a2ml (never edit the source under scan to hide it)" +] + +[release-process] +steps = [ + "Update version in STATE.a2ml and META.a2ml", + "Run cargo test --features http (all suites) and confirm zero warnings", + "Run just readiness-summary", + "Tag the release with git tag vX.X.X", + "Push the tag to origin" +] + +[rollback-process] +steps = [ + "Identify the last stable tag", + "Revert to the stable tag with git revert", + "Re-run cargo test --features http to confirm green" +] + +[suppression-runbook] +when = "A reported WeakPoint is a legitimate, audited residual (e.g. genuine FFI unsafe)" +steps = [ + "Write the audit (e.g. audits/audit-ffi-unsafe.md §1) explaining why the residual is sound", + "Add a classification entry to audits/assail-classifications.a2ml naming file + category + audit reference + rationale", + "Re-run the scan; apply_user_classifications flips the finding to suppressed = true", + "Commit the registry edit as a reviewable companion change — never a single-side source edit" +] +reference = "hyperpolymath/007 audits/assail-classifications.a2ml" + +[monitoring] +self-check = "src/diagnostics.rs — self-check for Hypatia / gitbot-fleet (env-var watcher)" +warnings-policy = "Zero compiler warnings in release + test builds" +ci = ".github/workflows/chapel-ci.yml (MassPanic wiring + cross-rust contract checks + always-on aggregator gate)"