From 869be0a34a6058ea64c17be372799285558b6201 Mon Sep 17 00:00:00 2001 From: hyperpolymath <6759885+hyperpolymath@users.noreply.github.com> Date: Tue, 20 Oct 2026 00:57:56 +0000 Subject: [PATCH] Flatten stacked readiness, audit, and K9 fixes for main Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com> --- .github/workflows/actions.lock | 15 ++++--- .github/workflows/chapel-ci.yml | 34 +++++++------- .github/workflows/dogfood-gate.yml | 4 +- .github/workflows/scorecard.yml | 2 +- Cargo.lock | 21 +++++---- README.adoc | 4 +- generated/k9iser/cargo-manifest.k9 | 53 ---------------------- generated/k9iser/container-build.k9 | 58 ------------------------ k9iser.toml | 70 ----------------------------- src/aggregate/proven_tests.rs | 11 ++++- src/assail/analyzer.rs | 18 ++++---- src/assail/mod.rs | 3 +- src/bridge/mod.rs | 2 +- src/bridge/registry.rs | 2 +- src/diagnostics.rs | 6 +++ src/main.rs | 51 ++++++++++----------- src/report/gui.rs | 9 ++-- src/safe_path.rs | 19 +++++--- src/scan/cargodeny.rs | 9 ++-- src/storage/mod.rs | 33 ++++++++------ 20 files changed, 137 insertions(+), 287 deletions(-) delete mode 100644 generated/k9iser/cargo-manifest.k9 delete mode 100644 generated/k9iser/container-build.k9 delete mode 100644 k9iser.toml diff --git a/.github/workflows/actions.lock b/.github/workflows/actions.lock index 8f31bb1..681da1c 100644 --- a/.github/workflows/actions.lock +++ b/.github/workflows/actions.lock @@ -18,11 +18,11 @@ workflows: - 'actions/upload-pages-artifact@v5.0.0' - 'haskell-actions/setup@v2.12.1' '.github/workflows/chapel-ci.yml': - - 'actions/cache@v6.1.0' - - 'actions/checkout@v7.0.1' - - 'actions/download-artifact@v8.0.2' - - 'actions/upload-artifact@v7.0.2' - - 'dtolnay/rust-toolchain@v1' + - 'actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9' + - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1' + - 'actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333' + - 'actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9' + - 'dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8' '.github/workflows/codeql.yml': - 'actions/checkout@v7.0.1' - 'github/codeql-action@v4.39.0' @@ -114,6 +114,11 @@ dependencies: commit: 'sha1-368f82528645a54fb793d4d04e342629a3f51346' owner_id: 44036562 repo_id: 438112499 + 'actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333': + ref: 'v8.0.2' + commit: 'sha1-9000827ccba6bdab643e8b6fd33ac0654aef8333' + owner_id: 44036562 + repo_id: 192626254 'actions/download-artifact@v8.0.2': ref: 'v8.0.2' commit: 'sha1-9000827ccba6bdab643e8b6fd33ac0654aef8333' diff --git a/.github/workflows/chapel-ci.yml b/.github/workflows/chapel-ci.yml index 2a6eb88..3ae7720 100644 --- a/.github/workflows/chapel-ci.yml +++ b/.github/workflows/chapel-ci.yml @@ -72,7 +72,7 @@ jobs: outputs: relevant: ${{ steps.f.outputs.relevant }} steps: - - uses: actions/checkout@v7.0.1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 2 - id: f @@ -105,7 +105,7 @@ jobs: runs-on: ubuntu-22.04 timeout-minutes: 45 steps: - - uses: actions/checkout@v7.0.1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install Chapel ${{ env.CHAPEL_VERSION }} run: | set -euo pipefail @@ -128,7 +128,7 @@ jobs: runs-on: ubuntu-22.04 timeout-minutes: 45 steps: - - uses: actions/checkout@v7.0.1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install Chapel ${{ env.CHAPEL_VERSION }} run: | set -euo pipefail @@ -144,7 +144,7 @@ jobs: chpl src/MassPanic.chpl src/Protocol.chpl src/Imaging.chpl src/Temporal.chpl -o mass-panic chpl smoke/two_repo_smoke.chpl src/Protocol.chpl src/Imaging.chpl -o smoke/two_repo_smoke - name: Upload Chapel artefacts - uses: actions/upload-artifact@v7.0.2 + uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: chapel-binaries path: | @@ -159,7 +159,7 @@ jobs: runs-on: ubuntu-22.04 timeout-minutes: 45 steps: - - uses: actions/checkout@v7.0.1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install Chapel ${{ env.CHAPEL_VERSION }} run: | set -euo pipefail @@ -168,7 +168,7 @@ jobs: curl -fsSL --retry 3 -o /tmp/chapel.deb "${{ env.CHAPEL_DEB_URL }}" sudo apt-get install -y /tmp/chapel.deb - name: Download Chapel artefacts - uses: actions/download-artifact@v8.0.2 + uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: name: chapel-binaries path: chapel/ @@ -184,7 +184,7 @@ jobs: runs-on: ubuntu-22.04 timeout-minutes: 45 steps: - - uses: actions/checkout@v7.0.1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install Chapel ${{ env.CHAPEL_VERSION }} run: | set -euo pipefail @@ -193,7 +193,7 @@ jobs: curl -fsSL --retry 3 -o /tmp/chapel.deb "${{ env.CHAPEL_DEB_URL }}" sudo apt-get install -y /tmp/chapel.deb - name: Download Chapel artefacts - uses: actions/download-artifact@v8.0.2 + uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: name: chapel-binaries path: chapel/ @@ -221,11 +221,11 @@ jobs: runs-on: ubuntu-22.04 timeout-minutes: 45 steps: - - uses: actions/checkout@v7.0.1 - - uses: dtolnay/rust-toolchain@v1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8 # v1 with: toolchain: stable - - uses: actions/cache@v6.1.0 + - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: | ~/.cargo/registry @@ -244,11 +244,11 @@ jobs: runs-on: ubuntu-22.04 timeout-minutes: 45 steps: - - uses: actions/checkout@v7.0.1 - - uses: dtolnay/rust-toolchain@v1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8 # v1 with: toolchain: stable - - uses: actions/cache@v6.1.0 + - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: | ~/.cargo/registry @@ -263,7 +263,7 @@ jobs: curl -fsSL --retry 3 -o /tmp/chapel.deb "${{ env.CHAPEL_DEB_URL }}" sudo apt-get install -y /tmp/chapel.deb - name: Download Chapel artefacts - uses: actions/download-artifact@v8.0.2 + uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: name: chapel-binaries path: chapel/ @@ -283,7 +283,7 @@ jobs: env: CHPL_HOME: /opt/chapel-multilocale steps: - - uses: actions/checkout@v7.0.1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # Cache the entire built-from-source Chapel tree. Key is stable across # PRs as long as the version, conduit, launcher and cache-gen marker @@ -291,7 +291,7 @@ jobs: # restore is ~30s. - name: Cache multilocale Chapel ($CHPL_HOME) id: chapel-cache - uses: actions/cache@v6.1.0 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: ${{ env.CHAPEL_MULTILOCALE_HOME }} key: ${{ runner.os }}-chapel-multilocale-${{ env.CHAPEL_VERSION }}-gasnet-smp-${{ env.CHAPEL_MULTILOCALE_CACHE_GEN }} diff --git a/.github/workflows/dogfood-gate.yml b/.github/workflows/dogfood-gate.yml index 21d7548..d86b910 100644 --- a/.github/workflows/dogfood-gate.yml +++ b/.github/workflows/dogfood-gate.yml @@ -82,7 +82,7 @@ jobs: echo "k9_count=$COUNT" >> "$GITHUB_OUTPUT" echo "config_count=$CONFIG_COUNT" >> "$GITHUB_OUTPUT" if [ "$COUNT" -eq 0 ] && [ "$CONFIG_COUNT" -gt 0 ]; then - echo "::warning::Found $CONFIG_COUNT config files but no K9 contracts. Run k9iser to generate contracts." + echo "::warning::Found $CONFIG_COUNT config files but no K9 contracts. See standards 1-formats/k9/spec/K9-CONTRACT-SPEC.adoc." fi - name: Validate K9 contracts @@ -98,7 +98,7 @@ jobs: :warning: **No .a2ml/.deed manifest files found.** Every RSR-compliant repo should have a repo deed (`_chora.deed`) at its root. - Generate contracts with: `k9iser generate .` + Author them per hyperpolymath/standards `1-formats/k9/spec/K9-CONTRACT-SPEC.adoc` (D173). EOF else echo "## K9 Contract Validation" >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index a389a51..d1ca432 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -17,7 +17,7 @@ jobs: scorecard: uses: hyperpolymath/standards/.github/workflows/scorecard-reusable.yml@333b06a01db1874ffedb2de1bfabb195640927c7 permissions: - actions: read + actions: read # required by scorecard-reusable.yml to read workflow metadata contents: read security-events: write id-token: write diff --git a/Cargo.lock b/Cargo.lock index 430b8ab..5e0d690 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -886,7 +886,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -1179,7 +1179,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -1482,7 +1482,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -1502,11 +1502,10 @@ dependencies = [ [[package]] name = "event-listener" -version = "5.4.1" +version = "5.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2" dependencies = [ - "concurrent-queue", "parking", "pin-project-lite", ] @@ -2338,9 +2337,9 @@ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "memmap2" -version = "0.9.9" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490" +checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0" dependencies = [ "libc", ] @@ -3456,7 +3455,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -3898,7 +3897,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix 1.1.4", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -4728,7 +4727,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] diff --git a/README.adoc b/README.adoc index c7a3502..8509005 100644 --- a/README.adoc +++ b/README.adoc @@ -183,13 +183,13 @@ Consult `man/panic-attack.1` or `panic-attack help` for every command, and use ` === Automation readiness -`panic-attack diagnostics` validates AI manifests, VeriSimDB caches, Hypatia and gitbot-fleet watchers, and the docs PanLL expects (`docs/ambush-timeline.md`, `docs/panll-export.md`). `docs/patch-bridge-design.md` details how the patch-bridge subcommands plug into BoJ cartridges, CVE workflows, and the PanLL panel. +`panic-attack diagnostics` validates AI manifests, VeriSimDB caches, Hypatia and gitbot-fleet watchers, and the docs PanLL expects (`docs/ambush-timeline.adoc`, `docs/panll-export.adoc`). `docs/patch-bridge-design.adoc` details how the patch-bridge subcommands plug into BoJ cartridges, CVE workflows, and the PanLL panel. `just test-elixir` runs the Elixir harnesses that cover the CLI surface so documentation, help text, and automation flags stay aligned. == Integration & Groove -panic-attack can emit PanLL-friendly exports (event chains, system images, temporal diffs) per `docs/panll-export.md`, and it also advertises its static-analysis capability via the Gossamer groove protocol. Start the discovery endpoint with `panic-attack groove --port 7600` and check the JSON manifest by curling `http://localhost:7600/.well-known/groove` (the health endpoint sits at `/health`). +panic-attack can emit PanLL-friendly exports (event chains, system images, temporal diffs) per `docs/panll-export.adoc`, and it also advertises its static-analysis capability via the Gossamer groove protocol. Start the discovery endpoint with `panic-attack groove --port 7600` and check the JSON manifest by curling `http://localhost:7600/.well-known/groove` (the health endpoint sits at `/health`). The manifest follows the Idris-aligned semantics that live in `boj-server/src/interface/abi/Groove.idr` and the `gossamer` schema references. As a result, any groove-aware consumer—PanLL event-chain panels, the Burble voice platform, Hypatia, or other automation fabrics—sees the same capability vocabulary and can attach its own UI (Groove + Burble PanLL) confidently. diff --git a/generated/k9iser/cargo-manifest.k9 b/generated/k9iser/cargo-manifest.k9 deleted file mode 100644 index 0e313e0..0000000 --- a/generated/k9iser/cargo-manifest.k9 +++ /dev/null @@ -1,53 +0,0 @@ -K9! -# SPDX-License-Identifier: MPL-2.0 -# K9 contract for cargo-manifest; constraints from k9iser.toml. -# Hunt-tier signing is required before execution; this file contains no signature. - -{ - pedigree = { - schema_version = "1.0.0", - metadata = { - name = "cargo-manifest", - version = "1.0.0", - }, - security = { - leash = 'Hunt, - signature_required = true, - }, - }, - - config = { - source = "Cargo.toml", - format = "toml", - }, - - # Declarative requirements for the source configuration. - must = [ - "package.name == 'panic-attack'", - "package.edition == '2021'", - "package.rust-version is set", - "package.license-file is set", - "package.repository is non-empty", - "profile.release.lto is set", - "profile.release.codegen-units == 1", - "profile.release.strip == 'symbols'", - "profile.release.panic == 'unwind'", - "dependencies.blake3 is set", - "dependencies.sha2 is set", - "no dependency on md5", - "no dependency on sha1", - ], - - trust = { - "signed-by" = "ci-pipeline", - }, - - dust = { - remove = ["unused optional features"], - }, - - intend = { - "production-ready" = true, - "security-critical" = true, - }, -} diff --git a/generated/k9iser/container-build.k9 b/generated/k9iser/container-build.k9 deleted file mode 100644 index 99eb4e6..0000000 --- a/generated/k9iser/container-build.k9 +++ /dev/null @@ -1,58 +0,0 @@ -K9! -# SPDX-License-Identifier: MPL-2.0 -# K9 contract for container-build; constraints from k9iser.toml. -# Hunt-tier signing is required before execution; this file contains no signature. - -{ - pedigree = { - schema_version = "1.0.0", - metadata = { - name = "container-build", - version = "1.0.0", - }, - security = { - leash = 'Hunt, - signature_required = true, - }, - }, - - config = { - source = "stapeln.toml", - format = "toml", - }, - - # Declarative requirements for the source configuration. - must = [ - "metadata.license == 'MPL-2.0'", - "metadata.registry == 'ghcr.io/hyperpolymath'", - "build.runtime == 'podman'", - "layers.base.from starts-with 'cgr.dev/chainguard/'", - "layers.base.verify == true", - "layers.runtime.user == 'nonroot'", - "security.non-root == true", - "security.no-new-privileges == true", - "security.cap-drop contains 'ALL'", - "security.signing.algorithm == 'ML-DSA-87'", - "security.signing.provider == 'cerro-torre'", - "security.sbom.format == 'spdx-json'", - "security.sbom.include-deps == true", - "verify.vordr == true", - "verify.svalinn == true", - "verify.scan-on-build == true", - "verify.fail-on contains 'critical'", - "verify.fail-on contains 'high'", - ], - - trust = { - "signed-by" = "ci-pipeline", - }, - - dust = { - remove = ["deprecated layer definitions"], - }, - - intend = { - "production-ready" = true, - "supply-chain-verified" = true, - }, -} diff --git a/k9iser.toml b/k9iser.toml deleted file mode 100644 index 0e323cd..0000000 --- a/k9iser.toml +++ /dev/null @@ -1,70 +0,0 @@ -# k9iser manifest — wrap configs into self-validating K9 contracts -# SPDX-License-Identifier: MPL-2.0 -# -# panic-attack: universal static analysis and bug signature detection tool -# for 49 languages. Safety-tier "hunt" because this is a security tool whose -# own configuration must be above reproach. - -[project] -name = "panic-attack" -safety-tier = "hunt" # security tool — highest scrutiny - -# ── Cargo.toml — Rust package manifest ─────────────────────────────────────── - -[[configs]] -name = "cargo-manifest" -source = "Cargo.toml" -format = "toml" -must = [ - "package.name == 'panic-attack'", - "package.edition == '2021'", - "package.rust-version is set", # minimum MSRV declared - "package.license-file is set", # LICENSE present (MPL-2.0) - "package.repository is non-empty", - "profile.release.lto is set", # LTO enabled for security binary - "profile.release.codegen-units == 1", # single CGU for max optimisation - "profile.release.strip == 'symbols'", - "profile.release.panic == 'unwind'", # stack unwind required for proper error propagation - "dependencies.blake3 is set", # BLAKE3 hashing mandatory - "dependencies.sha2 is set", # SHA-2 hashing mandatory - "no dependency on md5", # MD5 banned for security tool - "no dependency on sha1", # SHA-1 banned for security tool -] -trust = ["signed-by: ci-pipeline"] -dust = ["remove: unused optional features"] -intend = ["production-ready", "security-critical"] - -# ── stapeln.toml — container build config ──────────────────────────────────── - -[[configs]] -name = "container-build" -source = "stapeln.toml" -format = "toml" -must = [ - "metadata.license == 'MPL-2.0'", - "metadata.registry == 'ghcr.io/hyperpolymath'", - "build.runtime == 'podman'", # Podman, never Docker - "layers.base.from starts-with 'cgr.dev/chainguard/'", # Chainguard base only - "layers.base.verify == true", - "layers.runtime.user == 'nonroot'", # never run as root - "security.non-root == true", - "security.no-new-privileges == true", - "security.cap-drop contains 'ALL'", - "security.signing.algorithm == 'ML-DSA-87'", # post-quantum signing - "security.signing.provider == 'cerro-torre'", - "security.sbom.format == 'spdx-json'", # SBOM mandatory - "security.sbom.include-deps == true", - "verify.vordr == true", - "verify.svalinn == true", - "verify.scan-on-build == true", - "verify.fail-on contains 'critical'", - "verify.fail-on contains 'high'", -] -trust = ["signed-by: ci-pipeline"] -dust = ["remove: deprecated layer definitions"] -intend = ["production-ready", "supply-chain-verified"] - -[validation] -strict = true -auto-fix = false -report-format = "text" diff --git a/src/aggregate/proven_tests.rs b/src/aggregate/proven_tests.rs index 60c0819..9799cad 100644 --- a/src/aggregate/proven_tests.rs +++ b/src/aggregate/proven_tests.rs @@ -119,7 +119,10 @@ impl ReportTier { impl FromStr for ProvenTestsReport { type Err = anyhow::Error; - /// Parse a run report from JSON text. + /// Parse a run report from JSON text, requiring schema version 1. + /// + /// # Errors + /// Returns JSON deserialisation errors or an error if `schema_version` is not 1. fn from_str(text: &str) -> Result { let report: ProvenTestsReport = serde_json::from_str(text).context("parsing proven-tests run report")?; @@ -134,7 +137,11 @@ impl FromStr for ProvenTestsReport { } impl ProvenTestsReport { - /// Parse a run report from a file path. + /// Read a UTF-8 JSON run report from `path` and parse it as schema version 1. + /// + /// # Errors + /// Returns file read errors (including invalid UTF-8), JSON deserialisation + /// errors or an error if `schema_version` is not 1. pub fn from_path(path: &Path) -> Result { let text = std::fs::read_to_string(path).with_context(|| format!("reading {}", path.display()))?; diff --git a/src/assail/analyzer.rs b/src/assail/analyzer.rs index 1b75185..4b86ad1 100644 --- a/src/assail/analyzer.rs +++ b/src/assail/analyzer.rs @@ -263,18 +263,16 @@ static RE_HARDCODED_SECRET: OnceLock = OnceLock::new(); /// Does not handle OCaml `(* *)` or Forth `\` — edge cases for later. static RE_TODO_COMMENT: OnceLock = OnceLock::new(); -/// Heuristic: is the `malloc` on `lines[idx]` NULL-checked within a short -/// window of following lines? +/// Heuristically recognise a null check for the allocation on `lines[idx]`. /// -/// An "UncheckedAllocation" finding should only fire when the result is *not* -/// guarded. This recognises the common C idioms (`if (p == NULL)`, -/// `if (!p)`, `if (p == nullptr)`) on the lvalue assigned from `malloc`. +/// Searches the allocation line and up to six following lines for `NULL` or +/// `nullptr` alongside the assigned identifier, or an `if` line containing `!` +/// immediately followed by that identifier. If no identifier can be extracted, +/// any `NULL` or `nullptr` substring in the window suffices. Substring matches +/// do not establish that the allocation is guarded. /// -/// Deliberately conservative to avoid false negatives in a security scanner: -/// a site is treated as checked only on a clear NULL-guard signal that -/// references the allocated variable (or, when no lvalue can be parsed, any -/// NULL/nullptr token in the window). Genuinely-unchecked allocations — the -/// real finding — are still reported. +/// # Panics +/// Panics if the zero-based `idx` is outside `lines`. fn c_alloc_result_is_null_checked(lines: &[&str], idx: usize) -> bool { let line = lines[idx]; // Parse the lvalue assigned from malloc: the last identifier left of '='. diff --git a/src/assail/mod.rs b/src/assail/mod.rs index 7ec19ad..aaf369e 100644 --- a/src/assail/mod.rs +++ b/src/assail/mod.rs @@ -411,7 +411,8 @@ pub fn apply_suppression(report: &mut AssailReport) { /// 3. UnsafeFFI finding where the file's [[ffi_kind]] is /// `BuildSystem` or `TestMock` (audit-accepted by default). /// -/// JIT context handling remains inline in analyzer.rs:1117..1129. +/// JIT context handling remains in the transmute classification block of +/// `Analyzer::analyze_rust` in `analyzer.rs`. /// /// Fills missing `wp.test_context` for findings with a known file path so /// downstream audit consumers can render it. Returns the number of diff --git a/src/bridge/mod.rs b/src/bridge/mod.rs index a39e3c9..66e5ee3 100644 --- a/src/bridge/mod.rs +++ b/src/bridge/mod.rs @@ -12,7 +12,7 @@ //! This module extends panic-attack with `bridge` subcommands. The core tool //! remains standalone — PanLL panel and BoJ cartridge are optional integrations. //! -//! Design document: docs/patch-bridge-design.md +//! Design document: docs/patch-bridge-design.adoc pub mod classify; pub mod intelligence; diff --git a/src/bridge/registry.rs b/src/bridge/registry.rs index 0e55eca..9749606 100644 --- a/src/bridge/registry.rs +++ b/src/bridge/registry.rs @@ -7,7 +7,7 @@ //! Phase 2 will add VeriSimDB hexad persistence and SCM format. //! //! Lifecycle: Applied → Active → Retiring → Retired -//! See docs/patch-bridge-design.md Section 8 for full lifecycle specification. +//! See docs/patch-bridge-design.adoc Section 8 for full lifecycle specification. use super::{AssessedCve, Classification}; use anyhow::{Context, Result}; diff --git a/src/diagnostics.rs b/src/diagnostics.rs index f89ec6b..5890014 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -11,6 +11,12 @@ const HYPATIA_ENV: &str = "HYPATIA_API_KEY"; const GITBOT_FLEET_ENV: &str = "GITBOT_FLEET_ENDPOINT"; const PANICBOT_DIRECTIVE_PATH: &str = ".machine_readable/bot_directives/panicbot.scm"; +/// Print readiness checks using `manifest`, paths relative to the current +/// working directory and the configured integration environment variables. +/// +/// # Errors +/// Returns an error after printing all results if any check has error severity. +/// Warnings alone do not cause failure. pub fn run_self_diagnostics(manifest: &Manifest) -> Result<()> { println!("panic-attack self-diagnostics"); diff --git a/src/main.rs b/src/main.rs index d09d67a..adede31 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,36 +7,34 @@ //! concurrency) and detecting bug signatures using logic programming techniques inspired by //! Mozart/Oz and Datalog. -mod a2ml; -mod abduct; -mod adjudicate; -mod aggregate; -mod ambush; -mod amuck; -mod assail; -mod assay; -mod assemblyline; -mod attack; -mod attestation; -mod axial; +use panic_attack::a2ml; +use panic_attack::abduct; +use panic_attack::adjudicate; +use panic_attack::aggregate; +use panic_attack::ambush; +use panic_attack::amuck; +use panic_attack::assail; +use panic_attack::assay; +use panic_attack::assemblyline; +use panic_attack::attack; +use panic_attack::attestation; +use panic_attack::axial; #[cfg(feature = "http")] -mod bridge; -mod campaign; +use panic_attack::bridge; +use panic_attack::campaign; mod diagnostics; mod groove; -mod i18n; -mod kanren; +use panic_attack::i18n; mod kin; -mod mass_panic; -mod notify; -mod panll; -mod query; -mod report; -mod signatures; -mod storage; -mod sweep_tracker; -mod test_context; -mod types; +use panic_attack::mass_panic; +use panic_attack::notify; +use panic_attack::panll; +use panic_attack::query; +use panic_attack::report; +use panic_attack::signatures; +use panic_attack::storage; +use panic_attack::sweep_tracker; +use panic_attack::types; extern crate walkdir; @@ -57,7 +55,6 @@ use anyhow::{anyhow, Context, Result}; use clap::{CommandFactory, Parser, Subcommand}; use clap_complete::{generate, Shell}; use clap_complete_nushell::Nushell; -use panic_attack::{comment_marker, ffi_kind}; use std::collections::HashMap; use std::fs::{self, File}; use std::io::{self, Read, Write}; diff --git a/src/report/gui.rs b/src/report/gui.rs index 014c3fa..549560b 100644 --- a/src/report/gui.rs +++ b/src/report/gui.rs @@ -120,8 +120,11 @@ impl ReportGui { } impl App for ReportGui { + /// Render the file loader, navigation and selected report tab. + /// A load-button click or Enter after the path field loses focus attempts + /// to read the specified JSON file, updating the viewer or its status message. fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut Frame) { - egui::Panel::top("header").show_inside(ui, |ui| { + egui::Panel::top("header").show(ui, |ui| { ui.horizontal(|ui| { ui.heading("panic-attack report"); ui.separator(); @@ -138,7 +141,7 @@ impl App for ReportGui { }); }); - egui::Panel::left("nav").show_inside(ui, |ui| { + egui::Panel::left("nav").show(ui, |ui| { ui.selectable_value(&mut self.tab, ReportTab::Summary, "Summary"); ui.selectable_value(&mut self.tab, ReportTab::Assail, "Assail"); ui.selectable_value(&mut self.tab, ReportTab::Matrix, "Matrix"); @@ -159,7 +162,7 @@ impl App for ReportGui { ui.selectable_value(&mut self.tab, ReportTab::Temporal, temporal_label); }); - egui::CentralPanel::default().show_inside(ui, |ui| match self.tab { + egui::CentralPanel::default().show(ui, |ui| match self.tab { ReportTab::Summary => self.render_summary(ui), ReportTab::Assail => self.render_assail(ui), ReportTab::Matrix => self.render_matrix(ui), diff --git a/src/safe_path.rs b/src/safe_path.rs index 12e008d..ec06273 100644 --- a/src/safe_path.rs +++ b/src/safe_path.rs @@ -100,17 +100,24 @@ pub fn sanitize_filename(filename: &str) -> String { } } +#[cfg(test)] +/// Returns true if `c` is a character `sanitize_filename` would keep. +fn is_safe_char(c: char) -> bool { + c.is_ascii_alphanumeric() || SAFE_PUNCT.contains(&c) +} + +#[cfg(test)] +/// Returns true if every character in `s` is safe. Used by the +/// idempotence + non-empty proptest invariants. +fn all_chars_safe(s: &str) -> bool { + s.chars().all(is_safe_char) +} + #[cfg(test)] mod tests { use super::*; use proptest::prelude::*; - /// Property-test predicate kept separate from the sanitizer implementation. - fn all_chars_safe(s: &str) -> bool { - s.chars() - .all(|c| c.is_ascii_alphanumeric() || SAFE_PUNCT.contains(&c)) - } - // ───────────────────────────────────────────────────────────────── // has_traversal — concrete cases // ───────────────────────────────────────────────────────────────── diff --git a/src/scan/cargodeny.rs b/src/scan/cargodeny.rs index 59ee87e..e002feb 100644 --- a/src/scan/cargodeny.rs +++ b/src/scan/cargodeny.rs @@ -182,10 +182,11 @@ pub fn run_cargo_deny(config: &CargoDenyConfig) -> Result> { .unwrap_or(WeakPointCategory::Advisory); let location = diagnostic.location.and_then(|loc| { - loc.file.map(|file| match loc.line { - Some(line) => format!("{}:{}", file, line), - None => file, - }) + if let (Some(ref file), Some(line)) = (&loc.file, loc.line) { + Some(format!("{}:{}", file, line)) + } else { + loc.file.clone() + } }); // Parse file and line from location string diff --git a/src/storage/mod.rs b/src/storage/mod.rs index 1c551ba..3680f52 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -229,7 +229,10 @@ pub struct MigrationSemantic { pub snapshot_label: Option, } -/// Build a VerisimDB hexad from an assault report +/// Build a VerisimDB hexad from an assault report. +/// +/// # Errors +/// Propagates errors from serialising the report into its JSON document. fn build_hexad(report: &AssaultReport) -> Result { let now = Utc::now(); let id = format!( @@ -375,6 +378,9 @@ pub fn persist_report( /// /// Unlike single-repo hexads which wrap an AssaultReport, assemblyline /// hexads capture the batch scan results across many repos. +/// +/// # Errors +/// Propagates errors from serialising the report into its JSON document. fn build_assemblyline_hexad( report: &crate::assemblyline::AssemblylineReport, ) -> Result { @@ -492,13 +498,13 @@ fn severity_label(severity: &crate::types::Severity) -> &'static str { } } -/// Build one hexad per `WeakPoint` across all repo results in an -/// assemblyline report (issue #33 S1). +/// Build one hexad per unsuppressed `WeakPoint` across repo results with an +/// AssailReport in an assemblyline report (issue #33 S1). /// /// Subject identity lives in `semantic.finding.finding_id`; each emitted -/// hexad's top-level `id` remains per-run-unique so two runs of the same -/// finding produce two distinct hexad files (the join key is the -/// `finding_id`, not the hexad id). +/// hexad's top-level `id` includes a millisecond timestamp and repo/finding +/// indices. IDs are distinct within a call but may repeat across calls in +/// the same millisecond; use `finding_id` as the join key. /// /// `run_id` is shared across every finding-hexad in this run and stamped /// into both `first_seen_run` and `last_seen_run` (S1 has no prior-run @@ -662,10 +668,10 @@ fn derive_crosslang_interactions_for_report( /// repo in an assemblyline report (issue #33 kanren-crosslang follow-up). /// /// Subject identity lives in `semantic.crosslang.interaction_id`; the -/// top-level hexad `id` is per-run-unique so two runs of the same -/// interaction produce two distinct hexad files (the join key is the -/// `interaction_id`, not the hexad id). Returns an empty `Vec` when the -/// report has no AssailReport-bearing results or no derivable interactions. +/// top-level hexad `id` includes a millisecond timestamp and repo/interaction +/// indices. IDs are distinct within a call but may repeat across calls in +/// the same millisecond; use `interaction_id` as the join key. Returns an empty +/// `Vec` when the report has no AssailReport-bearing results or no derivable interactions. pub fn build_crosslang_hexads( report: &crate::assemblyline::AssemblylineReport, ) -> Result> { @@ -772,9 +778,10 @@ const HEXAD_FILE_READ_LIMIT: u64 = 16 * 1024 * 1024; /// Build a campaign-state hexad for one lifecycle event (issue #33 S2). /// -/// Append-only: each call produces a fresh hexad with a unique id. The -/// `finding_id` is carried as the semantic subject so the newest hexad -/// per finding is the current state. +/// The id is derived from the current millisecond timestamp, so calls in the +/// same millisecond can produce the same id. This only constructs a hexad; +/// it does not write to storage. The `finding_id` is carried as the semantic +/// subject so stored events can be grouped by finding. pub fn build_campaign_hexad(semantic: CampaignSemantic) -> PanicAttackHexad { let now = Utc::now(); let hexad_id = format!(