From 826e9e1bddda1a9cebd7cfc30330903031bafa29 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 16:54:21 +0000 Subject: [PATCH 01/28] fix(ci): move scan-and-report's rust-toolchain pin to the current v1 commit 02cb101e was v1 when it was pinned; v1 now points at 683b588c and no tag names 02cb101e any more. Callers of this reusable (echidna security-scan) must lock it, and the estate repair chain (close-lock) records a leaf SHA only through a tag, so the old pin left them unlockable and failing at startup. The lock is regenerated in the next commit by the repair chain. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019a95kk88TZ1iegsBK3kox3 --- .github/workflows/scan-and-report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scan-and-report.yml b/.github/workflows/scan-and-report.yml index 5e0d74a..048e8bd 100644 --- a/.github/workflows/scan-and-report.yml +++ b/.github/workflows/scan-and-report.yml @@ -39,7 +39,7 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install Rust - uses: dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de # v1 + uses: dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8 # v1 with: toolchain: stable From f93d082c86fb3dcaa2b752422c4cd4ff38936516 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 16:58:24 +0000 Subject: [PATCH 02/28] fix(ci): regenerate actions.lock for scan-and-report and the standards pins Generated in CI with a real token by the estate repair chain (relock-sha-keys, complete-job-refs, close-lock, prune-stale; standards 333b06a0), never by hand: this session cannot reach the action repos through the API. check-lock-sync passed and the governance gate at 333b06a0 returned 0. Applied from the run's diff; the result matches its LOCK-SHA256 8bacccdbeedc6b8e4b799ecbfe6246e7ab09472a608cd729ed5bdcc383186c22. Beyond the scan-and-report rust-toolchain pin, the lock had drifted from the workflow YAML (checkout v7.0.1, cache v6.1.0 and others already in the workflows but locked at older versions) and had no hyperpolymath/standards records, which is why boj-build, casket-pages, chapel-ci and dogfood-gate died at startup on main. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019a95kk88TZ1iegsBK3kox3 --- .github/workflows/actions.lock | 185 ++++++++++++++++++++++++++------- 1 file changed, 148 insertions(+), 37 deletions(-) diff --git a/.github/workflows/actions.lock b/.github/workflows/actions.lock index 807b706..2ce62c6 100644 --- a/.github/workflows/actions.lock +++ b/.github/workflows/actions.lock @@ -4,74 +4,91 @@ version: 'v0.0.2' workflows: '.github/workflows/boj-build.yml': - - 'actions/checkout@v4.1.7' + - 'actions/checkout@v7.0.1' '.github/workflows/bridge-gate.yml': - - 'actions/checkout@v4.3.1' + - 'actions/checkout@v7.0.1' '.github/workflows/cargo-audit.yml': - - 'actions/checkout@v5.0.1' + - 'actions/checkout@v7.0.1' - 'dtolnay/rust-toolchain@v1' '.github/workflows/casket-pages.yml': - - 'actions/cache@v4.3.0' - - 'actions/checkout@v4.1.1' - - 'actions/configure-pages@v5.0.0' - - 'actions/deploy-pages@v4.0.5' - - 'actions/upload-pages-artifact@v3.0.1' - - 'haskell-actions/setup@v2.7.5' + - 'actions/cache@v6.1.0' + - 'actions/checkout@v7.0.1' + - 'actions/configure-pages@v6.0.0' + - 'actions/deploy-pages@v5.0.1' + - 'actions/upload-pages-artifact@v5.0.0' + - 'haskell-actions/setup@v2.12.1' '.github/workflows/chapel-ci.yml': - - 'actions/cache@v4.3.0' - - 'actions/checkout@v6.0.2' - - 'actions/download-artifact@v4.1.8' - - 'actions/upload-artifact@v4.6.2' + - 'actions/cache@v6.1.0' + - 'actions/checkout@v7.0.1' + - 'actions/download-artifact@v8.0.1' + - 'actions/upload-artifact@v7.0.1' - 'dtolnay/rust-toolchain@v1' '.github/workflows/codeql.yml': - 'actions/checkout@v7.0.1' - 'github/codeql-action@v4.38.2' '.github/workflows/coverage.yml': - - 'actions/checkout@v5.0.1' + - 'actions/checkout@v7.0.1' - 'dtolnay/rust-toolchain@v1' '.github/workflows/dependency-review.yml': - - 'actions/checkout@v5.0.1' - - 'actions/dependency-review-action@v4.3.4' + - 'actions/checkout@v7.0.1' + - 'actions/dependency-review-action@v5.0.0' '.github/workflows/dogfood-gate.yml': - - 'actions/checkout@v4.3.1' + - 'actions/checkout@v7.0.1' '.github/workflows/e2e.yml': - - 'actions/checkout@v4.3.1' - - 'actions/upload-artifact@v4.6.2' + - 'Swatinem/rust-cache@v2.9.2' + - 'actions/checkout@v7.0.1' + - 'actions/upload-artifact@v7.0.1' - 'dtolnay/rust-toolchain@v1' - - 'swatinem/rust-cache@v2.8.2' - '.github/workflows/governance.yml': [] - '.github/workflows/hypatia-scan.yml': [] + '.github/workflows/governance.yml': + - 'hyperpolymath/standards@fad242d35291de1898242d6737ba02b74a59a2f2' + '.github/workflows/hypatia-scan.yml': + - 'hyperpolymath/standards@84355587cb2a1f86e6882de83514a32db2646e7a' '.github/workflows/instant-sync.yml': - 'peter-evans/repository-dispatch@v4.0.1' '.github/workflows/label-triage.yml': [] '.github/workflows/labels.yml': [] - '.github/workflows/mirror.yml': [] + '.github/workflows/mirror.yml': + - 'hyperpolymath/standards@84355587cb2a1f86e6882de83514a32db2646e7a' '.github/workflows/pages.yml': - - 'actions/checkout@v4.4.0' - - 'actions/deploy-pages@v4.0.5' - - 'actions/upload-pages-artifact@v3.0.1' + - 'actions/checkout@v7.0.1' + - 'actions/deploy-pages@v5.0.1' + - 'actions/upload-pages-artifact@v5.0.0' '.github/workflows/push-email-notify.yml': - - 'hyperpolymath/smtp-notify-action@v0.2.0' + - 'hyperpolymath/smtp-notify-action@v0.5.0' '.github/workflows/release.yml': - - 'actions/checkout@v5.0.1' - - 'actions/download-artifact@v4.1.8' - - 'actions/upload-artifact@v4.6.2' + - 'Swatinem/rust-cache@v2.9.2' + - 'actions/checkout@v7.0.1' + - 'actions/download-artifact@v8.0.1' + - 'actions/upload-artifact@v7.0.1' - 'dtolnay/rust-toolchain@v1' - - 'softprops/action-gh-release@v2.5.0' - - 'swatinem/rust-cache@v2.8.2' - '.github/workflows/rust-ci.yml': [] + - 'slsa-framework/slsa-github-generator@f7dd8c54c2067bafc12ca7a55595d5ee9b75204a' + - 'softprops/action-gh-release@v3.0.3' + '.github/workflows/rust-ci.yml': + - 'hyperpolymath/standards@84355587cb2a1f86e6882de83514a32db2646e7a' '.github/workflows/scan-and-report.yml': - - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1' - - 'dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de' - 'Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6' - '.github/workflows/scorecard.yml': [] - '.github/workflows/secret-scanner.yml': [] + - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1' + - 'dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8' + '.github/workflows/scorecard.yml': + - 'hyperpolymath/standards@84355587cb2a1f86e6882de83514a32db2646e7a' + '.github/workflows/secret-scanner.yml': + - 'hyperpolymath/standards@84355587cb2a1f86e6882de83514a32db2646e7a' dependencies: + 'Swatinem/rust-cache@v2.9.2': + ref: 'v2.9.2' + commit: 'sha1-6323deb102c322ba6fcbdcafc7e3dddab59af2b6' + owner_id: 580492 + repo_id: 298565987 'actions/cache@v4.3.0': ref: 'v4.3.0' commit: 'sha1-0057852bfaa89a56745cba8c7296529d2fc39830' owner_id: 44036562 repo_id: 215566462 + 'actions/cache@v6.1.0': + ref: 'v6.1.0' + commit: 'sha1-55cc8345863c7cc4c66a329aec7e433d2d1c52a9' + owner_id: 44036562 + repo_id: 215566462 'actions/checkout@v4.1.1': ref: 'v4.1.1' commit: 'sha1-b4ffde65f46336ab88eb53be808477a3936bae11' @@ -112,21 +129,41 @@ dependencies: commit: 'sha1-983d7736d9b0ae728b81ab479565c72886d7745b' owner_id: 44036562 repo_id: 513659658 + 'actions/configure-pages@v6.0.0': + ref: 'v6.0.0' + commit: 'sha1-45bfe0192ca1faeb007ade9deae92b16b8254a0d' + owner_id: 44036562 + repo_id: 513659658 'actions/dependency-review-action@v4.3.4': ref: 'v4.3.4' commit: 'sha1-5a2ce3f5b92ee19cbb1541a4984c76d921601d7c' owner_id: 44036562 repo_id: 476372928 + 'actions/dependency-review-action@v5.0.0': + ref: 'v5.0.0' + commit: 'sha1-a1d282b36b6f3519aa1f3fc636f609c47dddb294' + owner_id: 44036562 + repo_id: 476372928 'actions/deploy-pages@v4.0.5': ref: 'v4.0.5' commit: 'sha1-d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e' owner_id: 44036562 repo_id: 438112499 + 'actions/deploy-pages@v5.0.1': + ref: 'v5.0.1' + commit: 'sha1-368f82528645a54fb793d4d04e342629a3f51346' + owner_id: 44036562 + repo_id: 438112499 'actions/download-artifact@v4.1.8': ref: 'v4.1.8' commit: 'sha1-fa0a91b85d4f404e444e00e005971372dc801d16' owner_id: 44036562 repo_id: 192626254 + 'actions/download-artifact@v8.0.1': + ref: 'v8.0.1' + commit: 'sha1-3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c' + owner_id: 44036562 + repo_id: 192626254 'actions/upload-artifact@v4': ref: 'v4' commit: 'sha1-ea165f8d65b6e75b540449e92b4886f43607fa02' @@ -137,6 +174,11 @@ dependencies: commit: 'sha1-ea165f8d65b6e75b540449e92b4886f43607fa02' owner_id: 44036562 repo_id: 192625955 + 'actions/upload-artifact@v7.0.1': + ref: 'v7.0.1' + commit: 'sha1-043fb46d1a93c77aae656e7c1c64a875d1fc6a0a' + owner_id: 44036562 + repo_id: 192625955 'actions/upload-pages-artifact@v3.0.1': ref: 'v3.0.1' commit: 'sha1-56afc609e74202658d3ffba0e8f6dda462b719fa' @@ -144,16 +186,36 @@ dependencies: repo_id: 496012378 uses: - 'actions/upload-artifact@v4' + 'actions/upload-pages-artifact@v5.0.0': + ref: 'v5.0.0' + commit: 'sha1-fc324d3547104276b827a68afc52ff2a11cc49c9' + owner_id: 44036562 + repo_id: 496012378 + 'dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8': + ref: 'v1' + commit: 'sha1-683b588c487a69d01f6c6ddf40f036ff682cc2c8' + owner_id: 1940490 + repo_id: 260749683 'dtolnay/rust-toolchain@v1': ref: 'v1' commit: 'sha1-02cb101ec7c40f2c49e1d9714d64511d8e1b74de' owner_id: 1940490 repo_id: 260749683 + 'github/codeql-action@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28': + ref: 'v4.37.8' + commit: 'sha1-db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28' + owner_id: 9919 + repo_id: 259445878 'github/codeql-action@v4.38.2': ref: 'v4.38.2' commit: 'sha1-2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2' owner_id: 9919 repo_id: 259445878 + 'haskell-actions/setup@v2.12.1': + ref: 'v2.12.1' + commit: 'sha1-0f8e8c99d88aeb3fbfd523f1ef2c6f762d10d64d' + owner_id: 75048950 + repo_id: 623796603 'haskell-actions/setup@v2.7.5': ref: 'v2.7.5' commit: 'sha1-ec49483bfc012387b227434aba94f59a6ecd0900' @@ -164,16 +226,65 @@ dependencies: commit: 'sha1-ede1191ef6ff3ac02c4f4d9efdf837ee517e11d7' owner_id: 6759885 repo_id: 1352485172 + 'hyperpolymath/smtp-notify-action@v0.5.0': + ref: 'v0.5.0' + commit: 'sha1-c1c9fa07992a02c1fd3d67a0dc1b08cccb852aef' + owner_id: 6759885 + repo_id: 1352485172 + 'hyperpolymath/standards@84355587cb2a1f86e6882de83514a32db2646e7a': + ref: '84355587cb2a1f86e6882de83514a32db2646e7a' + commit: 'sha1-84355587cb2a1f86e6882de83514a32db2646e7a' + owner_id: 6759885 + repo_id: 1116521501 + uses: + - 'Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6' + - 'actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9' + - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1' + - 'actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a' + - 'dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772' + - 'erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124' + - 'github/codeql-action@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28' + - 'ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc' + - 'webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555' + 'hyperpolymath/standards@fad242d35291de1898242d6737ba02b74a59a2f2': + ref: 'fad242d35291de1898242d6737ba02b74a59a2f2' + commit: 'sha1-fad242d35291de1898242d6737ba02b74a59a2f2' + owner_id: 6759885 + repo_id: 1116521501 + uses: + - 'actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9' + - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1' + - 'editorconfig-checker/action-editorconfig-checker@840e866d93b8e032123c23bac69dece044d4d84c' + - 'erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124' 'peter-evans/repository-dispatch@v4.0.1': ref: 'v4.0.1' commit: 'sha1-28959ce8df70de7be546dd1250a005dd32156697' owner_id: 18365890 repo_id: 220359305 + 'slsa-framework/slsa-github-generator@f7dd8c54c2067bafc12ca7a55595d5ee9b75204a': + ref: 'f7dd8c54c2067bafc12ca7a55595d5ee9b75204a' + commit: 'sha1-f7dd8c54c2067bafc12ca7a55595d5ee9b75204a' + owner_id: 80431187 + repo_id: 475074978 + uses: + - 'actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08' + - 'slsa-framework/slsa-github-generator@v2.1.0' + - 'softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda' + 'slsa-framework/slsa-github-generator@v2.1.0': + ref: 'v2.1.0' + commit: 'sha1-f7dd8c54c2067bafc12ca7a55595d5ee9b75204a' + owner_id: 80431187 + repo_id: 475074978 'softprops/action-gh-release@v2.5.0': ref: 'v2.5.0' commit: 'sha1-a06a81a03ee405af7f2048a818ed3f03bbf83c7b' owner_id: 2242 repo_id: 204253808 + 'softprops/action-gh-release@v3.0.3': + ref: 'v3.0.3' + commit: 'sha1-efb35369e0ad2afab669f228072c1b0d510eae64' + owner_id: 2242 + repo_id: 204253808 'swatinem/rust-cache@v2.8.2': ref: 'v2.8.2' commit: 'sha1-779680da715d629ac1d338a641029a2f4372abb5' From 39a177b6cdbbecccdf31d51087d5fd998b3c1d92 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 17:01:35 +0000 Subject: [PATCH 03/28] fix(ci): re-pin standards reusables to 333b06a0; reset the lock for a full regen The callers were on 84355587 and fad242d3. 84355587's rust-ci reusable pins dtolnay/rust-toolchain@6c977a6c ('stable' branch, no tag), which the repair chain cannot lock. 333b06a0 is current standards main. The scorecard job's own permissions block now grants actions: read, which the 333b06a0 reusable requests. actions.lock goes back to main's version so the next commit regenerates it from a clean base (the previous attempt left two spellings of Swatinem/rust-cache that gh actions-lock rejects). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019a95kk88TZ1iegsBK3kox3 --- .github/workflows/actions.lock | 185 ++++++--------------------- .github/workflows/governance.yml | 2 +- .github/workflows/hypatia-scan.yml | 2 +- .github/workflows/mirror.yml | 2 +- .github/workflows/rust-ci.yml | 2 +- .github/workflows/scorecard.yml | 3 +- .github/workflows/secret-scanner.yml | 2 +- 7 files changed, 44 insertions(+), 154 deletions(-) diff --git a/.github/workflows/actions.lock b/.github/workflows/actions.lock index 2ce62c6..807b706 100644 --- a/.github/workflows/actions.lock +++ b/.github/workflows/actions.lock @@ -4,91 +4,74 @@ version: 'v0.0.2' workflows: '.github/workflows/boj-build.yml': - - 'actions/checkout@v7.0.1' + - 'actions/checkout@v4.1.7' '.github/workflows/bridge-gate.yml': - - 'actions/checkout@v7.0.1' + - 'actions/checkout@v4.3.1' '.github/workflows/cargo-audit.yml': - - 'actions/checkout@v7.0.1' + - 'actions/checkout@v5.0.1' - 'dtolnay/rust-toolchain@v1' '.github/workflows/casket-pages.yml': - - 'actions/cache@v6.1.0' - - 'actions/checkout@v7.0.1' - - 'actions/configure-pages@v6.0.0' - - 'actions/deploy-pages@v5.0.1' - - 'actions/upload-pages-artifact@v5.0.0' - - 'haskell-actions/setup@v2.12.1' + - 'actions/cache@v4.3.0' + - 'actions/checkout@v4.1.1' + - 'actions/configure-pages@v5.0.0' + - 'actions/deploy-pages@v4.0.5' + - 'actions/upload-pages-artifact@v3.0.1' + - 'haskell-actions/setup@v2.7.5' '.github/workflows/chapel-ci.yml': - - 'actions/cache@v6.1.0' - - 'actions/checkout@v7.0.1' - - 'actions/download-artifact@v8.0.1' - - 'actions/upload-artifact@v7.0.1' + - 'actions/cache@v4.3.0' + - 'actions/checkout@v6.0.2' + - 'actions/download-artifact@v4.1.8' + - 'actions/upload-artifact@v4.6.2' - 'dtolnay/rust-toolchain@v1' '.github/workflows/codeql.yml': - 'actions/checkout@v7.0.1' - 'github/codeql-action@v4.38.2' '.github/workflows/coverage.yml': - - 'actions/checkout@v7.0.1' + - 'actions/checkout@v5.0.1' - 'dtolnay/rust-toolchain@v1' '.github/workflows/dependency-review.yml': - - 'actions/checkout@v7.0.1' - - 'actions/dependency-review-action@v5.0.0' + - 'actions/checkout@v5.0.1' + - 'actions/dependency-review-action@v4.3.4' '.github/workflows/dogfood-gate.yml': - - 'actions/checkout@v7.0.1' + - 'actions/checkout@v4.3.1' '.github/workflows/e2e.yml': - - 'Swatinem/rust-cache@v2.9.2' - - 'actions/checkout@v7.0.1' - - 'actions/upload-artifact@v7.0.1' + - 'actions/checkout@v4.3.1' + - 'actions/upload-artifact@v4.6.2' - 'dtolnay/rust-toolchain@v1' - '.github/workflows/governance.yml': - - 'hyperpolymath/standards@fad242d35291de1898242d6737ba02b74a59a2f2' - '.github/workflows/hypatia-scan.yml': - - 'hyperpolymath/standards@84355587cb2a1f86e6882de83514a32db2646e7a' + - 'swatinem/rust-cache@v2.8.2' + '.github/workflows/governance.yml': [] + '.github/workflows/hypatia-scan.yml': [] '.github/workflows/instant-sync.yml': - 'peter-evans/repository-dispatch@v4.0.1' '.github/workflows/label-triage.yml': [] '.github/workflows/labels.yml': [] - '.github/workflows/mirror.yml': - - 'hyperpolymath/standards@84355587cb2a1f86e6882de83514a32db2646e7a' + '.github/workflows/mirror.yml': [] '.github/workflows/pages.yml': - - 'actions/checkout@v7.0.1' - - 'actions/deploy-pages@v5.0.1' - - 'actions/upload-pages-artifact@v5.0.0' + - 'actions/checkout@v4.4.0' + - 'actions/deploy-pages@v4.0.5' + - 'actions/upload-pages-artifact@v3.0.1' '.github/workflows/push-email-notify.yml': - - 'hyperpolymath/smtp-notify-action@v0.5.0' + - 'hyperpolymath/smtp-notify-action@v0.2.0' '.github/workflows/release.yml': - - 'Swatinem/rust-cache@v2.9.2' - - 'actions/checkout@v7.0.1' - - 'actions/download-artifact@v8.0.1' - - 'actions/upload-artifact@v7.0.1' + - 'actions/checkout@v5.0.1' + - 'actions/download-artifact@v4.1.8' + - 'actions/upload-artifact@v4.6.2' - 'dtolnay/rust-toolchain@v1' - - 'slsa-framework/slsa-github-generator@f7dd8c54c2067bafc12ca7a55595d5ee9b75204a' - - 'softprops/action-gh-release@v3.0.3' - '.github/workflows/rust-ci.yml': - - 'hyperpolymath/standards@84355587cb2a1f86e6882de83514a32db2646e7a' + - 'softprops/action-gh-release@v2.5.0' + - 'swatinem/rust-cache@v2.8.2' + '.github/workflows/rust-ci.yml': [] '.github/workflows/scan-and-report.yml': - - 'Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6' - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1' - - 'dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8' - '.github/workflows/scorecard.yml': - - 'hyperpolymath/standards@84355587cb2a1f86e6882de83514a32db2646e7a' - '.github/workflows/secret-scanner.yml': - - 'hyperpolymath/standards@84355587cb2a1f86e6882de83514a32db2646e7a' + - 'dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de' + - 'Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6' + '.github/workflows/scorecard.yml': [] + '.github/workflows/secret-scanner.yml': [] dependencies: - 'Swatinem/rust-cache@v2.9.2': - ref: 'v2.9.2' - commit: 'sha1-6323deb102c322ba6fcbdcafc7e3dddab59af2b6' - owner_id: 580492 - repo_id: 298565987 'actions/cache@v4.3.0': ref: 'v4.3.0' commit: 'sha1-0057852bfaa89a56745cba8c7296529d2fc39830' owner_id: 44036562 repo_id: 215566462 - 'actions/cache@v6.1.0': - ref: 'v6.1.0' - commit: 'sha1-55cc8345863c7cc4c66a329aec7e433d2d1c52a9' - owner_id: 44036562 - repo_id: 215566462 'actions/checkout@v4.1.1': ref: 'v4.1.1' commit: 'sha1-b4ffde65f46336ab88eb53be808477a3936bae11' @@ -129,41 +112,21 @@ dependencies: commit: 'sha1-983d7736d9b0ae728b81ab479565c72886d7745b' owner_id: 44036562 repo_id: 513659658 - 'actions/configure-pages@v6.0.0': - ref: 'v6.0.0' - commit: 'sha1-45bfe0192ca1faeb007ade9deae92b16b8254a0d' - owner_id: 44036562 - repo_id: 513659658 'actions/dependency-review-action@v4.3.4': ref: 'v4.3.4' commit: 'sha1-5a2ce3f5b92ee19cbb1541a4984c76d921601d7c' owner_id: 44036562 repo_id: 476372928 - 'actions/dependency-review-action@v5.0.0': - ref: 'v5.0.0' - commit: 'sha1-a1d282b36b6f3519aa1f3fc636f609c47dddb294' - owner_id: 44036562 - repo_id: 476372928 'actions/deploy-pages@v4.0.5': ref: 'v4.0.5' commit: 'sha1-d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e' owner_id: 44036562 repo_id: 438112499 - 'actions/deploy-pages@v5.0.1': - ref: 'v5.0.1' - commit: 'sha1-368f82528645a54fb793d4d04e342629a3f51346' - owner_id: 44036562 - repo_id: 438112499 'actions/download-artifact@v4.1.8': ref: 'v4.1.8' commit: 'sha1-fa0a91b85d4f404e444e00e005971372dc801d16' owner_id: 44036562 repo_id: 192626254 - 'actions/download-artifact@v8.0.1': - ref: 'v8.0.1' - commit: 'sha1-3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c' - owner_id: 44036562 - repo_id: 192626254 'actions/upload-artifact@v4': ref: 'v4' commit: 'sha1-ea165f8d65b6e75b540449e92b4886f43607fa02' @@ -174,11 +137,6 @@ dependencies: commit: 'sha1-ea165f8d65b6e75b540449e92b4886f43607fa02' owner_id: 44036562 repo_id: 192625955 - 'actions/upload-artifact@v7.0.1': - ref: 'v7.0.1' - commit: 'sha1-043fb46d1a93c77aae656e7c1c64a875d1fc6a0a' - owner_id: 44036562 - repo_id: 192625955 'actions/upload-pages-artifact@v3.0.1': ref: 'v3.0.1' commit: 'sha1-56afc609e74202658d3ffba0e8f6dda462b719fa' @@ -186,36 +144,16 @@ dependencies: repo_id: 496012378 uses: - 'actions/upload-artifact@v4' - 'actions/upload-pages-artifact@v5.0.0': - ref: 'v5.0.0' - commit: 'sha1-fc324d3547104276b827a68afc52ff2a11cc49c9' - owner_id: 44036562 - repo_id: 496012378 - 'dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8': - ref: 'v1' - commit: 'sha1-683b588c487a69d01f6c6ddf40f036ff682cc2c8' - owner_id: 1940490 - repo_id: 260749683 'dtolnay/rust-toolchain@v1': ref: 'v1' commit: 'sha1-02cb101ec7c40f2c49e1d9714d64511d8e1b74de' owner_id: 1940490 repo_id: 260749683 - 'github/codeql-action@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28': - ref: 'v4.37.8' - commit: 'sha1-db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28' - owner_id: 9919 - repo_id: 259445878 'github/codeql-action@v4.38.2': ref: 'v4.38.2' commit: 'sha1-2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2' owner_id: 9919 repo_id: 259445878 - 'haskell-actions/setup@v2.12.1': - ref: 'v2.12.1' - commit: 'sha1-0f8e8c99d88aeb3fbfd523f1ef2c6f762d10d64d' - owner_id: 75048950 - repo_id: 623796603 'haskell-actions/setup@v2.7.5': ref: 'v2.7.5' commit: 'sha1-ec49483bfc012387b227434aba94f59a6ecd0900' @@ -226,65 +164,16 @@ dependencies: commit: 'sha1-ede1191ef6ff3ac02c4f4d9efdf837ee517e11d7' owner_id: 6759885 repo_id: 1352485172 - 'hyperpolymath/smtp-notify-action@v0.5.0': - ref: 'v0.5.0' - commit: 'sha1-c1c9fa07992a02c1fd3d67a0dc1b08cccb852aef' - owner_id: 6759885 - repo_id: 1352485172 - 'hyperpolymath/standards@84355587cb2a1f86e6882de83514a32db2646e7a': - ref: '84355587cb2a1f86e6882de83514a32db2646e7a' - commit: 'sha1-84355587cb2a1f86e6882de83514a32db2646e7a' - owner_id: 6759885 - repo_id: 1116521501 - uses: - - 'Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6' - - 'actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9' - - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1' - - 'actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a' - - 'dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772' - - 'erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124' - - 'github/codeql-action@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28' - - 'ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc' - - 'webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555' - 'hyperpolymath/standards@fad242d35291de1898242d6737ba02b74a59a2f2': - ref: 'fad242d35291de1898242d6737ba02b74a59a2f2' - commit: 'sha1-fad242d35291de1898242d6737ba02b74a59a2f2' - owner_id: 6759885 - repo_id: 1116521501 - uses: - - 'actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9' - - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1' - - 'editorconfig-checker/action-editorconfig-checker@840e866d93b8e032123c23bac69dece044d4d84c' - - 'erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124' 'peter-evans/repository-dispatch@v4.0.1': ref: 'v4.0.1' commit: 'sha1-28959ce8df70de7be546dd1250a005dd32156697' owner_id: 18365890 repo_id: 220359305 - 'slsa-framework/slsa-github-generator@f7dd8c54c2067bafc12ca7a55595d5ee9b75204a': - ref: 'f7dd8c54c2067bafc12ca7a55595d5ee9b75204a' - commit: 'sha1-f7dd8c54c2067bafc12ca7a55595d5ee9b75204a' - owner_id: 80431187 - repo_id: 475074978 - uses: - - 'actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08' - - 'slsa-framework/slsa-github-generator@v2.1.0' - - 'softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda' - 'slsa-framework/slsa-github-generator@v2.1.0': - ref: 'v2.1.0' - commit: 'sha1-f7dd8c54c2067bafc12ca7a55595d5ee9b75204a' - owner_id: 80431187 - repo_id: 475074978 'softprops/action-gh-release@v2.5.0': ref: 'v2.5.0' commit: 'sha1-a06a81a03ee405af7f2048a818ed3f03bbf83c7b' owner_id: 2242 repo_id: 204253808 - 'softprops/action-gh-release@v3.0.3': - ref: 'v3.0.3' - commit: 'sha1-efb35369e0ad2afab669f228072c1b0d510eae64' - owner_id: 2242 - repo_id: 204253808 'swatinem/rust-cache@v2.8.2': ref: 'v2.8.2' commit: 'sha1-779680da715d629ac1d338a641029a2f4372abb5' diff --git a/.github/workflows/governance.yml b/.github/workflows/governance.yml index e7aa4e9..c19baf7 100644 --- a/.github/workflows/governance.yml +++ b/.github/workflows/governance.yml @@ -17,4 +17,4 @@ permissions: jobs: governance: - uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@fad242d35291de1898242d6737ba02b74a59a2f2 + uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@333b06a01db1874ffedb2de1bfabb195640927c7 diff --git a/.github/workflows/hypatia-scan.yml b/.github/workflows/hypatia-scan.yml index a5a2131..9bf8587 100644 --- a/.github/workflows/hypatia-scan.yml +++ b/.github/workflows/hypatia-scan.yml @@ -20,4 +20,4 @@ permissions: jobs: hypatia: - uses: hyperpolymath/standards/.github/workflows/hypatia-scan-reusable.yml@84355587cb2a1f86e6882de83514a32db2646e7a + uses: hyperpolymath/standards/.github/workflows/hypatia-scan-reusable.yml@333b06a01db1874ffedb2de1bfabb195640927c7 diff --git a/.github/workflows/mirror.yml b/.github/workflows/mirror.yml index b95ea10..e4e4c3f 100644 --- a/.github/workflows/mirror.yml +++ b/.github/workflows/mirror.yml @@ -15,5 +15,5 @@ permissions: jobs: mirror: - uses: hyperpolymath/standards/.github/workflows/mirror-reusable.yml@84355587cb2a1f86e6882de83514a32db2646e7a + uses: hyperpolymath/standards/.github/workflows/mirror-reusable.yml@333b06a01db1874ffedb2de1bfabb195640927c7 secrets: inherit diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index bb07f89..73b1435 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -18,4 +18,4 @@ permissions: jobs: rust-ci: - uses: hyperpolymath/standards/.github/workflows/rust-ci-reusable.yml@84355587cb2a1f86e6882de83514a32db2646e7a # re-pinned 2026-07-01: toolchain-required-input fix (standards#439) + uses: hyperpolymath/standards/.github/workflows/rust-ci-reusable.yml@333b06a01db1874ffedb2de1bfabb195640927c7 # re-pinned 2026-07-01: toolchain-required-input fix (standards#439) diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 5d212f3..a389a51 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -15,8 +15,9 @@ permissions: jobs: scorecard: - uses: hyperpolymath/standards/.github/workflows/scorecard-reusable.yml@84355587cb2a1f86e6882de83514a32db2646e7a + uses: hyperpolymath/standards/.github/workflows/scorecard-reusable.yml@333b06a01db1874ffedb2de1bfabb195640927c7 permissions: + actions: read contents: read security-events: write id-token: write diff --git a/.github/workflows/secret-scanner.yml b/.github/workflows/secret-scanner.yml index fffde78..e366d02 100644 --- a/.github/workflows/secret-scanner.yml +++ b/.github/workflows/secret-scanner.yml @@ -21,5 +21,5 @@ jobs: scan: permissions: contents: read - uses: hyperpolymath/standards/.github/workflows/secret-scanner-reusable.yml@84355587cb2a1f86e6882de83514a32db2646e7a + uses: hyperpolymath/standards/.github/workflows/secret-scanner-reusable.yml@333b06a01db1874ffedb2de1bfabb195640927c7 secrets: inherit From b3f1d4d23095912bfcdf3dbc5880318faef2aad2 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 17:04:46 +0000 Subject: [PATCH 04/28] fix(ci): drop the duplicate 'with:' blocks in cargo-audit, coverage and release Each 'Install Rust toolchain' step had a stray 'with: toolchain: v1' before its real 'with:' block. GitHub rejects a workflow with duplicate keys before creating any job, so all three workflows were dead; the governance duplicate-key check fails on them. 'v1' is not a toolchain; the second block (stable, plus llvm-tools-preview for coverage) is the intended one and is kept. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019a95kk88TZ1iegsBK3kox3 --- .github/workflows/cargo-audit.yml | 2 -- .github/workflows/coverage.yml | 2 -- .github/workflows/release.yml | 2 -- 3 files changed, 6 deletions(-) diff --git a/.github/workflows/cargo-audit.yml b/.github/workflows/cargo-audit.yml index 97bb168..f6eb2a4 100644 --- a/.github/workflows/cargo-audit.yml +++ b/.github/workflows/cargo-audit.yml @@ -26,8 +26,6 @@ jobs: - name: Install Rust toolchain uses: dtolnay/rust-toolchain@v1 - with: - toolchain: v1 with: toolchain: stable diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index aefc6f3..93c65c5 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -24,8 +24,6 @@ jobs: - name: Install Rust toolchain uses: dtolnay/rust-toolchain@v1 - with: - toolchain: v1 with: toolchain: stable components: llvm-tools-preview diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0cbde44..9e0eb09 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,8 +31,6 @@ jobs: - name: Install Rust toolchain uses: dtolnay/rust-toolchain@v1 - with: - toolchain: v1 with: toolchain: stable From 25f5a54a8a71f276dba7a55c61a5599398c4324d Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 17:09:41 +0000 Subject: [PATCH 05/28] style: apply cargo fmt (surfaced once rust-ci could start) Whitespace-only; rust-ci's fmt step never ran on main because the workflow failed at startup on the lockfile. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019a95kk88TZ1iegsBK3kox3 --- src/aggregate/mod.rs | 28 +++--- src/aggregate/proven_tests.rs | 24 ++++-- src/assail/analyzer.rs | 62 ++++++++------ src/assail/mod.rs | 31 ++++--- src/assay/mod.rs | 18 ++-- src/comment_marker.rs | 5 +- src/diagnostics.rs | 2 +- src/ffi_kind.rs | 23 ++--- src/jit_context.rs | 18 ++-- src/kanren/core.rs | 2 +- src/lib.rs | 2 +- src/main.rs | 5 +- src/panll/mod.rs | 4 +- src/safe_path.rs | 5 +- src/scan/cargodeny.rs | 155 ++++++++++++++++++++-------------- src/storage/mod.rs | 4 +- src/test_context.rs | 10 +-- tests/analyzer_tests.rs | 23 +++-- tests/panll_tests.rs | 6 +- tests/property_tests.rs | 4 +- tests/readiness.rs | 6 +- tests/report_tests.rs | 6 +- tests/sarif_tests.rs | 4 +- tests/seam_contract_tests.rs | 2 +- 24 files changed, 241 insertions(+), 208 deletions(-) diff --git a/src/aggregate/mod.rs b/src/aggregate/mod.rs index cc26ecd..e4eaff3 100644 --- a/src/aggregate/mod.rs +++ b/src/aggregate/mod.rs @@ -397,8 +397,7 @@ pub fn run(config: AggregateConfig) -> Result { pub fn write_report(report: &AggregateReport, path: &Path) -> Result<()> { if let Some(parent) = path.parent() { if !parent.as_os_str().is_empty() { - fs::create_dir_all(parent) - .with_context(|| format!("creating {}", parent.display()))?; + fs::create_dir_all(parent).with_context(|| format!("creating {}", parent.display()))?; } } let json = serde_json::to_string_pretty(report).context("serializing aggregate report")?; @@ -496,8 +495,7 @@ fn has_metavar_hole(text: &str) -> bool { fn has_proof_keyword(text: &str) -> bool { const KW: &[&str] = &[ - "theorem", "lemma", "Theorem", "Lemma", "Proof", "proof", "where", ":=", "data ", - "record ", + "theorem", "lemma", "Theorem", "Lemma", "Proof", "proof", "where", ":=", "data ", "record ", ]; KW.iter().any(|k| text.contains(k)) } @@ -579,10 +577,7 @@ fn scan_name(text: &str) -> Option { let end = stripped.find('"')?; Some(stripped[..end].to_string()) } else { - let token: String = rest - .chars() - .take_while(|c| !c.is_whitespace()) - .collect(); + let token: String = rest.chars().take_while(|c| !c.is_whitespace()).collect(); if token.is_empty() { None } else { @@ -634,11 +629,7 @@ pub fn parse_coverage_spec(spec: &str) -> Option { [single] => (CoverageKind::Free, (*single).to_string()), _ => return None, }; - Some(Coverage { - claim, - kind, - value, - }) + Some(Coverage { claim, kind, value }) } fn kind_of(s: &str) -> Option { @@ -702,10 +693,7 @@ fn summarise(findings: &[FindingLite]) -> FindingSummary { } fn count_matching(findings: &[FindingLite], cov: &Coverage) -> usize { - findings - .iter() - .filter(|f| matches_coverage(f, cov)) - .count() + findings.iter().filter(|f| matches_coverage(f, cov)).count() } fn matches_coverage(f: &FindingLite, cov: &Coverage) -> bool { @@ -897,7 +885,11 @@ mod tests { Prover::Idris2, "-- we deliberately avoid any postulate here\ntheorem t : Nat\nt = 0\n", ); - assert!(holes.is_empty(), "comment word should not register: {:?}", holes); + assert!( + holes.is_empty(), + "comment word should not register: {:?}", + holes + ); assert_eq!(v, ProofVerdict::Closed); } diff --git a/src/aggregate/proven_tests.rs b/src/aggregate/proven_tests.rs index a748760..e12d807 100644 --- a/src/aggregate/proven_tests.rs +++ b/src/aggregate/proven_tests.rs @@ -131,8 +131,8 @@ impl ProvenTestsReport { /// Parse a run report from a file path. pub fn from_path(path: &Path) -> Result { - let text = std::fs::read_to_string(path) - .with_context(|| format!("reading {}", path.display()))?; + let text = + std::fs::read_to_string(path).with_context(|| format!("reading {}", path.display()))?; Self::from_str(&text) } @@ -163,12 +163,21 @@ impl ProvenTestsReport { e.proof_ladder.len() ), Some(ReportTier::ProvisionallyProven) => { - format!("{}: Provisionally-Proven → informational (no proof artifact)", e.test_id) + format!( + "{}: Provisionally-Proven → informational (no proof artifact)", + e.test_id + ) } Some(ReportTier::Unproven) => { - format!("{}: Unproven → informational (no proof artifact)", e.test_id) + format!( + "{}: Unproven → informational (no proof artifact)", + e.test_id + ) } - None => format!("{}: unrecognised provenance tier {:?}", e.test_id, e.provenance), + None => format!( + "{}: unrecognised provenance tier {:?}", + e.test_id, e.provenance + ), }) .collect() } @@ -240,6 +249,9 @@ mod tests { let weird = SAMPLE.replace("\"Unproven\"", "\"Sorta-Proven\""); let r = ProvenTestsReport::from_str(&weird).unwrap(); // notes() surfaces the unrecognised tier rather than crashing - assert!(r.notes().iter().any(|n| n.contains("unrecognised provenance tier"))); + assert!(r + .notes() + .iter() + .any(|n| n.contains("unrecognised provenance tier"))); } } diff --git a/src/assail/analyzer.rs b/src/assail/analyzer.rs index c21c436..7aab8fb 100644 --- a/src/assail/analyzer.rs +++ b/src/assail/analyzer.rs @@ -984,9 +984,8 @@ impl Analyzer { // crypto counts too (a `#[test] fn exercises_unsafe_wrapper()` // inside a production file would otherwise count toward that // file's unsafe-block total). - let code_only = Self::strip_should_panic_items_rs( - &Self::strip_cfg_test_modules_rs(&without_comments), - ); + let code_only = + Self::strip_should_panic_items_rs(&Self::strip_cfg_test_modules_rs(&without_comments)); stats.unsafe_blocks += code_only.matches("unsafe {").count(); stats.unsafe_blocks += code_only.matches("unsafe fn").count(); @@ -1903,7 +1902,7 @@ impl Analyzer { ), recommended_attack: vec![AttackAxis::Network], suppressed: false, - test_context: None, + test_context: None, }); break; } @@ -1926,7 +1925,7 @@ impl Analyzer { ), recommended_attack: vec![AttackAxis::Network], suppressed: false, - test_context: None, + test_context: None, }); break; } @@ -2089,7 +2088,7 @@ impl Analyzer { ), recommended_attack: vec![AttackAxis::Network], suppressed: false, - test_context: None, + test_context: None, }); break; } @@ -2111,7 +2110,7 @@ impl Analyzer { ), recommended_attack: vec![AttackAxis::Network], suppressed: false, - test_context: None, + test_context: None, }); break; } @@ -2256,7 +2255,7 @@ impl Analyzer { ), recommended_attack: vec![AttackAxis::Network], suppressed: false, - test_context: None, + test_context: None, }); break; } @@ -2282,7 +2281,7 @@ impl Analyzer { ), recommended_attack: vec![AttackAxis::Network], suppressed: false, - test_context: None, + test_context: None, }); break; } @@ -3204,7 +3203,7 @@ impl Analyzer { ), recommended_attack: vec![AttackAxis::Memory], suppressed: false, - test_context: None, + test_context: None, }); } else { weak_points.push(WeakPoint { @@ -3216,7 +3215,7 @@ impl Analyzer { description: format!("Obj.magic (unsafe type coercion) in {}", file_path), recommended_attack: vec![AttackAxis::Memory], suppressed: false, - test_context: None, + test_context: None, }); } } @@ -4360,7 +4359,7 @@ impl Analyzer { description: format!("{} rawptr usage in {}", rawptr_count, file_path), recommended_attack: vec![AttackAxis::Memory], suppressed: false, - test_context: None, + test_context: None, }); } } @@ -4561,7 +4560,7 @@ impl Analyzer { ), recommended_attack: vec![], suppressed: false, - test_context: None, + test_context: None, }); } } @@ -4577,7 +4576,7 @@ impl Analyzer { description: format!("builtins.exec (command execution) in {}", file_path), recommended_attack: vec![AttackAxis::Cpu, AttackAxis::Disk], suppressed: false, - test_context: None, + test_context: None, }); } @@ -4619,8 +4618,9 @@ impl Analyzer { // statement position (start of line, or after whitespace/`;`/`&`/`|`/`(`) // so that `--eval` / `-eval` CLI flags and identifiers like `myeval` // are not flagged. - let shell_eval = RE_SHELL_EVAL_BUILTIN - .get_or_init(|| Regex::new(r"(?m)(?:^|[\s;&|(])eval[ \t]").expect("static regex is valid")); + let shell_eval = RE_SHELL_EVAL_BUILTIN.get_or_init(|| { + Regex::new(r"(?m)(?:^|[\s;&|(])eval[ \t]").expect("static regex is valid") + }); if shell_eval.is_match(content) { weak_points.push(WeakPoint { file: None, @@ -5105,15 +5105,18 @@ impl Analyzer { // "passwd\s*=" do not appear contiguously on any single source // line — the estate-wide secret-scanner grep otherwise self-flags // this file (it is the scanner's own pattern data). - let secret_re = RE_HARDCODED_SECRET.get_or_init(|| Regex::new( - concat!( + let secret_re = RE_HARDCODED_SECRET.get_or_init(|| { + Regex::new(concat!( r#"(?i)(api[_-]?key|api[_-]?secret|"#, - "p", "assword|", - "p", "asswd|", + "p", + "assword|", + "p", + "asswd|", r#"secret[_-]?key|access[_-]?token|private[_-]?key)"#, r#"\s*[=:]\s*["'][^"']{8,}"#, - ) - ).expect("static regex is valid")); + )) + .expect("static regex is valid") + }); if secret_re.is_match(content) { weak_points.push(WeakPoint { file: None, @@ -5196,7 +5199,7 @@ impl Analyzer { ), recommended_attack: vec![], suppressed: false, - test_context: None, + test_context: None, }); } @@ -5215,7 +5218,7 @@ impl Analyzer { .to_string(), recommended_attack: vec![], suppressed: false, - test_context: None, + test_context: None, }); } } @@ -5241,7 +5244,7 @@ impl Analyzer { .to_string(), recommended_attack: vec![], suppressed: false, - test_context: None, + test_context: None, }); } } @@ -5275,7 +5278,7 @@ impl Analyzer { ), recommended_attack: vec![], suppressed: false, - test_context: None, + test_context: None, }); } } @@ -5331,7 +5334,7 @@ impl Analyzer { .to_string(), recommended_attack: vec![], suppressed: false, - test_context: None, + test_context: None, }); } } @@ -6904,7 +6907,10 @@ fn one_unwrap() { .filter(|wp| wp.category == WeakPointCategory::PanicPath) .collect(); - assert!(!panic_points.is_empty(), "A production unwrap must be reported"); + assert!( + !panic_points.is_empty(), + "A production unwrap must be reported" + ); } // --------------------------------------------------------------- diff --git a/src/assail/mod.rs b/src/assail/mod.rs index eb4e53b..5ebb810 100644 --- a/src/assail/mod.rs +++ b/src/assail/mod.rs @@ -432,7 +432,9 @@ pub fn apply_v255_context_suppression(report: &mut AssailReport) -> usize { // fall back to parsing `location` ("path:line"). let file_path: Option = wp.file.clone().or_else(|| { wp.location.as_ref().and_then(|loc| { - loc.rsplit_once(':').map(|(p, _)| p.to_string()).or_else(|| Some(loc.clone())) + loc.rsplit_once(':') + .map(|(p, _)| p.to_string()) + .or_else(|| Some(loc.clone())) }) }); @@ -708,7 +710,7 @@ mod classifications_tests { description: "8 unsafe blocks".to_string(), recommended_attack: vec![AttackAxis::Memory], suppressed: false, - test_context: None, + test_context: None, }, WeakPoint { file: None, @@ -719,7 +721,7 @@ mod classifications_tests { description: "unsafe block".to_string(), recommended_attack: vec![AttackAxis::Memory], suppressed: false, - test_context: None, + test_context: None, }, ], statistics: ProgramStatistics { @@ -758,8 +760,8 @@ mod classifications_tests { mod v255_context_suppression_tests { use super::apply_v255_context_suppression; use crate::types::{ - AssailReport, AttackAxis, ProgramStatistics, Severity, TestContext, - WeakPoint, WeakPointCategory, + AssailReport, AttackAxis, ProgramStatistics, Severity, TestContext, WeakPoint, + WeakPointCategory, }; use std::fs; use tempfile::TempDir; @@ -798,10 +800,7 @@ mod v255_context_suppression_tests { #[test] fn panic_path_in_test_file_is_suppressed() { - let mut report = report_with(vec![wp( - WeakPointCategory::PanicPath, - "tests/foo.rs:42", - )]); + let mut report = report_with(vec![wp(WeakPointCategory::PanicPath, "tests/foo.rs:42")]); let n = apply_v255_context_suppression(&mut report); assert_eq!(n, 1); assert!(report.weak_points[0].suppressed); @@ -813,10 +812,7 @@ mod v255_context_suppression_tests { #[test] fn panic_path_in_prod_file_not_suppressed() { - let mut report = report_with(vec![wp( - WeakPointCategory::PanicPath, - "src/main.rs:42", - )]); + let mut report = report_with(vec![wp(WeakPointCategory::PanicPath, "src/main.rs:42")]); let n = apply_v255_context_suppression(&mut report); assert_eq!(n, 0); assert!(!report.weak_points[0].suppressed); @@ -828,8 +824,7 @@ mod v255_context_suppression_tests { #[test] fn unsafe_ffi_in_build_zig_is_suppressed() { - let mut report = - report_with(vec![wp(WeakPointCategory::UnsafeFFI, "build.zig:5")]); + let mut report = report_with(vec![wp(WeakPointCategory::UnsafeFFI, "build.zig:5")]); let n = apply_v255_context_suppression(&mut report); assert_eq!(n, 1); assert!(report.weak_points[0].suppressed); @@ -851,7 +846,11 @@ mod v255_context_suppression_tests { let tmp = TempDir::new().unwrap(); let path = tmp.path().join("foo.rs"); // Line 2 has an unwrap; line 1 has the marker. - fs::write(&path, "// panic-attack: accepted - test\nlet x = foo.unwrap();\n").unwrap(); + fs::write( + &path, + "// panic-attack: accepted - test\nlet x = foo.unwrap();\n", + ) + .unwrap(); let mut report = report_with(vec![wp( WeakPointCategory::UnsafeCode, diff --git a/src/assay/mod.rs b/src/assay/mod.rs index b1fb21c..d79a4b8 100644 --- a/src/assay/mod.rs +++ b/src/assay/mod.rs @@ -280,8 +280,7 @@ pub fn run(config: AssayConfig) -> Result { pub fn write_report(report: &AssayReport, path: &Path) -> Result<()> { if let Some(parent) = path.parent() { if !parent.as_os_str().is_empty() { - fs::create_dir_all(parent) - .with_context(|| format!("creating {}", parent.display()))?; + fs::create_dir_all(parent).with_context(|| format!("creating {}", parent.display()))?; } } let json = serde_json::to_string_pretty(report).context("serializing assay report")?; @@ -479,8 +478,9 @@ pub fn assimilate(config: AssimilateConfig) -> Result { if let Some(bak) = &backup { // Never clobber a pre-existing .orig backup. if !bak.exists() { - fs::copy(&dest, bak) - .with_context(|| format!("backing up {} -> {}", dest.display(), bak.display()))?; + fs::copy(&dest, bak).with_context(|| { + format!("backing up {} -> {}", dest.display(), bak.display()) + })?; } } fs::copy(&source, &dest) @@ -684,7 +684,9 @@ mod tests { // Destination now holds the proven content. let dest = target.path().join("src/safe_url.rs"); - assert!(fs::read_to_string(&dest).unwrap().contains("PROVEN SafeUrl")); + assert!(fs::read_to_string(&dest) + .unwrap() + .contains("PROVEN SafeUrl")); // Backup holds the old content. let bak = rec.backup.as_ref().unwrap(); assert!(fs::read_to_string(bak).unwrap().contains("OLD placeholder")); @@ -698,11 +700,7 @@ mod tests { let proven = TempDir::new().unwrap(); let tsrc = target.path().join("src"); fs::create_dir_all(&tsrc).unwrap(); - fs::write( - tsrc.join("storage.rs"), - "VERISIMDB_URL env access here\n", - ) - .unwrap(); + fs::write(tsrc.join("storage.rs"), "VERISIMDB_URL env access here\n").unwrap(); let psrc = proven.path().join("ports"); fs::create_dir_all(&psrc).unwrap(); fs::write(psrc.join("safe_url.rs"), "// PROVEN\n").unwrap(); diff --git a/src/comment_marker.rs b/src/comment_marker.rs index cb47620..eab824e 100644 --- a/src/comment_marker.rs +++ b/src/comment_marker.rs @@ -229,10 +229,7 @@ mod tests { let m = parse_accepted_marker_in_line( "// panic-attack: accepted (PA005 — manual audit 2026-06-02)", ); - assert_eq!( - m, - Some("(PA005 — manual audit 2026-06-02)".to_string()) - ); + assert_eq!(m, Some("(PA005 — manual audit 2026-06-02)".to_string())); } #[test] diff --git a/src/diagnostics.rs b/src/diagnostics.rs index 7ddb57b..18e004e 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -214,7 +214,7 @@ fn check_panicbot_readiness() -> Diagnostic { description: "test".to_string(), recommended_attack: vec![], suppressed: false, - test_context: None, + test_context: None, }], statistics: crate::types::ProgramStatistics::default(), file_statistics: vec![], diff --git a/src/ffi_kind.rs b/src/ffi_kind.rs index 66312d2..9efafb5 100644 --- a/src/ffi_kind.rs +++ b/src/ffi_kind.rs @@ -92,8 +92,10 @@ fn is_test_mock_path(p: &str) -> bool { // path. Plain test files are still RuntimeAbi if they call the // real FFI surface (they exercise it). let has_test_scope = p.contains("tests/") || p.contains("test/"); - let has_mock_indicator = - p.contains("/mocks/") || p.contains("_mock.") || p.contains("/stubs/") || p.contains("_stub."); + let has_mock_indicator = p.contains("/mocks/") + || p.contains("_mock.") + || p.contains("/stubs/") + || p.contains("_stub."); has_test_scope && has_mock_indicator } @@ -128,10 +130,9 @@ pub fn is_audited_boundary(audit_text: &str, file_path: &str) -> bool { for line in audit_text.lines() { let trimmed = line.trim(); if trimmed.starts_with("## ") { - in_approved_section = - trimmed.eq_ignore_ascii_case("## Approved boundaries") - || trimmed.eq_ignore_ascii_case("## Approved FFI boundaries") - || trimmed.eq_ignore_ascii_case("## Pre-approved boundaries"); + in_approved_section = trimmed.eq_ignore_ascii_case("## Approved boundaries") + || trimmed.eq_ignore_ascii_case("## Approved FFI boundaries") + || trimmed.eq_ignore_ascii_case("## Pre-approved boundaries"); continue; } if !in_approved_section { @@ -160,10 +161,7 @@ mod tests { #[test] fn build_zig_classified_build_system() { - assert_eq!( - FfiKind::classify_by_path("build.zig"), - FfiKind::BuildSystem - ); + assert_eq!(FfiKind::classify_by_path("build.zig"), FfiKind::BuildSystem); assert_eq!( FfiKind::classify_by_path("src/foo/build.zig"), FfiKind::BuildSystem @@ -209,10 +207,7 @@ mod tests { #[test] fn unrecognised_path_is_unknown() { - assert_eq!( - FfiKind::classify_by_path("src/main.rs"), - FfiKind::Unknown - ); + assert_eq!(FfiKind::classify_by_path("src/main.rs"), FfiKind::Unknown); assert_eq!( FfiKind::classify_by_path("examples/hello.zig"), FfiKind::Unknown diff --git a/src/jit_context.rs b/src/jit_context.rs index 874ec09..383b1f3 100644 --- a/src/jit_context.rs +++ b/src/jit_context.rs @@ -85,8 +85,7 @@ fn is_cranelift(content: &str) -> bool { content.contains("cranelift_jit::JITModule") || content.contains("cranelift_module::Module") || content.contains("JITModule::new(") - || (content.contains("cranelift_jit") - && content.contains("get_finalized_function")) + || (content.contains("cranelift_jit") && content.contains("get_finalized_function")) || content.contains("use cranelift_jit") || content.contains("use cranelift_module") } @@ -131,8 +130,7 @@ pub fn transmute_targets_fn_ptr(content: &str) -> bool { // `: unsafe fn(...)`) annotation co-occurs with a `transmute(` // call. The `=` may have intervening tokens (`unsafe { ... }`, // closure wrappers, etc.), so we don't require it adjacent. - let has_fn_annotation = - content.contains(": fn(") || content.contains(": unsafe fn("); + let has_fn_annotation = content.contains(": fn(") || content.contains(": unsafe fn("); let has_transmute_call = content.contains("transmute(") || content.contains("mem::transmute(") || content.contains("std::mem::transmute("); @@ -157,8 +155,7 @@ mod tests { #[test] fn llvm_inkwell_classified() { - let src = - "use inkwell::execution_engine::ExecutionEngine;\nfn run() {}\n"; + let src = "use inkwell::execution_engine::ExecutionEngine;\nfn run() {}\n"; assert_eq!(classify_rust(src), JitContext::Llvm); } @@ -203,15 +200,13 @@ mod tests { #[test] fn transmute_to_fn_ptr_turbofish_recognised() { - let src = - "let f: fn() = unsafe { std::mem::transmute::<_, fn()>(ptr) };"; + let src = "let f: fn() = unsafe { std::mem::transmute::<_, fn()>(ptr) };"; assert!(transmute_targets_fn_ptr(src)); } #[test] fn transmute_to_fn_ptr_let_binding_recognised() { - let src = - "let f: fn(i64) -> i64 = unsafe { mem::transmute(raw_ptr) };"; + let src = "let f: fn(i64) -> i64 = unsafe { mem::transmute(raw_ptr) };"; assert!(transmute_targets_fn_ptr(src)); } @@ -223,8 +218,7 @@ mod tests { #[test] fn transmute_to_non_fn_not_recognised() { - let src = - "let n: u64 = unsafe { std::mem::transmute::<_, u64>(value) };"; + let src = "let n: u64 = unsafe { std::mem::transmute::<_, u64>(value) };"; assert!(!transmute_targets_fn_ptr(src)); } diff --git a/src/kanren/core.rs b/src/kanren/core.rs index 1de1ee8..e0730c4 100644 --- a/src/kanren/core.rs +++ b/src/kanren/core.rs @@ -1340,7 +1340,7 @@ mod tests { description: description.to_string(), recommended_attack: vec![], suppressed: false, - test_context: None, + test_context: None, } } diff --git a/src/lib.rs b/src/lib.rs index 14e2985..cb28a3a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,7 +27,6 @@ pub mod assemblyline; pub mod attack; pub mod attestation; pub mod axial; -pub mod scan; #[cfg(feature = "http")] pub mod bridge; pub mod campaign; @@ -42,6 +41,7 @@ pub mod panll; pub mod query; pub mod report; pub mod safe_path; +pub mod scan; pub mod signatures; pub mod storage; pub mod sweep_tracker; diff --git a/src/main.rs b/src/main.rs index 41ab06c..2929e0a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2734,9 +2734,8 @@ fn run_main() -> Result<()> { PanicAttackHexad, }; - let json = std::fs::read_to_string(&hexad).with_context(|| { - format!("reading hexad file {}", hexad.display()) - })?; + let json = std::fs::read_to_string(&hexad) + .with_context(|| format!("reading hexad file {}", hexad.display()))?; let hexad_value: PanicAttackHexad = serde_json::from_str(&json) .with_context(|| format!("parsing hexad JSON at {}", hexad.display()))?; diff --git a/src/panll/mod.rs b/src/panll/mod.rs index a183635..4f26a25 100644 --- a/src/panll/mod.rs +++ b/src/panll/mod.rs @@ -689,7 +689,7 @@ mod tests { description: "unwrap on None".to_string(), recommended_attack: vec![], suppressed: false, - test_context: None, + test_context: None, }, WeakPoint { category: WeakPointCategory::UnsafeCode, @@ -700,7 +700,7 @@ mod tests { description: "unsafe block".to_string(), recommended_attack: vec![], suppressed: false, - test_context: None, + test_context: None, }, ], vec![], diff --git a/src/safe_path.rs b/src/safe_path.rs index 8c63a9e..e79b525 100644 --- a/src/safe_path.rs +++ b/src/safe_path.rs @@ -191,7 +191,10 @@ mod tests { #[test] fn sanitize_keeps_safe_punct() { - assert_eq!(sanitize_filename("my-file_2026.06.02"), "my-file_2026.06.02"); + assert_eq!( + sanitize_filename("my-file_2026.06.02"), + "my-file_2026.06.02" + ); assert_eq!(sanitize_filename("config~"), "config~"); } diff --git a/src/scan/cargodeny.rs b/src/scan/cargodeny.rs index 684bce9..5ebcb0b 100644 --- a/src/scan/cargodeny.rs +++ b/src/scan/cargodeny.rs @@ -115,7 +115,7 @@ fn code_to_category(code: &str) -> WeakPointCategory { /// Run cargo-deny check and parse output pub fn run_cargo_deny(config: &CargoDenyConfig) -> Result> { let workspace_root = &config.workspace_root; - + // Build cargo deny command let mut cmd = Command::new("cargo"); cmd.arg("deny") @@ -123,17 +123,17 @@ pub fn run_cargo_deny(config: &CargoDenyConfig) -> Result> { .arg("--format") .arg(&config.format) .current_dir(workspace_root); - + // Add optional flags if config.fail_on_warnings { cmd.arg("--fail-on-warnings"); } - + // Add extra args for arg in &config.extra_args { cmd.arg(arg); } - + // Execute command let output = cmd.output().with_context(|| { format!( @@ -141,7 +141,7 @@ pub fn run_cargo_deny(config: &CargoDenyConfig) -> Result> { workspace_root.display() ) })?; - + // Check exit status - cargo-deny returns non-zero on failures // but we still want to parse the output if !output.status.success() { @@ -152,35 +152,40 @@ pub fn run_cargo_deny(config: &CargoDenyConfig) -> Result> { workspace_root.display() ); } - + // Parse JSON output - let stdout = String::from_utf8(output.stdout) - .with_context(|| "cargo deny output is not valid UTF-8")?; - - let deny_output: DenyOutput = serde_json::from_str(&stdout) - .with_context(|| "failed to parse cargo deny JSON output")?; - + let stdout = + String::from_utf8(output.stdout).with_context(|| "cargo deny output is not valid UTF-8")?; + + let deny_output: DenyOutput = + serde_json::from_str(&stdout).with_context(|| "failed to parse cargo deny JSON output")?; + // Convert diagnostics to WeakPoints let mut weak_points = Vec::new(); for diagnostic in deny_output.diagnostics { - let severity = diagnostic.severity + let severity = diagnostic + .severity .map(|s| parse_deny_severity(&s).into()) .unwrap_or(Severity::Medium); - - let category = diagnostic.code + + let category = diagnostic + .code .map(|c| code_to_category(&c)) .unwrap_or(WeakPointCategory::Advisory); - - let location = diagnostic.location.map(|loc| { - if let (Some(ref file), Some(line)) = (&loc.file, loc.line) { - Some(format!("{}:{}", file, line)) - } else if let Some(ref file) = loc.file { - Some(file.clone()) - } else { - None - } - }).flatten(); - + + let location = diagnostic + .location + .map(|loc| { + if let (Some(ref file), Some(line)) = (&loc.file, loc.line) { + Some(format!("{}:{}", file, line)) + } else if let Some(ref file) = loc.file { + Some(file.clone()) + } else { + None + } + }) + .flatten(); + // Parse file and line from location string let (file, line) = if let Some(ref loc) = location { if let Some((f, l)) = loc.rsplit_once(':') { @@ -195,7 +200,7 @@ pub fn run_cargo_deny(config: &CargoDenyConfig) -> Result> { } else { (None, None) }; - + let weak_point = WeakPoint { category, location: location.clone(), @@ -207,10 +212,10 @@ pub fn run_cargo_deny(config: &CargoDenyConfig) -> Result> { suppressed: false, test_context: None, }; - + weak_points.push(weak_point); } - + Ok(weak_points) } @@ -261,40 +266,50 @@ impl From for CargoDenyWeakPoint { #[cfg(feature = "sarif")] pub fn parse_sarif_output(sarif_json: &str) -> Result> { use serde_json::Value; - + let v: Value = serde_json::from_str(sarif_json)?; - + // SARIF structure: { runs: [{ results: [{ ... }] }] } - let runs = v["runs"].as_array().ok_or_else(|| anyhow!("no runs in SARIF"))?; + let runs = v["runs"] + .as_array() + .ok_or_else(|| anyhow!("no runs in SARIF"))?; let mut weak_points = Vec::new(); - + for run in runs { - let results = run["results"].as_array().ok_or_else(|| anyhow!("no results in SARIF run"))?; + let results = run["results"] + .as_array() + .ok_or_else(|| anyhow!("no results in SARIF run"))?; for result in results { - let message = result["message"]["text"].as_str() + let message = result["message"]["text"] + .as_str() .or_else(|| result["message"].as_str()) - .unwrap_or("Unknown cargo-deny finding").to_string(); - + .unwrap_or("Unknown cargo-deny finding") + .to_string(); + let severity_str = result["level"].as_str().unwrap_or("warning"); let severity = parse_deny_severity(severity_str).into(); - + let rule_id = result["ruleId"].as_str().map(|s| s.to_string()); let category = rule_id .as_ref() .map(|c| code_to_category(c)) .unwrap_or(WeakPointCategory::Advisory); - + let locations = result["locations"].as_array(); let (location, file, line) = if let Some(locs) = locations { if !locs.is_empty() { let loc = &locs[0]; let physical_loc = &loc["physicalLocation"]; - let file_path = physical_loc["artifactLocation"]["uri"].as_str() + let file_path = physical_loc["artifactLocation"]["uri"] + .as_str() .or_else(|| physical_loc["artifactLocation"]["uriBaseId"].as_str()) .or_else(|| loc["physicalLocation"]["artifactLocation"]["uri"].as_str()); - let line_num = physical_loc["region"]["startLine"].as_u64() - .or_else(|| physical_loc["region"]["startLine"].as_i64().map(|n| n as u64)); - + let line_num = physical_loc["region"]["startLine"].as_u64().or_else(|| { + physical_loc["region"]["startLine"] + .as_i64() + .map(|n| n as u64) + }); + let location = file_path.map(|f| { if let Some(ln) = line_num { format!("{}:{}", f, ln) @@ -302,15 +317,19 @@ pub fn parse_sarif_output(sarif_json: &str) -> Result> { f.to_string() } }); - - (location, file_path.map(|s| s.to_string()), line_num.map(|n| n as u32)) + + ( + location, + file_path.map(|s| s.to_string()), + line_num.map(|n| n as u32), + ) } else { (None, None, None) } } else { (None, None, None) }; - + let wp = WeakPoint { category, location: location.clone(), @@ -322,19 +341,16 @@ pub fn parse_sarif_output(sarif_json: &str) -> Result> { suppressed: false, test_context: None, }; - + weak_points.push(wp); } } - + Ok(weak_points) } /// High-level function to scan a workspace and emit cargo-deny weak points -pub fn scan_workspace( - workspace_root: &Path, - deny_toml: Option<&Path>, -) -> Result> { +pub fn scan_workspace(workspace_root: &Path, deny_toml: Option<&Path>) -> Result> { let config = CargoDenyConfig { workspace_root: workspace_root.to_path_buf(), deny_toml: deny_toml.map(|p| p.to_path_buf()), @@ -342,30 +358,42 @@ pub fn scan_workspace( fail_on_warnings: false, extra_args: Vec::new(), }; - + run_cargo_deny(&config) } #[cfg(test)] mod tests { use super::*; - + #[test] fn test_severity_mapping() { assert_eq!(Severity::from(DenySeverity::Error), Severity::High); assert_eq!(Severity::from(DenySeverity::Warning), Severity::Medium); assert_eq!(Severity::from(DenySeverity::Note), Severity::Low); } - + #[test] fn test_code_to_category() { - assert_eq!(code_to_category("RUSTSEC-2021-0001"), WeakPointCategory::Advisory); - assert_eq!(code_to_category("CVE-2021-12345"), WeakPointCategory::Advisory); + assert_eq!( + code_to_category("RUSTSEC-2021-0001"), + WeakPointCategory::Advisory + ); + assert_eq!( + code_to_category("CVE-2021-12345"), + WeakPointCategory::Advisory + ); assert_eq!(code_to_category("banned"), WeakPointCategory::BannedDep); - assert_eq!(code_to_category("license"), WeakPointCategory::LicensePolicy); - assert_eq!(code_to_category("unknown-source"), WeakPointCategory::UnknownSource); + assert_eq!( + code_to_category("license"), + WeakPointCategory::LicensePolicy + ); + assert_eq!( + code_to_category("unknown-source"), + WeakPointCategory::UnknownSource + ); } - + #[test] fn test_parse_severity() { assert_eq!(parse_deny_severity("error"), DenySeverity::Error); @@ -412,11 +440,14 @@ mod tests { }"#; let deny_output: DenyOutput = serde_json::from_str(json_output).unwrap(); - + // Verify the parsing logic works assert_eq!(deny_output.diagnostics.len(), 3); assert_eq!(deny_output.diagnostics[0].code, Some("license".to_string())); assert_eq!(deny_output.diagnostics[1].code, Some("banned".to_string())); - assert_eq!(deny_output.diagnostics[2].code, Some("RUSTSEC-2021-0001".to_string())); + assert_eq!( + deny_output.diagnostics[2].code, + Some("RUSTSEC-2021-0001".to_string()) + ); } } diff --git a/src/storage/mod.rs b/src/storage/mod.rs index c5dc518..2c4fbd1 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -1393,7 +1393,7 @@ mod tests { description: format!("test finding at {}:{}", file, line), recommended_attack: Vec::new(), suppressed: false, - test_context: None, + test_context: None, } } @@ -1619,7 +1619,7 @@ mod tests { description: "ffi boundary".to_string(), recommended_attack: Vec::new(), suppressed: false, - test_context: None, + test_context: None, } } diff --git a/src/test_context.rs b/src/test_context.rs index 7a5a612..17c2635 100644 --- a/src/test_context.rs +++ b/src/test_context.rs @@ -229,10 +229,7 @@ mod tests { assert_eq!(classify_path("src/foo.ts"), TestContext::Production); assert_eq!(classify_path("src/foo.test.ts"), TestContext::TestOnly); assert_eq!(classify_path("src/foo.spec.js"), TestContext::TestOnly); - assert_eq!( - classify_path("__tests__/foo.tsx"), - TestContext::TestOnly - ); + assert_eq!(classify_path("__tests__/foo.tsx"), TestContext::TestOnly); } #[test] @@ -251,10 +248,7 @@ mod tests { #[test] fn doc_paths_win_over_test() { - assert_eq!( - classify_path("docs/examples/hello.rs"), - TestContext::Doc - ); + assert_eq!(classify_path("docs/examples/hello.rs"), TestContext::Doc); assert_eq!(classify_path("foo.example.py"), TestContext::Doc); } diff --git a/tests/analyzer_tests.rs b/tests/analyzer_tests.rs index a8239a5..67570eb 100644 --- a/tests/analyzer_tests.rs +++ b/tests/analyzer_tests.rs @@ -391,8 +391,8 @@ int* make(void) { } #[test] -fn test_c_analyzer_still_flags_genuinely_unchecked_malloc( -) -> Result<(), Box> { +fn test_c_analyzer_still_flags_genuinely_unchecked_malloc() -> Result<(), Box> +{ let dir = TempDir::new()?; let file = write_test_file( &dir, @@ -416,17 +416,23 @@ int main() { } #[test] -fn test_shell_eval_builtin_flagged_but_not_cli_flag( -) -> Result<(), Box> { +fn test_shell_eval_builtin_flagged_but_not_cli_flag() -> Result<(), Box> { let dir = TempDir::new()?; // `--eval` is a CLI flag, not the shell eval builtin. - let flag = write_test_file(&dir, "flag.sh", "#!/bin/sh\nmybinary --eval \"window.close()\"\n")?; + let flag = write_test_file( + &dir, + "flag.sh", + "#!/bin/sh\nmybinary --eval \"window.close()\"\n", + )?; let report = assail::analyze(&flag)?; let flagged = report .weak_points .iter() .any(|wp| matches!(wp.category, WeakPointCategory::CommandInjection)); - assert!(!flagged, "--eval CLI flag must NOT be flagged as shell eval"); + assert!( + !flagged, + "--eval CLI flag must NOT be flagged as shell eval" + ); // The real eval builtin must still be flagged. let builtin = write_test_file(&dir, "real.sh", "#!/bin/sh\neval \"$user_input\"\n")?; @@ -453,7 +459,10 @@ fn test_js_ffi_eval_symbol_not_flagged() -> Result<(), Box Result<(), Box Result<(), Box Result<(), Box AssailReport { description: "2 unsafe blocks in src/main.rs".to_string(), recommended_attack: vec![AttackAxis::Memory, AttackAxis::Concurrency], suppressed: false, - test_context: None, + test_context: None, }, WeakPoint { category: WeakPointCategory::PanicPath, @@ -38,7 +38,7 @@ fn make_assail_report() -> AssailReport { description: "5 unwrap/expect calls in src/lib.rs".to_string(), recommended_attack: vec![AttackAxis::Memory], suppressed: false, - test_context: None, + test_context: None, }, ], statistics: ProgramStatistics { @@ -157,7 +157,7 @@ fn test_robustness_score_clamped_to_zero() -> Result<(), Box AssailReport { line: None, recommended_attack: vec![AttackAxis::Memory], suppressed: false, - test_context: None, + test_context: None, }, WeakPoint { category: WeakPointCategory::PanicPath, @@ -38,7 +38,7 @@ fn make_test_report() -> AssailReport { line: None, recommended_attack: vec![], suppressed: false, - test_context: None, + test_context: None, }, ], statistics: ProgramStatistics::default(), diff --git a/tests/seam_contract_tests.rs b/tests/seam_contract_tests.rs index e1c577b..b66d5cb 100644 --- a/tests/seam_contract_tests.rs +++ b/tests/seam_contract_tests.rs @@ -46,7 +46,7 @@ fn minimal_assail_report() -> AssailReport { description: "unwrap calls".to_string(), recommended_attack: vec![AttackAxis::Memory], suppressed: false, - test_context: None, + test_context: None, }], statistics: ProgramStatistics { total_lines: 100, From 4bc95c32d84f779e68a90a1d744a92fbda263305 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 17:13:07 +0000 Subject: [PATCH 06/28] fix(ci): regenerate actions.lock from scratch with gh actions-lock + repair chain Built in CI by gh actions-lock v0.2.0 followed by the standards repair chain at 333b06a0 (relock-sha-keys, complete-job-refs, close-lock, prune-stale), starting from no lock: main's lock pins dtolnay/rust-toolchain@v1 at a commit the tag no longer names, which gh actions-lock rejects as a conflicting target. check-lock-sync and check-actions-lock-gate.sh pass; sha256 83a024a8b1293502f931da414fdd755710816dc8b8ea76df07dcb120b41e7115. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019a95kk88TZ1iegsBK3kox3 --- .github/workflows/actions.lock | 340 ++++++++++++++++++--------------- 1 file changed, 185 insertions(+), 155 deletions(-) diff --git a/.github/workflows/actions.lock b/.github/workflows/actions.lock index 807b706..70d1c12 100644 --- a/.github/workflows/actions.lock +++ b/.github/workflows/actions.lock @@ -1,105 +1,97 @@ # This file is machine-generated by `gh actions-lock`. # Do not edit by hand; run `gh actions-lock` to update. # Docs: https://gh.io/actions-lockfile -version: 'v0.0.2' +version: 'v0.0.3' workflows: '.github/workflows/boj-build.yml': - - 'actions/checkout@v4.1.7' + - 'actions/checkout@v7.0.1' '.github/workflows/bridge-gate.yml': - - 'actions/checkout@v4.3.1' + - 'actions/checkout@v7.0.1' '.github/workflows/cargo-audit.yml': - - 'actions/checkout@v5.0.1' + - 'actions/checkout@v7.0.1' - 'dtolnay/rust-toolchain@v1' '.github/workflows/casket-pages.yml': - - 'actions/cache@v4.3.0' - - 'actions/checkout@v4.1.1' - - 'actions/configure-pages@v5.0.0' - - 'actions/deploy-pages@v4.0.5' - - 'actions/upload-pages-artifact@v3.0.1' - - 'haskell-actions/setup@v2.7.5' + - 'actions/cache@v6.1.0' + - 'actions/checkout@v7.0.1' + - 'actions/configure-pages@v6.0.0' + - 'actions/deploy-pages@v5.0.1' + - 'actions/upload-pages-artifact@v5.0.0' + - 'haskell-actions/setup@v2.12.1' '.github/workflows/chapel-ci.yml': - - 'actions/cache@v4.3.0' - - 'actions/checkout@v6.0.2' - - 'actions/download-artifact@v4.1.8' - - 'actions/upload-artifact@v4.6.2' + - 'actions/cache@v6.1.0' + - 'actions/checkout@v7.0.1' + - 'actions/download-artifact@v8.0.1' + - 'actions/upload-artifact@v7.0.1' - 'dtolnay/rust-toolchain@v1' '.github/workflows/codeql.yml': - 'actions/checkout@v7.0.1' - 'github/codeql-action@v4.38.2' '.github/workflows/coverage.yml': - - 'actions/checkout@v5.0.1' + - 'actions/checkout@v7.0.1' - 'dtolnay/rust-toolchain@v1' '.github/workflows/dependency-review.yml': - - 'actions/checkout@v5.0.1' - - 'actions/dependency-review-action@v4.3.4' + - 'actions/checkout@v7.0.1' + - 'actions/dependency-review-action@v5.0.0' '.github/workflows/dogfood-gate.yml': - - 'actions/checkout@v4.3.1' + - 'actions/checkout@v7.0.1' '.github/workflows/e2e.yml': - - 'actions/checkout@v4.3.1' - - 'actions/upload-artifact@v4.6.2' + - 'actions/checkout@v7.0.1' + - 'actions/upload-artifact@v7.0.1' - 'dtolnay/rust-toolchain@v1' - - 'swatinem/rust-cache@v2.8.2' - '.github/workflows/governance.yml': [] - '.github/workflows/hypatia-scan.yml': [] + - 'swatinem/rust-cache@v2.9.2' + '.github/workflows/governance.yml': + - 'hyperpolymath/standards@333b06a01db1874ffedb2de1bfabb195640927c7' + '.github/workflows/hypatia-scan.yml': + - 'hyperpolymath/standards@333b06a01db1874ffedb2de1bfabb195640927c7' '.github/workflows/instant-sync.yml': - 'peter-evans/repository-dispatch@v4.0.1' '.github/workflows/label-triage.yml': [] '.github/workflows/labels.yml': [] - '.github/workflows/mirror.yml': [] + '.github/workflows/mirror.yml': + - 'hyperpolymath/standards@333b06a01db1874ffedb2de1bfabb195640927c7' '.github/workflows/pages.yml': - - 'actions/checkout@v4.4.0' - - 'actions/deploy-pages@v4.0.5' - - 'actions/upload-pages-artifact@v3.0.1' + - 'actions/checkout@v7.0.1' + - 'actions/deploy-pages@v5.0.1' + - 'actions/upload-pages-artifact@v5.0.0' '.github/workflows/push-email-notify.yml': - - 'hyperpolymath/smtp-notify-action@v0.2.0' + - 'hyperpolymath/smtp-notify-action@v0.5.0' '.github/workflows/release.yml': - - 'actions/checkout@v5.0.1' - - 'actions/download-artifact@v4.1.8' - - 'actions/upload-artifact@v4.6.2' + - 'actions/checkout@v7.0.1' + - 'actions/download-artifact@v8.0.1' + - 'actions/upload-artifact@v7.0.1' - 'dtolnay/rust-toolchain@v1' - - 'softprops/action-gh-release@v2.5.0' - - 'swatinem/rust-cache@v2.8.2' - '.github/workflows/rust-ci.yml': [] + - 'slsa-framework/slsa-github-generator@f7dd8c54c2067bafc12ca7a55595d5ee9b75204a' + - 'softprops/action-gh-release@v3.0.3' + - 'swatinem/rust-cache@v2.9.2' + '.github/workflows/rust-ci.yml': + - 'hyperpolymath/standards@333b06a01db1874ffedb2de1bfabb195640927c7' '.github/workflows/scan-and-report.yml': - - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1' - - 'dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de' - 'Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6' - '.github/workflows/scorecard.yml': [] - '.github/workflows/secret-scanner.yml': [] + - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1' + - 'dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8' + '.github/workflows/scorecard.yml': + - 'hyperpolymath/standards@333b06a01db1874ffedb2de1bfabb195640927c7' + '.github/workflows/secret-scanner.yml': + - 'hyperpolymath/standards@333b06a01db1874ffedb2de1bfabb195640927c7' dependencies: - 'actions/cache@v4.3.0': - ref: 'v4.3.0' - commit: 'sha1-0057852bfaa89a56745cba8c7296529d2fc39830' + 'Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6': + ref: 'v2.9.2' + commit: 'sha1-6323deb102c322ba6fcbdcafc7e3dddab59af2b6' + owner_id: 580492 + repo_id: 298565987 + 'actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9': + ref: 'v6.1.0' + commit: 'sha1-55cc8345863c7cc4c66a329aec7e433d2d1c52a9' owner_id: 44036562 repo_id: 215566462 - 'actions/checkout@v4.1.1': - ref: 'v4.1.1' - commit: 'sha1-b4ffde65f46336ab88eb53be808477a3936bae11' - owner_id: 44036562 - repo_id: 197814629 - 'actions/checkout@v4.1.7': - ref: 'v4.1.7' - commit: 'sha1-692973e3d937129bcbf40652eb9f2f61becf3332' - owner_id: 44036562 - repo_id: 197814629 - 'actions/checkout@v4.3.1': - ref: 'v4.3.1' - commit: 'sha1-34e114876b0b11c390a56381ad16ebd13914f8d5' - owner_id: 44036562 - repo_id: 197814629 - 'actions/checkout@v4.4.0': - ref: 'v4.4.0' - commit: 'sha1-11d5960a326750d5838078e36cf38b85af677262' - owner_id: 44036562 - repo_id: 197814629 - 'actions/checkout@v5.0.1': - ref: 'v5.0.1' - commit: 'sha1-93cb6efe18208431cddfb8368fd83d5badbf9bfd' + 'actions/cache@v6.1.0': + ref: 'v6.1.0' + commit: 'sha1-55cc8345863c7cc4c66a329aec7e433d2d1c52a9' owner_id: 44036562 - repo_id: 197814629 - 'actions/checkout@v6.0.2': - ref: 'v6.0.2' - commit: 'sha1-de0fac2e4500dabe0009e67214ff5f5447ce83dd' + repo_id: 215566462 + 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1': + ref: 'v7.0.1' + commit: 'sha1-3d3c42e5aac5ba805825da76410c181273ba90b1' owner_id: 44036562 repo_id: 197814629 'actions/checkout@v7.0.1': @@ -107,133 +99,171 @@ dependencies: commit: 'sha1-3d3c42e5aac5ba805825da76410c181273ba90b1' owner_id: 44036562 repo_id: 197814629 - 'actions/configure-pages@v5.0.0': - ref: 'v5.0.0' - commit: 'sha1-983d7736d9b0ae728b81ab479565c72886d7745b' + 'actions/configure-pages@v6.0.0': + ref: 'v6.0.0' + commit: 'sha1-45bfe0192ca1faeb007ade9deae92b16b8254a0d' owner_id: 44036562 repo_id: 513659658 - 'actions/dependency-review-action@v4.3.4': - ref: 'v4.3.4' - commit: 'sha1-5a2ce3f5b92ee19cbb1541a4984c76d921601d7c' + 'actions/dependency-review-action@v5.0.0': + ref: 'v5.0.0' + commit: 'sha1-a1d282b36b6f3519aa1f3fc636f609c47dddb294' owner_id: 44036562 repo_id: 476372928 - 'actions/deploy-pages@v4.0.5': - ref: 'v4.0.5' - commit: 'sha1-d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e' + 'actions/deploy-pages@v5.0.1': + ref: 'v5.0.1' + commit: 'sha1-368f82528645a54fb793d4d04e342629a3f51346' owner_id: 44036562 repo_id: 438112499 - 'actions/download-artifact@v4.1.8': - ref: 'v4.1.8' - commit: 'sha1-fa0a91b85d4f404e444e00e005971372dc801d16' + 'actions/download-artifact@v8.0.1': + ref: 'v8.0.1' + commit: 'sha1-3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c' owner_id: 44036562 repo_id: 192626254 - 'actions/upload-artifact@v4': - ref: 'v4' - commit: 'sha1-ea165f8d65b6e75b540449e92b4886f43607fa02' + 'actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08': + ref: 'v4.6.0' + commit: 'sha1-65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08' owner_id: 44036562 repo_id: 192625955 - 'actions/upload-artifact@v4.6.2': - ref: 'v4.6.2' - commit: 'sha1-ea165f8d65b6e75b540449e92b4886f43607fa02' + 'actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f': + ref: 'v7.0.0' + commit: 'sha1-bbbca2ddaa5d8feaa63e36b76fdaad77386f024f' owner_id: 44036562 repo_id: 192625955 - 'actions/upload-pages-artifact@v3.0.1': - ref: 'v3.0.1' - commit: 'sha1-56afc609e74202658d3ffba0e8f6dda462b719fa' + 'actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9': + ref: 'v7.0.2' + commit: 'sha1-cf430e030ddbb5b0abf93d22962f4752f3646cd9' + owner_id: 44036562 + repo_id: 192625955 + 'actions/upload-artifact@v7.0.1': + ref: 'v7.0.1' + commit: 'sha1-043fb46d1a93c77aae656e7c1c64a875d1fc6a0a' + owner_id: 44036562 + repo_id: 192625955 + 'actions/upload-pages-artifact@v5.0.0': + ref: 'v5.0.0' + commit: 'sha1-fc324d3547104276b827a68afc52ff2a11cc49c9' owner_id: 44036562 repo_id: 496012378 uses: - - 'actions/upload-artifact@v4' + - 'actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f' + 'denoland/setup-deno@22d081ff2d3a40755e97629de92e3bcbfa7cf2ed': + ref: 'v2.0.5' + commit: 'sha1-22d081ff2d3a40755e97629de92e3bcbfa7cf2ed' + owner_id: 42048915 + repo_id: 356423100 + 'dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8': + ref: 'v1' + commit: 'sha1-683b588c487a69d01f6c6ddf40f036ff682cc2c8' + owner_id: 1940490 + repo_id: 260749683 'dtolnay/rust-toolchain@v1': ref: 'v1' - commit: 'sha1-02cb101ec7c40f2c49e1d9714d64511d8e1b74de' + commit: 'sha1-683b588c487a69d01f6c6ddf40f036ff682cc2c8' owner_id: 1940490 repo_id: 260749683 + 'editorconfig-checker/action-editorconfig-checker@51f63319f592f97930c73d9c46184d20bd206393': + ref: 'v3.0.0' + commit: 'sha1-51f63319f592f97930c73d9c46184d20bd206393' + owner_id: 26415196 + repo_id: 297874902 + 'erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124': + ref: 'v1.24.1' + commit: 'sha1-54075bcc5e249e4758d363f27d099f55d843f124' + owner_id: 47606891 + repo_id: 331103973 + 'github/codeql-action@b96794f015dfd88f77b49b1c93e0fa7110f94c63': + ref: 'v4.38.0' + commit: 'sha1-b96794f015dfd88f77b49b1c93e0fa7110f94c63' + owner_id: 9919 + repo_id: 259445878 'github/codeql-action@v4.38.2': ref: 'v4.38.2' commit: 'sha1-2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2' owner_id: 9919 repo_id: 259445878 - 'haskell-actions/setup@v2.7.5': - ref: 'v2.7.5' - commit: 'sha1-ec49483bfc012387b227434aba94f59a6ecd0900' + 'goto-bus-stop/setup-zig@abea47f85e598557f500fa1fd2ab7464fcb39406': + ref: 'v2.2.1' + commit: 'sha1-abea47f85e598557f500fa1fd2ab7464fcb39406' + owner_id: 1006268 + repo_id: 212984112 + 'haskell-actions/setup@v2.12.1': + ref: 'v2.12.1' + commit: 'sha1-0f8e8c99d88aeb3fbfd523f1ef2c6f762d10d64d' owner_id: 75048950 repo_id: 623796603 - 'hyperpolymath/smtp-notify-action@v0.2.0': - ref: 'v0.2.0' - commit: 'sha1-ede1191ef6ff3ac02c4f4d9efdf837ee517e11d7' + 'hyperpolymath/smtp-notify-action@v0.5.0': + ref: 'v0.5.0' + commit: 'sha1-c1c9fa07992a02c1fd3d67a0dc1b08cccb852aef' owner_id: 6759885 repo_id: 1352485172 + 'hyperpolymath/standards@333b06a01db1874ffedb2de1bfabb195640927c7': + ref: '333b06a01db1874ffedb2de1bfabb195640927c7' + commit: 'sha1-333b06a01db1874ffedb2de1bfabb195640927c7' + owner_id: 6759885 + repo_id: 1116521501 + uses: + - 'actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9' + - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1' + - 'actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9' + - 'denoland/setup-deno@22d081ff2d3a40755e97629de92e3bcbfa7cf2ed' + - 'dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8' + - 'editorconfig-checker/action-editorconfig-checker@51f63319f592f97930c73d9c46184d20bd206393' + - 'erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124' + - 'github/codeql-action@b96794f015dfd88f77b49b1c93e0fa7110f94c63' + - 'goto-bus-stop/setup-zig@abea47f85e598557f500fa1fd2ab7464fcb39406' + - 'ossf/scorecard-action@fa7117db83c52d787ae5a89f350999701505d78e' + - 'oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6' + - 'swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6' + - 'webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555' + 'ossf/scorecard-action@fa7117db83c52d787ae5a89f350999701505d78e': + ref: 'v2.5.0' + commit: 'sha1-fa7117db83c52d787ae5a89f350999701505d78e' + owner_id: 67707773 + repo_id: 421101922 + 'oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6': + ref: 'v2.2.0' + commit: 'sha1-0c5077e51419868618aeaa5fe8019c62421857d6' + owner_id: 108928776 + repo_id: 512644635 'peter-evans/repository-dispatch@v4.0.1': ref: 'v4.0.1' commit: 'sha1-28959ce8df70de7be546dd1250a005dd32156697' owner_id: 18365890 repo_id: 220359305 - 'softprops/action-gh-release@v2.5.0': - ref: 'v2.5.0' - commit: 'sha1-a06a81a03ee405af7f2048a818ed3f03bbf83c7b' + 'slsa-framework/slsa-github-generator@f7dd8c54c2067bafc12ca7a55595d5ee9b75204a': + ref: 'f7dd8c54c2067bafc12ca7a55595d5ee9b75204a' + commit: 'sha1-f7dd8c54c2067bafc12ca7a55595d5ee9b75204a' + owner_id: 80431187 + repo_id: 475074978 + uses: + - 'actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08' + - 'slsa-framework/slsa-github-generator@v2.1.0' + - 'softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda' + 'slsa-framework/slsa-github-generator@v2.1.0': + ref: 'v2.1.0' + commit: 'sha1-f7dd8c54c2067bafc12ca7a55595d5ee9b75204a' + owner_id: 80431187 + repo_id: 475074978 + 'softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda': + ref: 'v2.2.1' + commit: 'sha1-c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda' owner_id: 2242 repo_id: 204253808 - 'swatinem/rust-cache@v2.8.2': - ref: 'v2.8.2' - commit: 'sha1-779680da715d629ac1d338a641029a2f4372abb5' + 'softprops/action-gh-release@v3.0.3': + ref: 'v3.0.3' + commit: 'sha1-efb35369e0ad2afab669f228072c1b0d510eae64' + owner_id: 2242 + repo_id: 204253808 + 'swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6': + ref: 'v2.9.2' + commit: 'sha1-6323deb102c322ba6fcbdcafc7e3dddab59af2b6' owner_id: 580492 repo_id: 298565987 - 'Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6': - ref: 'v2' + 'swatinem/rust-cache@v2.9.2': + ref: 'v2.9.2' commit: 'sha1-6323deb102c322ba6fcbdcafc7e3dddab59af2b6' owner_id: 580492 repo_id: 298565987 - 'actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9': - ref: 'v6.1.0' - commit: 'sha1-55cc8345863c7cc4c66a329aec7e433d2d1c52a9' - owner_id: 44036562 - repo_id: 215566462 - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1': - ref: 'v7.0.1' - commit: 'sha1-3d3c42e5aac5ba805825da76410c181273ba90b1' - owner_id: 44036562 - repo_id: 197814629 - 'actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a': - ref: 'v7.0.1' - commit: 'sha1-043fb46d1a93c77aae656e7c1c64a875d1fc6a0a' - owner_id: 44036562 - repo_id: 192625955 - 'actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08': - ref: 'v4.6.0' - commit: 'sha1-65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08' - owner_id: 44036562 - repo_id: 192625955 - 'dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de': - ref: 'v1' - commit: 'sha1-02cb101ec7c40f2c49e1d9714d64511d8e1b74de' - owner_id: 1940490 - repo_id: 260749683 - 'dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772': - ref: 'stable' - commit: 'sha1-6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772' - owner_id: 1940490 - repo_id: 260749683 - 'editorconfig-checker/action-editorconfig-checker@840e866d93b8e032123c23bac69dece044d4d84c': - ref: 'v2.2.0' - commit: 'sha1-840e866d93b8e032123c23bac69dece044d4d84c' - owner_id: 26415196 - repo_id: 297874902 - 'erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124': - ref: 'v1.24.1' - commit: 'sha1-54075bcc5e249e4758d363f27d099f55d843f124' - owner_id: 47606891 - repo_id: 331103973 - 'ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc': - ref: 'v2.4.4' - commit: 'sha1-2d1146689b8cda280b9bc96326124645441f03bc' - owner_id: 67707773 - repo_id: 421101922 - 'softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda': - ref: 'v2.2.1' - commit: 'sha1-c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda' - owner_id: 2242 - repo_id: 204253808 'webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555': ref: 'v0.10.0' commit: 'sha1-e83874834305fe9a4a2997156cb26c5de65a8555' From f0da8ea6dc78d0a4c401f9501eeeda3ed85372bd Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 17:14:19 +0000 Subject: [PATCH 07/28] fix(ci): pass toolchain: stable, not v1, in e2e and chapel-ci 'v1' is the action's tag, not a rustup toolchain; rustup rejects it ('invalid toolchain name: v1'). Same fix the cargo-audit, coverage and release workflows already carry. Surfaced now that these workflows start. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019a95kk88TZ1iegsBK3kox3 --- .github/workflows/chapel-ci.yml | 2 +- .github/workflows/e2e.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/chapel-ci.yml b/.github/workflows/chapel-ci.yml index 534eed8..5399822 100644 --- a/.github/workflows/chapel-ci.yml +++ b/.github/workflows/chapel-ci.yml @@ -224,7 +224,7 @@ jobs: - uses: actions/checkout@v7.0.1 - uses: dtolnay/rust-toolchain@v1 with: - toolchain: v1 + toolchain: stable - uses: actions/cache@v6.1.0 with: path: | diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 6dc65e4..589c494 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -45,7 +45,7 @@ jobs: - name: Install Rust toolchain uses: dtolnay/rust-toolchain@v1 with: - toolchain: v1 + toolchain: stable - name: Rust cache uses: Swatinem/rust-cache@v2.9.2 From 334c41c5de91bec5ad39e8cfcf3f569cc1478a40 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 17:14:56 +0000 Subject: [PATCH 08/28] fix(ci): give every dtolnay/rust-toolchain@v1 step toolchain: stable The @v1 tag has no default toolchain; without the input the action runs 'rustup toolchain install' with an empty name and fails (readiness, bench, e2e-pipeline, chapel-rust-diff). Surfaced now that these workflows start. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019a95kk88TZ1iegsBK3kox3 --- .github/workflows/chapel-ci.yml | 2 ++ .github/workflows/e2e.yml | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/.github/workflows/chapel-ci.yml b/.github/workflows/chapel-ci.yml index 5399822..5d2fa6e 100644 --- a/.github/workflows/chapel-ci.yml +++ b/.github/workflows/chapel-ci.yml @@ -246,6 +246,8 @@ jobs: steps: - uses: actions/checkout@v7.0.1 - uses: dtolnay/rust-toolchain@v1 + with: + toolchain: stable - uses: actions/cache@v6.1.0 with: path: | diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 589c494..7b699ac 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -67,6 +67,8 @@ jobs: - name: Install Rust toolchain uses: dtolnay/rust-toolchain@v1 + with: + toolchain: stable - name: Rust cache uses: Swatinem/rust-cache@v2.9.2 @@ -131,6 +133,8 @@ jobs: - name: Install Rust toolchain uses: dtolnay/rust-toolchain@v1 + with: + toolchain: stable - name: Rust cache uses: Swatinem/rust-cache@v2.9.2 @@ -150,6 +154,8 @@ jobs: - name: Install Rust toolchain uses: dtolnay/rust-toolchain@v1 + with: + toolchain: stable - name: Rust cache uses: Swatinem/rust-cache@v2.9.2 From 7704c549137437b48cb2954b7a8f3c90544c6d0d Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 17:50:46 +0000 Subject: [PATCH 09/28] refactor(bin): use the library's modules instead of compiling them twice main.rs re-declared every module with `mod x;`, so the binary compiled its own private copy of the whole tree. Items used only through the library API (proven_tests, jit_context, comment_marker, ffi_kind) were then dead code inside the binary, which clippy -D warnings rejects. The binary now imports those modules from panic_attack; only the bin-only modules (diagnostics, groove, kin) stay as `mod`. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019a95kk88TZ1iegsBK3kox3 --- src/main.rs | 53 ++++++++++++++++++++++++----------------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/src/main.rs b/src/main.rs index 2929e0a..78bd1e3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,39 +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; -mod comment_marker; +use panic_attack::bridge; +use panic_attack::campaign; mod diagnostics; -mod ffi_kind; mod groove; -mod i18n; -mod jit_context; -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; From 5cdd1dfa8d4e8e03572f8ac4b0d28e0c5ecb416e Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 17:50:46 +0000 Subject: [PATCH 10/28] fix(lint): clear the clippy 1.99 -D warnings errors - drop redundant borrows in format! args (storage) - split on a char array instead of a closure (safe_path) - rfind / find / and_then instead of filter+last, filter+next, map+flatten - egui Panel/CentralPanel::show_inside -> show (a pure rename in 0.36) - is_safe_char / all_chars_safe are proptest-only helpers: #[cfg(test)] - ProvenTestsReport::from_str becomes a real FromStr impl - qualify anyhow! inside the sarif-gated parser so the import is not unused without the feature Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019a95kk88TZ1iegsBK3kox3 --- src/aggregate/proven_tests.rs | 9 +++++++-- src/assail/analyzer.rs | 3 +-- src/report/gui.rs | 6 +++--- src/safe_path.rs | 6 ++++-- src/scan/cargodeny.rs | 25 ++++++++++--------------- src/storage/mod.rs | 12 ++++++------ tests/readiness.rs | 3 +-- 7 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/aggregate/proven_tests.rs b/src/aggregate/proven_tests.rs index e12d807..21e6856 100644 --- a/src/aggregate/proven_tests.rs +++ b/src/aggregate/proven_tests.rs @@ -27,6 +27,7 @@ use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use std::collections::BTreeSet; use std::path::Path; +use std::str::FromStr; /// A cited proof step (only Actually-Proven entries carry these). #[derive(Debug, Clone, Serialize, Deserialize)] @@ -115,9 +116,11 @@ impl ReportTier { } } -impl ProvenTestsReport { +impl FromStr for ProvenTestsReport { + type Err = anyhow::Error; + /// Parse a run report from JSON text. - pub fn from_str(text: &str) -> Result { + fn from_str(text: &str) -> Result { let report: ProvenTestsReport = serde_json::from_str(text).context("parsing proven-tests run report")?; if report.schema_version != 1 { @@ -128,7 +131,9 @@ impl ProvenTestsReport { } Ok(report) } +} +impl ProvenTestsReport { /// Parse a run report from a file path. pub fn from_path(path: &Path) -> Result { let text = diff --git a/src/assail/analyzer.rs b/src/assail/analyzer.rs index 7aab8fb..1fccd24 100644 --- a/src/assail/analyzer.rs +++ b/src/assail/analyzer.rs @@ -285,8 +285,7 @@ fn c_alloc_result_is_null_checked(lines: &[&str], idx: usize) -> bool { .next() .and_then(|lhs| { lhs.split(|c: char| !c.is_alphanumeric() && c != '_') - .filter(|s| !s.is_empty()) - .last() + .rfind(|s| !s.is_empty()) }) .map(str::to_string) } else { diff --git a/src/report/gui.rs b/src/report/gui.rs index 014c3fa..9502c4c 100644 --- a/src/report/gui.rs +++ b/src/report/gui.rs @@ -121,7 +121,7 @@ impl ReportGui { impl App for ReportGui { 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 +138,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 +159,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 e79b525..ec06273 100644 --- a/src/safe_path.rs +++ b/src/safe_path.rs @@ -62,7 +62,7 @@ const SAFE_PUNCT: &[char] = &['-', '_', '.', '~']; /// assert!(!has_traversal("foo..bar")); // .. inside a name is not a segment /// ``` pub fn has_traversal(path: &str) -> bool { - for segment in path.split(|c| c == '/' || c == '\\') { + for segment in path.split(['/', '\\']) { if segment == ".." { return true; } @@ -100,11 +100,13 @@ 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 { @@ -288,7 +290,7 @@ mod tests { #[test] fn prop_has_traversal_operational(s in ".*") { let split_says: bool = s - .split(|c| c == '/' || c == '\\') + .split(['/', '\\']) .any(|seg| seg == ".."); prop_assert_eq!( has_traversal(&s), diff --git a/src/scan/cargodeny.rs b/src/scan/cargodeny.rs index 5ebcb0b..7e9a89b 100644 --- a/src/scan/cargodeny.rs +++ b/src/scan/cargodeny.rs @@ -8,7 +8,7 @@ // feedback_no_automated_licence_edits. use crate::types::{Severity, WeakPoint, WeakPointCategory}; -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use std::path::{Path, PathBuf}; use std::process::Command; @@ -173,18 +173,13 @@ pub fn run_cargo_deny(config: &CargoDenyConfig) -> Result> { .map(|c| code_to_category(&c)) .unwrap_or(WeakPointCategory::Advisory); - let location = diagnostic - .location - .map(|loc| { - if let (Some(ref file), Some(line)) = (&loc.file, loc.line) { - Some(format!("{}:{}", file, line)) - } else if let Some(ref file) = loc.file { - Some(file.clone()) - } else { - None - } - }) - .flatten(); + let location = diagnostic.location.and_then(|loc| { + 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 let (file, line) = if let Some(ref loc) = location { @@ -272,13 +267,13 @@ pub fn parse_sarif_output(sarif_json: &str) -> Result> { // SARIF structure: { runs: [{ results: [{ ... }] }] } let runs = v["runs"] .as_array() - .ok_or_else(|| anyhow!("no runs in SARIF"))?; + .ok_or_else(|| anyhow::anyhow!("no runs in SARIF"))?; let mut weak_points = Vec::new(); for run in runs { let results = run["results"] .as_array() - .ok_or_else(|| anyhow!("no results in SARIF run"))?; + .ok_or_else(|| anyhow::anyhow!("no results in SARIF run"))?; for result in results { let message = result["message"]["text"] .as_str() diff --git a/src/storage/mod.rs b/src/storage/mod.rs index 2c4fbd1..1c551ba 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -235,7 +235,7 @@ fn build_hexad(report: &AssaultReport) -> Result { let id = format!( "pa-{}-{}", now.format("%Y%m%d%H%M%S"), - &uuid_from_timestamp(now.timestamp_millis()) + uuid_from_timestamp(now.timestamp_millis()) ); let critical_count = report @@ -382,7 +382,7 @@ fn build_assemblyline_hexad( let id = format!( "pa-asmline-{}-{}", now.format("%Y%m%d%H%M%S"), - &uuid_from_timestamp(now.timestamp_millis()) + uuid_from_timestamp(now.timestamp_millis()) ); let document = serde_json::to_value(report)?; @@ -510,7 +510,7 @@ pub fn build_finding_hexads( let run_id = format!( "pa-asmline-{}-{}", now.format("%Y%m%d%H%M%S"), - &uuid_from_timestamp(now.timestamp_millis()) + uuid_from_timestamp(now.timestamp_millis()) ); let mut hexads = Vec::new(); @@ -540,7 +540,7 @@ pub fn build_finding_hexads( now.format("%Y%m%d%H%M%S"), repo_idx, wp_idx, - &uuid_from_timestamp(now.timestamp_millis()), + uuid_from_timestamp(now.timestamp_millis()), ); let document = serde_json::json!({ @@ -691,7 +691,7 @@ pub fn build_crosslang_hexads( run_ts, repo_idx, int_idx, - &uuid_from_timestamp(now.timestamp_millis()), + uuid_from_timestamp(now.timestamp_millis()), ); let document = serde_json::json!({ @@ -780,7 +780,7 @@ pub fn build_campaign_hexad(semantic: CampaignSemantic) -> PanicAttackHexad { let hexad_id = format!( "pa-campaign-{}-{}", now.format("%Y%m%d%H%M%S"), - &uuid_from_timestamp(now.timestamp_millis()) + uuid_from_timestamp(now.timestamp_millis()) ); PanicAttackHexad { diff --git a/tests/readiness.rs b/tests/readiness.rs index a9b9afd..727bc4e 100644 --- a/tests/readiness.rs +++ b/tests/readiness.rs @@ -319,8 +319,7 @@ fn cfg_test_panic() { clean_report .weak_points .iter() - .filter(|wp| wp.category == WeakPointCategory::PanicPath && !wp.suppressed) - .next() + .find(|wp| wp.category == WeakPointCategory::PanicPath && !wp.suppressed) .is_none(), "excluded panic contexts must produce zero active findings" ); From 01dd90ffd3741a77b64845d9cb867b03ecbfa147 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 17:52:31 +0000 Subject: [PATCH 11/28] fix(diagnostics): look for the .adoc docs, not the retired .md names docs/ambush-timeline.md and docs/panll-export.md were converted to .adoc (estate docs policy) but the self-diagnostics check still demanded the .md files, so `panic-attack diagnostics` exited non-zero and both diagnostics readiness tests failed. Point the check, README.adoc and the bridge module docs at the .adoc files that exist. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019a95kk88TZ1iegsBK3kox3 --- README.adoc | 4 ++-- src/bridge/mod.rs | 2 +- src/bridge/registry.rs | 2 +- src/diagnostics.rs | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) 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/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 18e004e..5a18891 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -41,11 +41,11 @@ pub fn run_self_diagnostics(manifest: &Manifest) -> Result<()> { checks.push(check_verisimdb(Path::new("verisimdb-data/verisimdb"))); checks.push(check_file( "ambush timeline spec", - Path::new("docs/ambush-timeline.md"), + Path::new("docs/ambush-timeline.adoc"), )); checks.push(check_file( "panll export guide", - Path::new("docs/panll-export.md"), + Path::new("docs/panll-export.adoc"), )); checks.push(check_watcher("Hypatia scanner", HYPATIA_ENV)); From 0e6febbd0ed51cd25c0c0574181a3a82e21295b5 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Mon, 19 Oct 2026 17:58:28 +0000 Subject: [PATCH 12/28] docs: document analyzer heuristics, suppression rules, and API behavior --- src/aggregate/mod.rs | 12 ++++++++++++ src/assail/analyzer.rs | 24 ++++++++++++++++++++++++ src/assail/mod.rs | 11 ++++++----- src/assay/mod.rs | 19 +++++++++++++++++++ src/diagnostics.rs | 5 ++++- src/ffi_kind.rs | 9 ++++++--- src/jit_context.rs | 8 +++++--- src/main.rs | 7 +++++++ src/scan/cargodeny.rs | 25 ++++++++++++++++++++++--- 9 files changed, 105 insertions(+), 15 deletions(-) diff --git a/src/aggregate/mod.rs b/src/aggregate/mod.rs index e4eaff3..a30ee67 100644 --- a/src/aggregate/mod.rs +++ b/src/aggregate/mod.rs @@ -394,6 +394,11 @@ pub fn run(config: AggregateConfig) -> Result { }) } +/// Write the aggregate report as pretty-printed JSON, creating parent directories +/// and replacing any existing file at `path`. +/// +/// # Errors +/// Returns directory creation, JSON serialisation and file write errors. pub fn write_report(report: &AggregateReport, path: &Path) -> Result<()> { if let Some(parent) = path.parent() { if !parent.as_os_str().is_empty() { @@ -493,6 +498,8 @@ fn has_metavar_hole(text: &str) -> bool { false } +/// Look for case-sensitive proof or declaration substrings as a closure heuristic; +/// a match does not verify that the text contains a valid proof. fn has_proof_keyword(text: &str) -> bool { const KW: &[&str] = &[ "theorem", "lemma", "Theorem", "Lemma", "Proof", "proof", "where", ":=", "data ", "record ", @@ -606,6 +613,10 @@ fn scan_covers(text: &str) -> Vec { /// Parse `[claim:]kind:value`, e.g. `sound:category:UnsafeCode`, /// `safe:file:src/foo.rs`, `category:PanicPath`, `free:my-lemma`. +/// +/// A bare token other than `safe` or `sound` becomes free-form coverage with an +/// unspecified claim. Returns `None` for an empty first field, an unrecognised +/// kind or an unsupported field layout. Values are retained without trimming. pub fn parse_coverage_spec(spec: &str) -> Option { let mut parts: Vec<&str> = spec.splitn(3, ':').collect(); if parts.is_empty() || parts[0].is_empty() { @@ -692,6 +703,7 @@ fn summarise(findings: &[FindingLite]) -> FindingSummary { } } +/// Count findings selected by the coverage annotation; free-form coverage matches none. fn count_matching(findings: &[FindingLite], cov: &Coverage) -> usize { findings.iter().filter(|f| matches_coverage(f, cov)).count() } diff --git a/src/assail/analyzer.rs b/src/assail/analyzer.rs index 7aab8fb..bc69fc4 100644 --- a/src/assail/analyzer.rs +++ b/src/assail/analyzer.rs @@ -942,6 +942,10 @@ impl Analyzer { // Original language analyzers // ============================================================ + /// Accumulate Rust statistics and append heuristic findings for `file_path`. + /// Dangerous-pattern checks exclude string contents, comments, `cfg`-selected + /// test bodies and `should_panic` bodies. Panic counts and findings also exclude + /// test paths and build scripts. fn analyze_rust( &self, content: &str, @@ -1768,6 +1772,9 @@ impl Analyzer { false } + /// Accumulate C/C++ statistics and append heuristic findings for `file_path`. + /// Unchecked malloc findings include one-based line numbers for suppression + /// and omit allocations with a recognised nearby null check. fn analyze_c_cpp( &self, content: &str, @@ -2013,6 +2020,8 @@ impl Analyzer { Ok(()) } + /// Accumulate Go statistics and append heuristic concurrency, unsafe pointer, + /// command execution and cryptography findings located at `file_path`. fn analyze_go( &self, content: &str, @@ -2142,6 +2151,8 @@ impl Analyzer { Ok(()) } + /// Accumulate Python I/O and threading counts and append heuristic loop, + /// code execution, deserialisation, command and cryptography findings for `file_path`. fn analyze_python( &self, content: &str, @@ -3169,6 +3180,9 @@ impl Analyzer { } } + /// Accumulate OCaml statistics and append heuristic findings for `file_path`. + /// Classify `Obj.magic` as proof drift when Coq extraction markers are present, + /// or as unsafe type coercion otherwise. fn analyze_ocaml( &self, content: &str, @@ -4321,6 +4335,8 @@ impl Analyzer { Ok(()) } + /// Accumulate Odin operation counts and append a Medium unsafe-code finding + /// at `file_path` when `rawptr` occurs in the content. fn analyze_odin( &self, content: &str, @@ -4499,6 +4515,9 @@ impl Analyzer { // Config languages (Nickel, Nix) // ============================================================ + /// Accumulate Nix/Nickel I/O counts and append Nix command and unpinned-flake findings. + /// Uses `file_path` for file-type checks, finding locations and the sibling + /// `flake.lock` lookup on disk. fn analyze_config( &self, content: &str, @@ -4603,6 +4622,9 @@ impl Analyzer { // Shell scripting // ============================================================ + /// Accumulate shell I/O counts and append heuristic command, permission and + /// temporary-path findings for `file_path`. Unquoted expansion findings require + /// more than 20 `$VAR` matches after quoted string contents are removed. fn analyze_shell( &self, content: &str, @@ -5036,6 +5058,8 @@ impl Analyzer { // Cross-language security checks (run on ALL files) // ============================================================ + /// Append heuristic protocol, hardcoded secret and excessive comment-marker + /// findings located at `file_path`, independently of the detected language. fn analyze_cross_language( &self, content: &str, diff --git a/src/assail/mod.rs b/src/assail/mod.rs index 5ebb810..7ec19ad 100644 --- a/src/assail/mod.rs +++ b/src/assail/mod.rs @@ -402,21 +402,22 @@ pub fn apply_suppression(report: &mut AssailReport) { report.suppressed_count = count; } -/// v2.5.5 context-aware FP suppression pass — applies four checks per +/// v2.5.5 context-aware FP suppression pass — applies three checks per /// finding: /// 1. Inline `panic-attack: accepted` marker on the same or -/// preceding line ([[comment_marker]]). +/// preceding line, when `wp.line` is known ([[comment_marker]]). /// 2. PanicPath finding in a TestOnly or Doc test_context /// ([[test_context]]). /// 3. UnsafeFFI finding where the file's [[ffi_kind]] is /// `BuildSystem` or `TestMock` (audit-accepted by default). -/// 4. (jit_context wire-up handled inline in analyzer.rs:1117..1129 -/// already; not duplicated here.) /// -/// Sets `wp.test_context` for every finding with a known file path so +/// JIT context handling remains inline in analyzer.rs:1117..1129. +/// +/// Fills missing `wp.test_context` for findings with a known file path so /// downstream audit consumers can render it. Returns the number of /// findings newly flipped from `suppressed=false` to `suppressed=true` /// (already-suppressed findings are NOT re-counted). +/// File read errors disable marker checks and use path-only context classification. pub fn apply_v255_context_suppression(report: &mut AssailReport) -> usize { use crate::comment_marker; use crate::ffi_kind::FfiKind; diff --git a/src/assay/mod.rs b/src/assay/mod.rs index d79a4b8..4cd34f0 100644 --- a/src/assay/mod.rs +++ b/src/assay/mod.rs @@ -277,6 +277,11 @@ pub fn run(config: AssayConfig) -> Result { }) } +/// Write the assay report as pretty-printed JSON, creating parent directories +/// and replacing any existing file at `path`. +/// +/// # Errors +/// Returns directory creation, JSON serialisation and file write errors. pub fn write_report(report: &AssayReport, path: &Path) -> Result<()> { if let Some(parent) = path.parent() { if !parent.as_os_str().is_empty() { @@ -348,6 +353,20 @@ pub struct AssimilateConfig { pub dry_run: bool, } +/// Apply selected proven-module substitutions and return provenance records and notes. +/// +/// `apply_all` selects offered candidates; otherwise `candidate_id` is required. +/// Already-applied candidates are skipped. The `from` override requires exactly +/// one selected candidate. Call sites are returned for manual rewiring. +/// +/// Unless `dry_run` is set, copy replacement files, preserve existing `.orig` +/// backups and write provenance under the target's `.assimilated/` directory. +/// Dry runs still resolve and hash sources and existing destination files. +/// +/// # Errors +/// Returns survey errors, invalid candidate or source selection errors, and errors +/// from hashing, directory creation, file copying or provenance serialisation/writing. +/// Earlier file changes remain in place if a later operation fails. pub fn assimilate(config: AssimilateConfig) -> Result { let assay = run(AssayConfig { target: config.target.clone(), diff --git a/src/diagnostics.rs b/src/diagnostics.rs index 18e004e..76da97e 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -193,8 +193,11 @@ fn check_watcher(label: &'static str, env_key: &str) -> Diagnostic { /// Verifies: /// 1. Bot directives file exists at `.machine_readable/bot_directives/panicbot.scm` /// 2. JSON output contract: AssailReport serialises with the fields panicbot expects -/// (`program_path`, `weak_points`, `language`, `statistics`) +/// (`program_path`, `weak_points`, `language`) /// 3. WeakPointCategory and Severity serialise in PascalCase (no serde rename_all) +/// +/// Failed JSON checks, including serialisation errors, yield an error diagnostic; +/// missing directives alone yield a warning. fn check_panicbot_readiness() -> Diagnostic { let directive_path = Path::new(PANICBOT_DIRECTIVE_PATH); let has_directives = directive_path.is_file(); diff --git a/src/ffi_kind.rs b/src/ffi_kind.rs index 9efafb5..79874e4 100644 --- a/src/ffi_kind.rs +++ b/src/ffi_kind.rs @@ -87,6 +87,7 @@ fn is_build_system_path(p: &str) -> bool { || p.contains("build.cargo/") } +/// Recognise test mock paths by case-sensitive test-scope and mock/stub substrings. fn is_test_mock_path(p: &str) -> bool { // Test mocks live under tests/ AND have "mock" or "stub" in the // path. Plain test files are still RuntimeAbi if they call the @@ -120,9 +121,11 @@ fn is_runtime_abi_path(p: &str) -> bool { /// project's pre-approved FFI boundary audit (typically /// `audits/audit-ffi-unsafe.md`). /// -/// Implementation note: this is a placeholder that parses the audit -/// file when present and returns false otherwise. Wire-in to the -/// analyzer happens in the follow-up PR. +/// Reads the supplied `audit_text` without accessing the filesystem. Section +/// headings `Approved boundaries`, `Approved FFI boundaries` and `Pre-approved +/// boundaries` are recognised case-insensitively at Markdown level two. Within +/// them, the first token after an optional list marker is compared exactly with +/// `file_path`, ignoring surrounding backticks. Returns false if no token matches. pub fn is_audited_boundary(audit_text: &str, file_path: &str) -> bool { // The audit file's convention is a `## Approved boundaries` section // with a markdown list of `- file/path/here.zig — rationale` lines. diff --git a/src/jit_context.rs b/src/jit_context.rs index 383b1f3..d165441 100644 --- a/src/jit_context.rs +++ b/src/jit_context.rs @@ -81,6 +81,7 @@ pub fn classify_rust(content: &str) -> JitContext { } } +/// Recognise Cranelift imports or JIT API usage by case-sensitive substring matches. fn is_cranelift(content: &str) -> bool { content.contains("cranelift_jit::JITModule") || content.contains("cranelift_module::Module") @@ -113,9 +114,10 @@ fn is_javascript_embedding(content: &str) -> bool { || (content.contains("v8::") && content.contains("Function::call")) } -/// True if a `mem::transmute` (or `transmute_copy`) in this content -/// targets a function-pointer type. Pattern-matches the standard -/// turbofish forms and let-binding forms documented in +/// Look for `transmute` syntax suggesting a function-pointer target. +/// Recognises selected turbofish forms or a function-pointer annotation occurring +/// anywhere alongside a `transmute(` call, without verifying they belong to the +/// same expression. `transmute_copy` is not recognised. See the related forms in /// `src/assail/analyzer.rs:1110..1129`. pub fn transmute_targets_fn_ptr(content: &str) -> bool { // Turbofish forms — unambiguous. diff --git a/src/main.rs b/src/main.rs index 2929e0a..93079c8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1370,6 +1370,13 @@ fn main() -> Result<()> { result } +/// Parse process arguments and execute the selected command, including its output +/// and requested persistence. Default manifest settings are used if loading fails. +/// Argument parsing exits the process for help or invalid CLI syntax. +/// +/// # Errors +/// Returns command validation errors and propagates I/O, serialisation and +/// operation errors that are not handled by a command-specific fallback. fn run_main() -> Result<()> { let cli = Cli::parse(); let manifest = match Manifest::load_default() { diff --git a/src/scan/cargodeny.rs b/src/scan/cargodeny.rs index 5ebcb0b..01d2673 100644 --- a/src/scan/cargodeny.rs +++ b/src/scan/cargodeny.rs @@ -112,7 +112,15 @@ fn code_to_category(code: &str) -> WeakPointCategory { } } -/// Run cargo-deny check and parse output +/// Run `cargo deny check` in the configured workspace and return its diagnostic findings. +/// +/// Standard output must be UTF-8 JSON matching `DenyOutput`, regardless of +/// `config.format`. A non-zero exit status still yields findings if parsing succeeds. +/// Missing severity and code fields default to Medium and Advisory respectively. +/// `config.deny_toml` is currently ignored. +/// +/// # Errors +/// Returns process execution errors, invalid UTF-8 errors and JSON deserialisation errors. pub fn run_cargo_deny(config: &CargoDenyConfig) -> Result> { let workspace_root = &config.workspace_root; @@ -262,7 +270,13 @@ impl From for CargoDenyWeakPoint { } } -/// Parse cargo-deny SARIF output (alternative to JSON) +/// Convert cargo-deny SARIF results into findings, using only the first location +/// of each result. Missing levels default to Medium severity; missing or +/// unrecognised rule IDs default to Advisory. +/// +/// # Errors +/// Returns JSON parsing errors, or an error if `runs` or any run's `results` is +/// absent or is not an array. Empty `runs` and `results` arrays are valid. #[cfg(feature = "sarif")] pub fn parse_sarif_output(sarif_json: &str) -> Result> { use serde_json::Value; @@ -349,7 +363,12 @@ pub fn parse_sarif_output(sarif_json: &str) -> Result> { Ok(weak_points) } -/// High-level function to scan a workspace and emit cargo-deny weak points +/// Scan a workspace with cargo-deny's JSON defaults and return diagnostic findings. +/// The `deny_toml` argument currently has no effect. +/// +/// # Errors +/// Propagates execution, UTF-8 decoding and JSON deserialisation errors from +/// [`run_cargo_deny`]. A non-zero cargo-deny exit status alone is not an error. pub fn scan_workspace(workspace_root: &Path, deny_toml: Option<&Path>) -> Result> { let config = CargoDenyConfig { workspace_root: workspace_root.to_path_buf(), From 09f8fc86180e1d1b6641f02f34a8f131866a3a1e Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 17:58:34 +0000 Subject: [PATCH 13/28] fix(deps): bump crossbeam-epoch, rustls, webbrowser, memmap2, event-listener, wayland-scanner Lockfile-only, semver-compatible updates clearing RUSTSEC-2026-0204 (crossbeam-epoch), -0346/0347/0348/0349 (rustls 0.23.46), -0257 (webbrowser), the event-listener and memmap2 unsound notices, and moving wayland-scanner onto quick-xml 0.41. quick-xml 0.38 remains only via zbus_xml 5 under accesskit (eframe's GUI feature); no released accesskit or atspi-common moves off it yet. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019a95kk88TZ1iegsBK3kox3 --- Cargo.lock | 68 ++++++++++++++++++++++++++---------------------------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 41664e2..fdeaef1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -847,7 +847,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]] @@ -900,16 +900,6 @@ dependencies = [ "libc", ] -[[package]] -name = "core-foundation" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -923,7 +913,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [ "bitflags 1.3.2", - "core-foundation 0.9.4", + "core-foundation", "core-graphics-types", "foreign-types", "libc", @@ -936,7 +926,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [ "bitflags 1.3.2", - "core-foundation 0.9.4", + "core-foundation", "libc", ] @@ -1011,9 +1001,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.18" +version = "0.9.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "dc74980687109a3b14c72fd458107bf0baa1da1a1a805e178d15501ba9b86d9d" dependencies = [ "crossbeam-utils", ] @@ -1150,7 +1140,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -1453,7 +1443,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]] @@ -1473,11 +1463,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", ] @@ -2308,9 +2297,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", ] @@ -3203,6 +3192,15 @@ dependencies = [ "serde", ] +[[package]] +name = "quick-xml" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.45" @@ -3448,14 +3446,14 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] name = "rustls" -version = "0.23.45" +version = "0.23.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634" +checksum = "48e13bd8c0e9365c43cfa5c9e8f9ad49d3c8444926c9aac819e0e4dc503c8fdf" dependencies = [ "log", "once_cell", @@ -3890,7 +3888,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix 1.1.4", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -4443,12 +4441,12 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.8" +version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5423e94b6a63e68e439803a3e153a9252d5ead12fd853334e2ad33997e3889e3" +checksum = "338e30461b3a2b67d70eb30a6d89f8e0c93a833e07d2ae89085cd070c4a00ac0" dependencies = [ "proc-macro2", - "quick-xml", + "quick-xml 0.41.0", "quote", ] @@ -4486,15 +4484,15 @@ dependencies = [ [[package]] name = "webbrowser" -version = "1.2.0" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe985f41e291eecef5e5c0770a18d28390addb03331c043964d9e916453d6f16" +checksum = "62c35be770821a214dbc362fc26908c853e776c0004294d0b10b8a6bad582f94" dependencies = [ - "core-foundation 0.10.1", "jni", "log", "ndk-context", "objc2 0.6.4", + "objc2-app-kit 0.3.2", "objc2-foundation 0.3.2", "url", "web-sys", @@ -4720,7 +4718,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]] @@ -4945,7 +4943,7 @@ dependencies = [ "calloop 0.13.0", "cfg_aliases", "concurrent-queue", - "core-foundation 0.9.4", + "core-foundation", "core-graphics", "cursor-icon", "dpi", @@ -5180,7 +5178,7 @@ version = "5.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "441a0064125265655bccc3a6af6bef56814d9277ac83fce48b1cd7e160b80eac" dependencies = [ - "quick-xml", + "quick-xml 0.38.4", "serde", "zbus_names", "zvariant", From e274017cecdd51c7bacabcdbbf1b54e58368b31e Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 18:07:24 +0000 Subject: [PATCH 14/28] fix(ci): pass reusable workflows only the secrets they declare mirror-reusable declares seven optional mirror keys; secret-scanner-reusable declares none and its header says callers pass no secrets: line. Replace secrets: inherit with the explicit mapping (mirror) and nothing (scanner) so unrelated repo/org secrets are not forwarded. Flagged by zizmor secrets-inherit via CodeRabbit. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019a95kk88TZ1iegsBK3kox3 --- .github/workflows/mirror.yml | 9 ++++++++- .github/workflows/secret-scanner.yml | 1 - 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/mirror.yml b/.github/workflows/mirror.yml index e4e4c3f..7c43488 100644 --- a/.github/workflows/mirror.yml +++ b/.github/workflows/mirror.yml @@ -16,4 +16,11 @@ permissions: jobs: mirror: uses: hyperpolymath/standards/.github/workflows/mirror-reusable.yml@333b06a01db1874ffedb2de1bfabb195640927c7 - secrets: inherit + secrets: + GITLAB_SSH_KEY: ${{ secrets.GITLAB_SSH_KEY }} + BITBUCKET_SSH_KEY: ${{ secrets.BITBUCKET_SSH_KEY }} + CODEBERG_SSH_KEY: ${{ secrets.CODEBERG_SSH_KEY }} + SOURCEHUT_SSH_KEY: ${{ secrets.SOURCEHUT_SSH_KEY }} + DISROOT_SSH_KEY: ${{ secrets.DISROOT_SSH_KEY }} + GITEA_SSH_KEY: ${{ secrets.GITEA_SSH_KEY }} + RADICLE_KEY: ${{ secrets.RADICLE_KEY }} diff --git a/.github/workflows/secret-scanner.yml b/.github/workflows/secret-scanner.yml index e366d02..c839b81 100644 --- a/.github/workflows/secret-scanner.yml +++ b/.github/workflows/secret-scanner.yml @@ -22,4 +22,3 @@ jobs: permissions: contents: read uses: hyperpolymath/standards/.github/workflows/secret-scanner-reusable.yml@333b06a01db1874ffedb2de1bfabb195640927c7 - secrets: inherit From 2da14ab96cdbfa6b77022a23eb26ecd001820bc4 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 18:30:35 +0000 Subject: [PATCH 15/28] chore(k9): retire the k9iser output and manifest generated/k9iser/cargo-manifest.k9 and container-build.k9 are k9iser output in a [must]/[trust] section format that predates K9 contract v1.0.0 (D173): no K9! envelope, no pedigree, not a Nickel component. The Validate K9 contracts gate rejects both, nothing in this repository reads them, echidna already gitignores the same directory, and no k9iser repository exists in the estate any more. The two gate messages that told people to run k9iser now point at the K9 contract spec. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_019a95kk88TZ1iegsBK3kox3 --- generated/k9iser/cargo-manifest.k9 | 19 -------- generated/k9iser/container-build.k9 | 28 ------------ k9iser.toml | 70 ----------------------------- 3 files changed, 117 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/generated/k9iser/cargo-manifest.k9 b/generated/k9iser/cargo-manifest.k9 deleted file mode 100644 index ad723ab..0000000 --- a/generated/k9iser/cargo-manifest.k9 +++ /dev/null @@ -1,19 +0,0 @@ -# Auto-generated K9 contract for cargo-manifest -# Safety tier: hunt - -[must] -package.name : string { == 'panic-attack' } -package.edition : string { == '2021' } -profile.release.codegen-units : string { == 1 } -profile.release.strip : string { == 'symbols' } -profile.release.panic : string { == 'unwind' } - -[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 0f79c65..0000000 --- a/generated/k9iser/container-build.k9 +++ /dev/null @@ -1,28 +0,0 @@ -# Auto-generated K9 contract for container-build -# Safety tier: hunt - -[must] -metadata.license : string { == 'MPL-2.0' } -metadata.registry : string { == 'ghcr.io/hyperpolymath' } -build.runtime : string { == 'podman' } -layers.base.verify : bool { == true } -layers.runtime.user : string { == 'nonroot' } -security.non-root : bool { == true } -security.no-new-privileges : bool { == true } -security.signing.algorithm : string { == 'ML-DSA-87' } -security.signing.provider : string { == 'cerro-torre' } -security.sbom.format : string { == 'spdx-json' } -security.sbom.include-deps : bool { == true } -verify.vordr : bool { == true } -verify.svalinn : bool { == true } -verify.scan-on-build : bool { == true } - -[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" From afd3da71a4b7cd51159c54c2c9f911f132f4f319 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Mon, 19 Oct 2026 21:16:45 +0000 Subject: [PATCH 16/28] fix: resolve Rust lint warnings, correct diagnostic doc paths, and update K9 contracts and dependencies --- Cargo.lock | 121 ++++++++++++++++------------ README.adoc | 2 +- generated/k9iser/cargo-manifest.k9 | 72 +++++++++++++---- generated/k9iser/container-build.k9 | 86 ++++++++++++++------ src/aggregate/mod.rs | 2 + src/aggregate/proven_tests.rs | 19 ++++- src/assail/analyzer.rs | 3 +- src/diagnostics.rs | 4 +- src/main.rs | 5 +- src/safe_path.rs | 6 +- src/scan/cargodeny.rs | 22 ++--- src/storage/mod.rs | 12 +-- tests/readiness.rs | 3 +- 13 files changed, 228 insertions(+), 129 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b918de3..fbde510 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.52.0", ] [[package]] @@ -939,16 +939,6 @@ dependencies = [ "libc", ] -[[package]] -name = "core-foundation" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -962,7 +952,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [ "bitflags 1.3.2", - "core-foundation 0.9.4", + "core-foundation", "core-graphics-types", "foreign-types", "libc", @@ -975,7 +965,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [ "bitflags 1.3.2", - "core-foundation 0.9.4", + "core-foundation", "libc", ] @@ -1050,9 +1040,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.18" +version = "0.9.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "dc74980687109a3b14c72fd458107bf0baa1da1a1a805e178d15501ba9b86d9d" dependencies = [ "crossbeam-utils", ] @@ -1189,7 +1179,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -1492,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.52.0", ] [[package]] @@ -3227,12 +3217,11 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quick-xml" -version = "0.38.4" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" +checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1" dependencies = [ "memchr", - "serde", ] [[package]] @@ -3467,14 +3456,14 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] name = "rustls" -version = "0.23.45" +version = "0.23.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634" +checksum = "48e13bd8c0e9365c43cfa5c9e8f9ad49d3c8444926c9aac819e0e4dc503c8fdf" dependencies = [ "log", "once_cell", @@ -3909,7 +3898,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix 1.1.4", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -4038,7 +4027,7 @@ dependencies = [ "indexmap", "toml_datetime", "toml_parser", - "winnow", + "winnow 0.7.14", ] [[package]] @@ -4047,7 +4036,7 @@ version = "1.0.7+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "247eaa3197818b831697600aadf81514e577e0cba5eab10f7e064e78ae154df1" dependencies = [ - "winnow", + "winnow 0.7.14", ] [[package]] @@ -4462,9 +4451,9 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.8" +version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5423e94b6a63e68e439803a3e153a9252d5ead12fd853334e2ad33997e3889e3" +checksum = "338e30461b3a2b67d70eb30a6d89f8e0c93a833e07d2ae89085cd070c4a00ac0" dependencies = [ "proc-macro2", "quick-xml", @@ -4505,15 +4494,15 @@ dependencies = [ [[package]] name = "webbrowser" -version = "1.2.0" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe985f41e291eecef5e5c0770a18d28390addb03331c043964d9e916453d6f16" +checksum = "62c35be770821a214dbc362fc26908c853e776c0004294d0b10b8a6bad582f94" dependencies = [ - "core-foundation 0.10.1", "jni", "log", "ndk-context", "objc2 0.6.4", + "objc2-app-kit 0.3.2", "objc2-foundation 0.3.2", "url", "web-sys", @@ -4739,7 +4728,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.52.0", ] [[package]] @@ -4964,7 +4953,7 @@ dependencies = [ "calloop 0.13.0", "cfg_aliases", "concurrent-queue", - "core-foundation 0.9.4", + "core-foundation", "core-graphics", "cursor-icon", "dpi", @@ -5010,6 +4999,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" +dependencies = [ + "memchr", +] + [[package]] name = "wit-bindgen" version = "0.51.0" @@ -5137,7 +5135,7 @@ dependencies = [ "uds_windows", "uuid", "windows-sys 0.61.2", - "winnow", + "winnow 0.7.14", "zbus_macros", "zbus_names", "zvariant", @@ -5179,32 +5177,41 @@ dependencies = [ "syn 2.0.117", "zbus_names", "zvariant", - "zvariant_utils", + "zvariant_utils 3.3.0", ] [[package]] name = "zbus_names" -version = "4.3.1" +version = "4.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" +checksum = "812a12be4ead7a59e18ea8f047d08add0066b73bc62fc1467066a2472ae088dd" dependencies = [ "serde", - "winnow", + "winnow 1.0.4", "zvariant", ] [[package]] name = "zbus_xml" -version = "5.1.0" +version = "5.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "441a0064125265655bccc3a6af6bef56814d9277ac83fce48b1cd7e160b80eac" +checksum = "d1586c021a01ca0a9216dcd874e546382e156a5cbab5fab6cb5f10087e22682a" dependencies = [ - "quick-xml", "serde", + "winnow 1.0.4", "zbus_names", "zvariant", ] +[[package]] +name = "zcheapstr" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1afec51604565183aeb5c54c20aeab286120d4e4460f7f76e3e8bb8c0d99473" +dependencies = [ + "serde", +] + [[package]] name = "zerocopy" version = "0.8.39" @@ -5308,29 +5315,30 @@ dependencies = [ [[package]] name = "zvariant" -version = "5.10.0" +version = "5.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5708299b21903bbe348e94729f22c49c55d04720a004aa350f1f9c122fd2540b" +checksum = "80bf6d6cd6a4b601acb64cfb8284cdc643fcfa75ef26ae3c46ec8aa4e8a1fbb7" dependencies = [ "endi", "enumflags2", "serde", - "winnow", + "winnow 1.0.4", + "zcheapstr", "zvariant_derive", - "zvariant_utils", + "zvariant_utils 4.2.0", ] [[package]] name = "zvariant_derive" -version = "5.10.0" +version = "5.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b59b012ebe9c46656f9cc08d8da8b4c726510aef12559da3e5f1bf72780752c" +checksum = "ef2672456ba1cb54b5eaec136e63135b3c7495fb938e53b28a62e633496d486d" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.117", - "zvariant_utils", + "syn 3.0.3", + "zvariant_utils 4.2.0", ] [[package]] @@ -5343,5 +5351,18 @@ dependencies = [ "quote", "serde", "syn 2.0.117", - "winnow", + "winnow 0.7.14", +] + +[[package]] +name = "zvariant_utils" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad0294361a320b694a328460dc73add56c306150f5cb6bfafc44446120008a3" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn 3.0.3", + "winnow 1.0.4", ] diff --git a/README.adoc b/README.adoc index c7a3502..8c73639 100644 --- a/README.adoc +++ b/README.adoc @@ -183,7 +183,7 @@ 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.md` 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. diff --git a/generated/k9iser/cargo-manifest.k9 b/generated/k9iser/cargo-manifest.k9 index ad723ab..87c835d 100644 --- a/generated/k9iser/cargo-manifest.k9 +++ b/generated/k9iser/cargo-manifest.k9 @@ -1,19 +1,57 @@ -# Auto-generated K9 contract for cargo-manifest -# Safety tier: hunt +K9! +# SPDX-License-Identifier: MPL-2.0 +# Configuration contract derived from k9iser.toml. +# Keep the requirements below in sync with that source manifest. +{ + pedigree = { + schema_version = "1.0.0", + component_type = "configuration-contract", + security = { + leash = 'Hunt, + signature_required = true, + allow_network = false, + allow_filesystem_write = false, + allow_subprocess = false, + }, + metadata = { + name = "cargo-manifest", + version = "1.0.0", + description = "Requirements for Cargo.toml", + }, + side_effects = ["Validation reads the source configuration; no deployment is performed."], + }, -[must] -package.name : string { == 'panic-attack' } -package.edition : string { == '2021' } -profile.release.codegen-units : string { == 1 } -profile.release.strip : string { == 'symbols' } -profile.release.panic : string { == 'unwind' } + config = { + source = "Cargo.toml", + format = "toml", + }, -[trust] -signed-by = "ci-pipeline" - -[dust] -remove = ["unused optional features"] - -[intend] -production-ready = true -security-critical = true + # Declarative requirements; a signature is required before Hunt execution. + validation = { + 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", + "security-critical", + ], + }, +} diff --git a/generated/k9iser/container-build.k9 b/generated/k9iser/container-build.k9 index 0f79c65..28507d1 100644 --- a/generated/k9iser/container-build.k9 +++ b/generated/k9iser/container-build.k9 @@ -1,28 +1,62 @@ -# Auto-generated K9 contract for container-build -# Safety tier: hunt +K9! +# SPDX-License-Identifier: MPL-2.0 +# Configuration contract derived from k9iser.toml. +# Keep the requirements below in sync with that source manifest. +{ + pedigree = { + schema_version = "1.0.0", + component_type = "configuration-contract", + security = { + leash = 'Hunt, + signature_required = true, + allow_network = false, + allow_filesystem_write = false, + allow_subprocess = false, + }, + metadata = { + name = "container-build", + version = "1.0.0", + description = "Requirements for stapeln.toml", + }, + side_effects = ["Validation reads the source configuration; no deployment is performed."], + }, -[must] -metadata.license : string { == 'MPL-2.0' } -metadata.registry : string { == 'ghcr.io/hyperpolymath' } -build.runtime : string { == 'podman' } -layers.base.verify : bool { == true } -layers.runtime.user : string { == 'nonroot' } -security.non-root : bool { == true } -security.no-new-privileges : bool { == true } -security.signing.algorithm : string { == 'ML-DSA-87' } -security.signing.provider : string { == 'cerro-torre' } -security.sbom.format : string { == 'spdx-json' } -security.sbom.include-deps : bool { == true } -verify.vordr : bool { == true } -verify.svalinn : bool { == true } -verify.scan-on-build : bool { == true } + config = { + source = "stapeln.toml", + format = "toml", + }, -[trust] -signed-by = "ci-pipeline" - -[dust] -remove = ["deprecated layer definitions"] - -[intend] -production-ready = true -supply-chain-verified = true + # Declarative requirements; a signature is required before Hunt execution. + validation = { + 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", + "supply-chain-verified", + ], + }, +} diff --git a/src/aggregate/mod.rs b/src/aggregate/mod.rs index a30ee67..4a23f4f 100644 --- a/src/aggregate/mod.rs +++ b/src/aggregate/mod.rs @@ -51,6 +51,8 @@ use std::io::Read; use std::path::{Path, PathBuf}; /// Ingest a `proven-tests` JSON run report as an aggregate input kind. +// This public library API is not yet used by the CLI's copy of this module. +#[allow(dead_code)] pub mod proven_tests; /// Upper bound on the text we scan for verdict / coverage classification. diff --git a/src/aggregate/proven_tests.rs b/src/aggregate/proven_tests.rs index e12d807..acd78a9 100644 --- a/src/aggregate/proven_tests.rs +++ b/src/aggregate/proven_tests.rs @@ -115,9 +115,11 @@ impl ReportTier { } } -impl ProvenTestsReport { +impl std::str::FromStr for ProvenTestsReport { + type Err = anyhow::Error; + /// Parse a run report from JSON text. - pub fn from_str(text: &str) -> Result { + fn from_str(text: &str) -> Result { let report: ProvenTestsReport = serde_json::from_str(text).context("parsing proven-tests run report")?; if report.schema_version != 1 { @@ -128,12 +130,21 @@ impl ProvenTestsReport { } Ok(report) } +} + +impl ProvenTestsReport { + /// Parse a run report from JSON text. + // Preserve the existing constructor alongside the standard parsing trait. + #[allow(clippy::should_implement_trait)] + pub fn from_str(text: &str) -> Result { + text.parse() + } /// Parse a run report from a file path. pub fn from_path(path: &Path) -> Result { let text = std::fs::read_to_string(path).with_context(|| format!("reading {}", path.display()))?; - Self::from_str(&text) + text.parse() } /// The distinct `.idr` proof-ladder files cited by Actually-Proven entries. @@ -210,7 +221,7 @@ mod tests { #[test] fn parses_schema_v1() { - let r = ProvenTestsReport::from_str(SAMPLE).expect("parse"); + let r: ProvenTestsReport = SAMPLE.parse().expect("parse"); assert_eq!(r.schema_version, 1); assert_eq!(r.entries.len(), 3); assert_eq!(r.summary.actually_proven, 1); diff --git a/src/assail/analyzer.rs b/src/assail/analyzer.rs index bc69fc4..1b75185 100644 --- a/src/assail/analyzer.rs +++ b/src/assail/analyzer.rs @@ -285,8 +285,7 @@ fn c_alloc_result_is_null_checked(lines: &[&str], idx: usize) -> bool { .next() .and_then(|lhs| { lhs.split(|c: char| !c.is_alphanumeric() && c != '_') - .filter(|s| !s.is_empty()) - .last() + .rfind(|s| !s.is_empty()) }) .map(str::to_string) } else { diff --git a/src/diagnostics.rs b/src/diagnostics.rs index 76da97e..f89ec6b 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -41,11 +41,11 @@ pub fn run_self_diagnostics(manifest: &Manifest) -> Result<()> { checks.push(check_verisimdb(Path::new("verisimdb-data/verisimdb"))); checks.push(check_file( "ambush timeline spec", - Path::new("docs/ambush-timeline.md"), + Path::new("docs/ambush-timeline.adoc"), )); checks.push(check_file( "panll export guide", - Path::new("docs/panll-export.md"), + Path::new("docs/panll-export.adoc"), )); checks.push(check_watcher("Hypatia scanner", HYPATIA_ENV)); diff --git a/src/main.rs b/src/main.rs index 93079c8..abefcc2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,12 +22,11 @@ mod axial; #[cfg(feature = "http")] mod bridge; mod campaign; -mod comment_marker; +use panic_attack::comment_marker; mod diagnostics; -mod ffi_kind; +use panic_attack::ffi_kind; mod groove; mod i18n; -mod jit_context; mod kanren; mod kin; mod mass_panic; diff --git a/src/safe_path.rs b/src/safe_path.rs index e79b525..f5d63a4 100644 --- a/src/safe_path.rs +++ b/src/safe_path.rs @@ -62,7 +62,7 @@ const SAFE_PUNCT: &[char] = &['-', '_', '.', '~']; /// assert!(!has_traversal("foo..bar")); // .. inside a name is not a segment /// ``` pub fn has_traversal(path: &str) -> bool { - for segment in path.split(|c| c == '/' || c == '\\') { + for segment in path.split(['/', '\\']) { if segment == ".." { return true; } @@ -101,12 +101,14 @@ pub fn sanitize_filename(filename: &str) -> String { } /// Returns true if `c` is a character `sanitize_filename` would keep. +#[cfg(test)] fn is_safe_char(c: char) -> bool { c.is_ascii_alphanumeric() || SAFE_PUNCT.contains(&c) } /// Returns true if every character in `s` is safe. Used by the /// idempotence + non-empty proptest invariants. +#[cfg(test)] fn all_chars_safe(s: &str) -> bool { s.chars().all(is_safe_char) } @@ -288,7 +290,7 @@ mod tests { #[test] fn prop_has_traversal_operational(s in ".*") { let split_says: bool = s - .split(|c| c == '/' || c == '\\') + .split(['/', '\\']) .any(|seg| seg == ".."); prop_assert_eq!( has_traversal(&s), diff --git a/src/scan/cargodeny.rs b/src/scan/cargodeny.rs index 01d2673..59ee87e 100644 --- a/src/scan/cargodeny.rs +++ b/src/scan/cargodeny.rs @@ -8,7 +8,7 @@ // feedback_no_automated_licence_edits. use crate::types::{Severity, WeakPoint, WeakPointCategory}; -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use std::path::{Path, PathBuf}; use std::process::Command; @@ -181,18 +181,12 @@ pub fn run_cargo_deny(config: &CargoDenyConfig) -> Result> { .map(|c| code_to_category(&c)) .unwrap_or(WeakPointCategory::Advisory); - let location = diagnostic - .location - .map(|loc| { - if let (Some(ref file), Some(line)) = (&loc.file, loc.line) { - Some(format!("{}:{}", file, line)) - } else if let Some(ref file) = loc.file { - Some(file.clone()) - } else { - None - } + let location = diagnostic.location.and_then(|loc| { + loc.file.map(|file| match loc.line { + Some(line) => format!("{}:{}", file, line), + None => file, }) - .flatten(); + }); // Parse file and line from location string let (file, line) = if let Some(ref loc) = location { @@ -286,13 +280,13 @@ pub fn parse_sarif_output(sarif_json: &str) -> Result> { // SARIF structure: { runs: [{ results: [{ ... }] }] } let runs = v["runs"] .as_array() - .ok_or_else(|| anyhow!("no runs in SARIF"))?; + .ok_or_else(|| anyhow::anyhow!("no runs in SARIF"))?; let mut weak_points = Vec::new(); for run in runs { let results = run["results"] .as_array() - .ok_or_else(|| anyhow!("no results in SARIF run"))?; + .ok_or_else(|| anyhow::anyhow!("no results in SARIF run"))?; for result in results { let message = result["message"]["text"] .as_str() diff --git a/src/storage/mod.rs b/src/storage/mod.rs index 2c4fbd1..1c551ba 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -235,7 +235,7 @@ fn build_hexad(report: &AssaultReport) -> Result { let id = format!( "pa-{}-{}", now.format("%Y%m%d%H%M%S"), - &uuid_from_timestamp(now.timestamp_millis()) + uuid_from_timestamp(now.timestamp_millis()) ); let critical_count = report @@ -382,7 +382,7 @@ fn build_assemblyline_hexad( let id = format!( "pa-asmline-{}-{}", now.format("%Y%m%d%H%M%S"), - &uuid_from_timestamp(now.timestamp_millis()) + uuid_from_timestamp(now.timestamp_millis()) ); let document = serde_json::to_value(report)?; @@ -510,7 +510,7 @@ pub fn build_finding_hexads( let run_id = format!( "pa-asmline-{}-{}", now.format("%Y%m%d%H%M%S"), - &uuid_from_timestamp(now.timestamp_millis()) + uuid_from_timestamp(now.timestamp_millis()) ); let mut hexads = Vec::new(); @@ -540,7 +540,7 @@ pub fn build_finding_hexads( now.format("%Y%m%d%H%M%S"), repo_idx, wp_idx, - &uuid_from_timestamp(now.timestamp_millis()), + uuid_from_timestamp(now.timestamp_millis()), ); let document = serde_json::json!({ @@ -691,7 +691,7 @@ pub fn build_crosslang_hexads( run_ts, repo_idx, int_idx, - &uuid_from_timestamp(now.timestamp_millis()), + uuid_from_timestamp(now.timestamp_millis()), ); let document = serde_json::json!({ @@ -780,7 +780,7 @@ pub fn build_campaign_hexad(semantic: CampaignSemantic) -> PanicAttackHexad { let hexad_id = format!( "pa-campaign-{}-{}", now.format("%Y%m%d%H%M%S"), - &uuid_from_timestamp(now.timestamp_millis()) + uuid_from_timestamp(now.timestamp_millis()) ); PanicAttackHexad { diff --git a/tests/readiness.rs b/tests/readiness.rs index a9b9afd..727bc4e 100644 --- a/tests/readiness.rs +++ b/tests/readiness.rs @@ -319,8 +319,7 @@ fn cfg_test_panic() { clean_report .weak_points .iter() - .filter(|wp| wp.category == WeakPointCategory::PanicPath && !wp.suppressed) - .next() + .find(|wp| wp.category == WeakPointCategory::PanicPath && !wp.suppressed) .is_none(), "excluded panic contexts must produce zero active findings" ); From 09a5857c3843d3e0f834e1a9eb49eb19223619fd Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 21:30:29 +0000 Subject: [PATCH 17/28] Revert "fix: resolve Rust lint warnings, correct diagnostic doc paths, and update K9 contracts and dependencies" This reverts commit afd3da71a4b7cd51159c54c2c9f911f132f4f319, pushed by CodeRabbit's CI-fix agent. This PR is the lock repair at the base of a stack; the clippy fixes, the readiness doc paths and the K9 retirement are the next three PRs (#220, #221, #223), and a second copy here would conflict with them. The commit also rewrote generated/k9iser/*.k9 to make them conform, which the owner has ruled the other way: k9iser is retired and #223 deletes those files. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_019a95kk88TZ1iegsBK3kox3 --- Cargo.lock | 121 ++++++++++++---------------- README.adoc | 2 +- generated/k9iser/cargo-manifest.k9 | 72 ++++------------- generated/k9iser/container-build.k9 | 86 ++++++-------------- src/aggregate/mod.rs | 2 - src/aggregate/proven_tests.rs | 19 +---- src/assail/analyzer.rs | 3 +- src/diagnostics.rs | 4 +- src/main.rs | 5 +- src/safe_path.rs | 6 +- src/scan/cargodeny.rs | 22 +++-- src/storage/mod.rs | 12 +-- tests/readiness.rs | 3 +- 13 files changed, 129 insertions(+), 228 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fbde510..b918de3 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.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -939,6 +939,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -952,7 +962,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", "core-graphics-types", "foreign-types", "libc", @@ -965,7 +975,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", "libc", ] @@ -1040,9 +1050,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.21" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc74980687109a3b14c72fd458107bf0baa1da1a1a805e178d15501ba9b86d9d" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ "crossbeam-utils", ] @@ -1179,7 +1189,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1482,7 +1492,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3217,11 +3227,12 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quick-xml" -version = "0.41.0" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1" +checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" dependencies = [ "memchr", + "serde", ] [[package]] @@ -3456,14 +3467,14 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "rustls" -version = "0.23.46" +version = "0.23.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e13bd8c0e9365c43cfa5c9e8f9ad49d3c8444926c9aac819e0e4dc503c8fdf" +checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634" dependencies = [ "log", "once_cell", @@ -3898,7 +3909,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix 1.1.4", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4027,7 +4038,7 @@ dependencies = [ "indexmap", "toml_datetime", "toml_parser", - "winnow 0.7.14", + "winnow", ] [[package]] @@ -4036,7 +4047,7 @@ version = "1.0.7+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "247eaa3197818b831697600aadf81514e577e0cba5eab10f7e064e78ae154df1" dependencies = [ - "winnow 0.7.14", + "winnow", ] [[package]] @@ -4451,9 +4462,9 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.11" +version = "0.31.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "338e30461b3a2b67d70eb30a6d89f8e0c93a833e07d2ae89085cd070c4a00ac0" +checksum = "5423e94b6a63e68e439803a3e153a9252d5ead12fd853334e2ad33997e3889e3" dependencies = [ "proc-macro2", "quick-xml", @@ -4494,15 +4505,15 @@ dependencies = [ [[package]] name = "webbrowser" -version = "1.2.4" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62c35be770821a214dbc362fc26908c853e776c0004294d0b10b8a6bad582f94" +checksum = "fe985f41e291eecef5e5c0770a18d28390addb03331c043964d9e916453d6f16" dependencies = [ + "core-foundation 0.10.1", "jni", "log", "ndk-context", "objc2 0.6.4", - "objc2-app-kit 0.3.2", "objc2-foundation 0.3.2", "url", "web-sys", @@ -4728,7 +4739,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4953,7 +4964,7 @@ dependencies = [ "calloop 0.13.0", "cfg_aliases", "concurrent-queue", - "core-foundation", + "core-foundation 0.9.4", "core-graphics", "cursor-icon", "dpi", @@ -4999,15 +5010,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winnow" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" -dependencies = [ - "memchr", -] - [[package]] name = "wit-bindgen" version = "0.51.0" @@ -5135,7 +5137,7 @@ dependencies = [ "uds_windows", "uuid", "windows-sys 0.61.2", - "winnow 0.7.14", + "winnow", "zbus_macros", "zbus_names", "zvariant", @@ -5177,41 +5179,32 @@ dependencies = [ "syn 2.0.117", "zbus_names", "zvariant", - "zvariant_utils 3.3.0", + "zvariant_utils", ] [[package]] name = "zbus_names" -version = "4.3.5" +version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812a12be4ead7a59e18ea8f047d08add0066b73bc62fc1467066a2472ae088dd" +checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" dependencies = [ "serde", - "winnow 1.0.4", + "winnow", "zvariant", ] [[package]] name = "zbus_xml" -version = "5.2.1" +version = "5.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1586c021a01ca0a9216dcd874e546382e156a5cbab5fab6cb5f10087e22682a" +checksum = "441a0064125265655bccc3a6af6bef56814d9277ac83fce48b1cd7e160b80eac" dependencies = [ + "quick-xml", "serde", - "winnow 1.0.4", "zbus_names", "zvariant", ] -[[package]] -name = "zcheapstr" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1afec51604565183aeb5c54c20aeab286120d4e4460f7f76e3e8bb8c0d99473" -dependencies = [ - "serde", -] - [[package]] name = "zerocopy" version = "0.8.39" @@ -5315,30 +5308,29 @@ dependencies = [ [[package]] name = "zvariant" -version = "5.15.1" +version = "5.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80bf6d6cd6a4b601acb64cfb8284cdc643fcfa75ef26ae3c46ec8aa4e8a1fbb7" +checksum = "5708299b21903bbe348e94729f22c49c55d04720a004aa350f1f9c122fd2540b" dependencies = [ "endi", "enumflags2", "serde", - "winnow 1.0.4", - "zcheapstr", + "winnow", "zvariant_derive", - "zvariant_utils 4.2.0", + "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "5.15.1" +version = "5.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2672456ba1cb54b5eaec136e63135b3c7495fb938e53b28a62e633496d486d" +checksum = "5b59b012ebe9c46656f9cc08d8da8b4c726510aef12559da3e5f1bf72780752c" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 3.0.3", - "zvariant_utils 4.2.0", + "syn 2.0.117", + "zvariant_utils", ] [[package]] @@ -5351,18 +5343,5 @@ dependencies = [ "quote", "serde", "syn 2.0.117", - "winnow 0.7.14", -] - -[[package]] -name = "zvariant_utils" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad0294361a320b694a328460dc73add56c306150f5cb6bfafc44446120008a3" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "syn 3.0.3", - "winnow 1.0.4", + "winnow", ] diff --git a/README.adoc b/README.adoc index 8c73639..c7a3502 100644 --- a/README.adoc +++ b/README.adoc @@ -183,7 +183,7 @@ 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.adoc`, `docs/panll-export.adoc`). `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.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. `just test-elixir` runs the Elixir harnesses that cover the CLI surface so documentation, help text, and automation flags stay aligned. diff --git a/generated/k9iser/cargo-manifest.k9 b/generated/k9iser/cargo-manifest.k9 index 87c835d..ad723ab 100644 --- a/generated/k9iser/cargo-manifest.k9 +++ b/generated/k9iser/cargo-manifest.k9 @@ -1,57 +1,19 @@ -K9! -# SPDX-License-Identifier: MPL-2.0 -# Configuration contract derived from k9iser.toml. -# Keep the requirements below in sync with that source manifest. -{ - pedigree = { - schema_version = "1.0.0", - component_type = "configuration-contract", - security = { - leash = 'Hunt, - signature_required = true, - allow_network = false, - allow_filesystem_write = false, - allow_subprocess = false, - }, - metadata = { - name = "cargo-manifest", - version = "1.0.0", - description = "Requirements for Cargo.toml", - }, - side_effects = ["Validation reads the source configuration; no deployment is performed."], - }, +# Auto-generated K9 contract for cargo-manifest +# Safety tier: hunt - config = { - source = "Cargo.toml", - format = "toml", - }, +[must] +package.name : string { == 'panic-attack' } +package.edition : string { == '2021' } +profile.release.codegen-units : string { == 1 } +profile.release.strip : string { == 'symbols' } +profile.release.panic : string { == 'unwind' } - # Declarative requirements; a signature is required before Hunt execution. - validation = { - 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", - "security-critical", - ], - }, -} +[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 index 28507d1..0f79c65 100644 --- a/generated/k9iser/container-build.k9 +++ b/generated/k9iser/container-build.k9 @@ -1,62 +1,28 @@ -K9! -# SPDX-License-Identifier: MPL-2.0 -# Configuration contract derived from k9iser.toml. -# Keep the requirements below in sync with that source manifest. -{ - pedigree = { - schema_version = "1.0.0", - component_type = "configuration-contract", - security = { - leash = 'Hunt, - signature_required = true, - allow_network = false, - allow_filesystem_write = false, - allow_subprocess = false, - }, - metadata = { - name = "container-build", - version = "1.0.0", - description = "Requirements for stapeln.toml", - }, - side_effects = ["Validation reads the source configuration; no deployment is performed."], - }, +# Auto-generated K9 contract for container-build +# Safety tier: hunt - config = { - source = "stapeln.toml", - format = "toml", - }, +[must] +metadata.license : string { == 'MPL-2.0' } +metadata.registry : string { == 'ghcr.io/hyperpolymath' } +build.runtime : string { == 'podman' } +layers.base.verify : bool { == true } +layers.runtime.user : string { == 'nonroot' } +security.non-root : bool { == true } +security.no-new-privileges : bool { == true } +security.signing.algorithm : string { == 'ML-DSA-87' } +security.signing.provider : string { == 'cerro-torre' } +security.sbom.format : string { == 'spdx-json' } +security.sbom.include-deps : bool { == true } +verify.vordr : bool { == true } +verify.svalinn : bool { == true } +verify.scan-on-build : bool { == true } - # Declarative requirements; a signature is required before Hunt execution. - validation = { - 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", - "supply-chain-verified", - ], - }, -} +[trust] +signed-by = "ci-pipeline" + +[dust] +remove = ["deprecated layer definitions"] + +[intend] +production-ready = true +supply-chain-verified = true diff --git a/src/aggregate/mod.rs b/src/aggregate/mod.rs index 4a23f4f..a30ee67 100644 --- a/src/aggregate/mod.rs +++ b/src/aggregate/mod.rs @@ -51,8 +51,6 @@ use std::io::Read; use std::path::{Path, PathBuf}; /// Ingest a `proven-tests` JSON run report as an aggregate input kind. -// This public library API is not yet used by the CLI's copy of this module. -#[allow(dead_code)] pub mod proven_tests; /// Upper bound on the text we scan for verdict / coverage classification. diff --git a/src/aggregate/proven_tests.rs b/src/aggregate/proven_tests.rs index acd78a9..e12d807 100644 --- a/src/aggregate/proven_tests.rs +++ b/src/aggregate/proven_tests.rs @@ -115,11 +115,9 @@ impl ReportTier { } } -impl std::str::FromStr for ProvenTestsReport { - type Err = anyhow::Error; - +impl ProvenTestsReport { /// Parse a run report from JSON text. - fn from_str(text: &str) -> Result { + pub fn from_str(text: &str) -> Result { let report: ProvenTestsReport = serde_json::from_str(text).context("parsing proven-tests run report")?; if report.schema_version != 1 { @@ -130,21 +128,12 @@ impl std::str::FromStr for ProvenTestsReport { } Ok(report) } -} - -impl ProvenTestsReport { - /// Parse a run report from JSON text. - // Preserve the existing constructor alongside the standard parsing trait. - #[allow(clippy::should_implement_trait)] - pub fn from_str(text: &str) -> Result { - text.parse() - } /// Parse a run report from a file path. pub fn from_path(path: &Path) -> Result { let text = std::fs::read_to_string(path).with_context(|| format!("reading {}", path.display()))?; - text.parse() + Self::from_str(&text) } /// The distinct `.idr` proof-ladder files cited by Actually-Proven entries. @@ -221,7 +210,7 @@ mod tests { #[test] fn parses_schema_v1() { - let r: ProvenTestsReport = SAMPLE.parse().expect("parse"); + let r = ProvenTestsReport::from_str(SAMPLE).expect("parse"); assert_eq!(r.schema_version, 1); assert_eq!(r.entries.len(), 3); assert_eq!(r.summary.actually_proven, 1); diff --git a/src/assail/analyzer.rs b/src/assail/analyzer.rs index 1b75185..bc69fc4 100644 --- a/src/assail/analyzer.rs +++ b/src/assail/analyzer.rs @@ -285,7 +285,8 @@ fn c_alloc_result_is_null_checked(lines: &[&str], idx: usize) -> bool { .next() .and_then(|lhs| { lhs.split(|c: char| !c.is_alphanumeric() && c != '_') - .rfind(|s| !s.is_empty()) + .filter(|s| !s.is_empty()) + .last() }) .map(str::to_string) } else { diff --git a/src/diagnostics.rs b/src/diagnostics.rs index f89ec6b..76da97e 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -41,11 +41,11 @@ pub fn run_self_diagnostics(manifest: &Manifest) -> Result<()> { checks.push(check_verisimdb(Path::new("verisimdb-data/verisimdb"))); checks.push(check_file( "ambush timeline spec", - Path::new("docs/ambush-timeline.adoc"), + Path::new("docs/ambush-timeline.md"), )); checks.push(check_file( "panll export guide", - Path::new("docs/panll-export.adoc"), + Path::new("docs/panll-export.md"), )); checks.push(check_watcher("Hypatia scanner", HYPATIA_ENV)); diff --git a/src/main.rs b/src/main.rs index abefcc2..93079c8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,11 +22,12 @@ mod axial; #[cfg(feature = "http")] mod bridge; mod campaign; -use panic_attack::comment_marker; +mod comment_marker; mod diagnostics; -use panic_attack::ffi_kind; +mod ffi_kind; mod groove; mod i18n; +mod jit_context; mod kanren; mod kin; mod mass_panic; diff --git a/src/safe_path.rs b/src/safe_path.rs index f5d63a4..e79b525 100644 --- a/src/safe_path.rs +++ b/src/safe_path.rs @@ -62,7 +62,7 @@ const SAFE_PUNCT: &[char] = &['-', '_', '.', '~']; /// assert!(!has_traversal("foo..bar")); // .. inside a name is not a segment /// ``` pub fn has_traversal(path: &str) -> bool { - for segment in path.split(['/', '\\']) { + for segment in path.split(|c| c == '/' || c == '\\') { if segment == ".." { return true; } @@ -101,14 +101,12 @@ pub fn sanitize_filename(filename: &str) -> String { } /// Returns true if `c` is a character `sanitize_filename` would keep. -#[cfg(test)] fn is_safe_char(c: char) -> bool { c.is_ascii_alphanumeric() || SAFE_PUNCT.contains(&c) } /// Returns true if every character in `s` is safe. Used by the /// idempotence + non-empty proptest invariants. -#[cfg(test)] fn all_chars_safe(s: &str) -> bool { s.chars().all(is_safe_char) } @@ -290,7 +288,7 @@ mod tests { #[test] fn prop_has_traversal_operational(s in ".*") { let split_says: bool = s - .split(['/', '\\']) + .split(|c| c == '/' || c == '\\') .any(|seg| seg == ".."); prop_assert_eq!( has_traversal(&s), diff --git a/src/scan/cargodeny.rs b/src/scan/cargodeny.rs index 59ee87e..01d2673 100644 --- a/src/scan/cargodeny.rs +++ b/src/scan/cargodeny.rs @@ -8,7 +8,7 @@ // feedback_no_automated_licence_edits. use crate::types::{Severity, WeakPoint, WeakPointCategory}; -use anyhow::{Context, Result}; +use anyhow::{anyhow, Context, Result}; use serde::{Deserialize, Serialize}; use std::path::{Path, PathBuf}; use std::process::Command; @@ -181,12 +181,18 @@ pub fn run_cargo_deny(config: &CargoDenyConfig) -> Result> { .map(|c| code_to_category(&c)) .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, + let location = diagnostic + .location + .map(|loc| { + if let (Some(ref file), Some(line)) = (&loc.file, loc.line) { + Some(format!("{}:{}", file, line)) + } else if let Some(ref file) = loc.file { + Some(file.clone()) + } else { + None + } }) - }); + .flatten(); // Parse file and line from location string let (file, line) = if let Some(ref loc) = location { @@ -280,13 +286,13 @@ pub fn parse_sarif_output(sarif_json: &str) -> Result> { // SARIF structure: { runs: [{ results: [{ ... }] }] } let runs = v["runs"] .as_array() - .ok_or_else(|| anyhow::anyhow!("no runs in SARIF"))?; + .ok_or_else(|| anyhow!("no runs in SARIF"))?; let mut weak_points = Vec::new(); for run in runs { let results = run["results"] .as_array() - .ok_or_else(|| anyhow::anyhow!("no results in SARIF run"))?; + .ok_or_else(|| anyhow!("no results in SARIF run"))?; for result in results { let message = result["message"]["text"] .as_str() diff --git a/src/storage/mod.rs b/src/storage/mod.rs index 1c551ba..2c4fbd1 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -235,7 +235,7 @@ fn build_hexad(report: &AssaultReport) -> Result { let id = format!( "pa-{}-{}", now.format("%Y%m%d%H%M%S"), - uuid_from_timestamp(now.timestamp_millis()) + &uuid_from_timestamp(now.timestamp_millis()) ); let critical_count = report @@ -382,7 +382,7 @@ fn build_assemblyline_hexad( let id = format!( "pa-asmline-{}-{}", now.format("%Y%m%d%H%M%S"), - uuid_from_timestamp(now.timestamp_millis()) + &uuid_from_timestamp(now.timestamp_millis()) ); let document = serde_json::to_value(report)?; @@ -510,7 +510,7 @@ pub fn build_finding_hexads( let run_id = format!( "pa-asmline-{}-{}", now.format("%Y%m%d%H%M%S"), - uuid_from_timestamp(now.timestamp_millis()) + &uuid_from_timestamp(now.timestamp_millis()) ); let mut hexads = Vec::new(); @@ -540,7 +540,7 @@ pub fn build_finding_hexads( now.format("%Y%m%d%H%M%S"), repo_idx, wp_idx, - uuid_from_timestamp(now.timestamp_millis()), + &uuid_from_timestamp(now.timestamp_millis()), ); let document = serde_json::json!({ @@ -691,7 +691,7 @@ pub fn build_crosslang_hexads( run_ts, repo_idx, int_idx, - uuid_from_timestamp(now.timestamp_millis()), + &uuid_from_timestamp(now.timestamp_millis()), ); let document = serde_json::json!({ @@ -780,7 +780,7 @@ pub fn build_campaign_hexad(semantic: CampaignSemantic) -> PanicAttackHexad { let hexad_id = format!( "pa-campaign-{}-{}", now.format("%Y%m%d%H%M%S"), - uuid_from_timestamp(now.timestamp_millis()) + &uuid_from_timestamp(now.timestamp_millis()) ); PanicAttackHexad { diff --git a/tests/readiness.rs b/tests/readiness.rs index 727bc4e..a9b9afd 100644 --- a/tests/readiness.rs +++ b/tests/readiness.rs @@ -319,7 +319,8 @@ fn cfg_test_panic() { clean_report .weak_points .iter() - .find(|wp| wp.category == WeakPointCategory::PanicPath && !wp.suppressed) + .filter(|wp| wp.category == WeakPointCategory::PanicPath && !wp.suppressed) + .next() .is_none(), "excluded panic contexts must produce zero active findings" ); From afed6638151a26130b68296ee206b6b3136a35f9 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 21:38:47 +0000 Subject: [PATCH 18/28] fix(ci): regenerate actions.lock with the repair chain after the base merge The base merge (88d2e49) brought in dependabot's bumps (#216, #217) and a hand-resolved lock. The estate repair chain was re-run in CI on this exact head (09a5857) with a real token: relock-sha-keys, complete-job-refs, close-lock, prune-stale, check-lock-sync, all RC=0, and the governance gate at standards 333b06a0 returned "Immutable direct and transitive lockfile coverage verified". The chain changed one line: the slsa-github-generator record's ref label, f7dd8c54 -> v2.1.0. This file matches the run's LOCK-SHA256 7555257dddf72015654fdf619089d0e270b45e9ba2fc32f1fdb5046f43111f66. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_019a95kk88TZ1iegsBK3kox3 --- .github/workflows/actions.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/actions.lock b/.github/workflows/actions.lock index a3c29d1..e4ad70c 100644 --- a/.github/workflows/actions.lock +++ b/.github/workflows/actions.lock @@ -236,7 +236,7 @@ dependencies: owner_id: 18365890 repo_id: 220359305 'slsa-framework/slsa-github-generator@f7dd8c54c2067bafc12ca7a55595d5ee9b75204a': - ref: 'f7dd8c54c2067bafc12ca7a55595d5ee9b75204a' + ref: 'v2.1.0' commit: 'sha1-f7dd8c54c2067bafc12ca7a55595d5ee9b75204a' owner_id: 80431187 repo_id: 475074978 From 9873d0849e71798cc84b1041c19f3391d5da85e0 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 21:38:50 +0000 Subject: [PATCH 19/28] chore(k9): point the remaining dogfood-gate hints at the K9 spec, not k9iser Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_019a95kk88TZ1iegsBK3kox3 --- .github/workflows/dogfood-gate.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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" From 0f68c62cb9e38c666b65757036ea10c562411718 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Mon, 19 Oct 2026 22:03:46 +0000 Subject: [PATCH 20/28] fix: resolve Rust lint warnings, correct diagnostic guide paths, and refresh K9 contracts and dependencies --- Cargo.lock | 109 +++++++++++++++++----------- generated/k9iser/cargo-manifest.k9 | 64 ++++++++++++---- generated/k9iser/container-build.k9 | 78 ++++++++++++++------ src/aggregate/mod.rs | 3 + src/aggregate/proven_tests.rs | 11 ++- src/assail/analyzer.rs | 3 +- src/diagnostics.rs | 4 +- src/main.rs | 4 +- src/safe_path.rs | 21 ++---- src/scan/cargodeny.rs | 22 ++---- src/storage/mod.rs | 12 +-- tests/readiness.rs | 18 +++-- 12 files changed, 216 insertions(+), 133 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b918de3..430b8ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -939,16 +939,6 @@ dependencies = [ "libc", ] -[[package]] -name = "core-foundation" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -962,7 +952,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [ "bitflags 1.3.2", - "core-foundation 0.9.4", + "core-foundation", "core-graphics-types", "foreign-types", "libc", @@ -975,7 +965,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [ "bitflags 1.3.2", - "core-foundation 0.9.4", + "core-foundation", "libc", ] @@ -1050,9 +1040,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.18" +version = "0.9.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "dc74980687109a3b14c72fd458107bf0baa1da1a1a805e178d15501ba9b86d9d" dependencies = [ "crossbeam-utils", ] @@ -3227,12 +3217,11 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quick-xml" -version = "0.38.4" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" +checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1" dependencies = [ "memchr", - "serde", ] [[package]] @@ -3472,9 +3461,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.45" +version = "0.23.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634" +checksum = "48e13bd8c0e9365c43cfa5c9e8f9ad49d3c8444926c9aac819e0e4dc503c8fdf" dependencies = [ "log", "once_cell", @@ -4038,7 +4027,7 @@ dependencies = [ "indexmap", "toml_datetime", "toml_parser", - "winnow", + "winnow 0.7.14", ] [[package]] @@ -4047,7 +4036,7 @@ version = "1.0.7+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "247eaa3197818b831697600aadf81514e577e0cba5eab10f7e064e78ae154df1" dependencies = [ - "winnow", + "winnow 0.7.14", ] [[package]] @@ -4462,9 +4451,9 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.8" +version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5423e94b6a63e68e439803a3e153a9252d5ead12fd853334e2ad33997e3889e3" +checksum = "338e30461b3a2b67d70eb30a6d89f8e0c93a833e07d2ae89085cd070c4a00ac0" dependencies = [ "proc-macro2", "quick-xml", @@ -4505,15 +4494,15 @@ dependencies = [ [[package]] name = "webbrowser" -version = "1.2.0" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe985f41e291eecef5e5c0770a18d28390addb03331c043964d9e916453d6f16" +checksum = "62c35be770821a214dbc362fc26908c853e776c0004294d0b10b8a6bad582f94" dependencies = [ - "core-foundation 0.10.1", "jni", "log", "ndk-context", "objc2 0.6.4", + "objc2-app-kit 0.3.2", "objc2-foundation 0.3.2", "url", "web-sys", @@ -4964,7 +4953,7 @@ dependencies = [ "calloop 0.13.0", "cfg_aliases", "concurrent-queue", - "core-foundation 0.9.4", + "core-foundation", "core-graphics", "cursor-icon", "dpi", @@ -5010,6 +4999,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" +dependencies = [ + "memchr", +] + [[package]] name = "wit-bindgen" version = "0.51.0" @@ -5137,7 +5135,7 @@ dependencies = [ "uds_windows", "uuid", "windows-sys 0.61.2", - "winnow", + "winnow 0.7.14", "zbus_macros", "zbus_names", "zvariant", @@ -5179,32 +5177,41 @@ dependencies = [ "syn 2.0.117", "zbus_names", "zvariant", - "zvariant_utils", + "zvariant_utils 3.3.0", ] [[package]] name = "zbus_names" -version = "4.3.1" +version = "4.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" +checksum = "812a12be4ead7a59e18ea8f047d08add0066b73bc62fc1467066a2472ae088dd" dependencies = [ "serde", - "winnow", + "winnow 1.0.4", "zvariant", ] [[package]] name = "zbus_xml" -version = "5.1.0" +version = "5.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "441a0064125265655bccc3a6af6bef56814d9277ac83fce48b1cd7e160b80eac" +checksum = "d1586c021a01ca0a9216dcd874e546382e156a5cbab5fab6cb5f10087e22682a" dependencies = [ - "quick-xml", "serde", + "winnow 1.0.4", "zbus_names", "zvariant", ] +[[package]] +name = "zcheapstr" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1afec51604565183aeb5c54c20aeab286120d4e4460f7f76e3e8bb8c0d99473" +dependencies = [ + "serde", +] + [[package]] name = "zerocopy" version = "0.8.39" @@ -5308,29 +5315,30 @@ dependencies = [ [[package]] name = "zvariant" -version = "5.10.0" +version = "5.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5708299b21903bbe348e94729f22c49c55d04720a004aa350f1f9c122fd2540b" +checksum = "80bf6d6cd6a4b601acb64cfb8284cdc643fcfa75ef26ae3c46ec8aa4e8a1fbb7" dependencies = [ "endi", "enumflags2", "serde", - "winnow", + "winnow 1.0.4", + "zcheapstr", "zvariant_derive", - "zvariant_utils", + "zvariant_utils 4.2.0", ] [[package]] name = "zvariant_derive" -version = "5.10.0" +version = "5.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b59b012ebe9c46656f9cc08d8da8b4c726510aef12559da3e5f1bf72780752c" +checksum = "ef2672456ba1cb54b5eaec136e63135b3c7495fb938e53b28a62e633496d486d" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.117", - "zvariant_utils", + "syn 3.0.3", + "zvariant_utils 4.2.0", ] [[package]] @@ -5343,5 +5351,18 @@ dependencies = [ "quote", "serde", "syn 2.0.117", - "winnow", + "winnow 0.7.14", +] + +[[package]] +name = "zvariant_utils" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad0294361a320b694a328460dc73add56c306150f5cb6bfafc44446120008a3" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn 3.0.3", + "winnow 1.0.4", ] diff --git a/generated/k9iser/cargo-manifest.k9 b/generated/k9iser/cargo-manifest.k9 index ad723ab..0e313e0 100644 --- a/generated/k9iser/cargo-manifest.k9 +++ b/generated/k9iser/cargo-manifest.k9 @@ -1,19 +1,53 @@ -# Auto-generated K9 contract for cargo-manifest -# Safety tier: hunt +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. -[must] -package.name : string { == 'panic-attack' } -package.edition : string { == '2021' } -profile.release.codegen-units : string { == 1 } -profile.release.strip : string { == 'symbols' } -profile.release.panic : string { == 'unwind' } +{ + pedigree = { + schema_version = "1.0.0", + metadata = { + name = "cargo-manifest", + version = "1.0.0", + }, + security = { + leash = 'Hunt, + signature_required = true, + }, + }, -[trust] -signed-by = "ci-pipeline" + config = { + source = "Cargo.toml", + format = "toml", + }, -[dust] -remove = ["unused optional features"] + # 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", + ], -[intend] -production-ready = true -security-critical = true + 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 index 0f79c65..99eb4e6 100644 --- a/generated/k9iser/container-build.k9 +++ b/generated/k9iser/container-build.k9 @@ -1,28 +1,58 @@ -# Auto-generated K9 contract for container-build -# Safety tier: hunt +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. -[must] -metadata.license : string { == 'MPL-2.0' } -metadata.registry : string { == 'ghcr.io/hyperpolymath' } -build.runtime : string { == 'podman' } -layers.base.verify : bool { == true } -layers.runtime.user : string { == 'nonroot' } -security.non-root : bool { == true } -security.no-new-privileges : bool { == true } -security.signing.algorithm : string { == 'ML-DSA-87' } -security.signing.provider : string { == 'cerro-torre' } -security.sbom.format : string { == 'spdx-json' } -security.sbom.include-deps : bool { == true } -verify.vordr : bool { == true } -verify.svalinn : bool { == true } -verify.scan-on-build : bool { == true } +{ + pedigree = { + schema_version = "1.0.0", + metadata = { + name = "container-build", + version = "1.0.0", + }, + security = { + leash = 'Hunt, + signature_required = true, + }, + }, -[trust] -signed-by = "ci-pipeline" + config = { + source = "stapeln.toml", + format = "toml", + }, -[dust] -remove = ["deprecated layer definitions"] + # 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'", + ], -[intend] -production-ready = true -supply-chain-verified = true + trust = { + "signed-by" = "ci-pipeline", + }, + + dust = { + remove = ["deprecated layer definitions"], + }, + + intend = { + "production-ready" = true, + "supply-chain-verified" = true, + }, +} diff --git a/src/aggregate/mod.rs b/src/aggregate/mod.rs index a30ee67..4ecf13d 100644 --- a/src/aggregate/mod.rs +++ b/src/aggregate/mod.rs @@ -51,6 +51,9 @@ use std::io::Read; use std::path::{Path, PathBuf}; /// Ingest a `proven-tests` JSON run report as an aggregate input kind. +// This public library API is also compiled into the binary's private aggregate +// module, whose CLI does not yet consume proven-tests reports. +#[allow(dead_code)] pub mod proven_tests; /// Upper bound on the text we scan for verdict / coverage classification. diff --git a/src/aggregate/proven_tests.rs b/src/aggregate/proven_tests.rs index e12d807..60c0819 100644 --- a/src/aggregate/proven_tests.rs +++ b/src/aggregate/proven_tests.rs @@ -27,6 +27,7 @@ use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use std::collections::BTreeSet; use std::path::Path; +use std::str::FromStr; /// A cited proof step (only Actually-Proven entries carry these). #[derive(Debug, Clone, Serialize, Deserialize)] @@ -115,9 +116,11 @@ impl ReportTier { } } -impl ProvenTestsReport { +impl FromStr for ProvenTestsReport { + type Err = anyhow::Error; + /// Parse a run report from JSON text. - pub fn from_str(text: &str) -> Result { + fn from_str(text: &str) -> Result { let report: ProvenTestsReport = serde_json::from_str(text).context("parsing proven-tests run report")?; if report.schema_version != 1 { @@ -128,7 +131,9 @@ impl ProvenTestsReport { } Ok(report) } +} +impl ProvenTestsReport { /// Parse a run report from a file path. pub fn from_path(path: &Path) -> Result { let text = @@ -210,7 +215,7 @@ mod tests { #[test] fn parses_schema_v1() { - let r = ProvenTestsReport::from_str(SAMPLE).expect("parse"); + let r: ProvenTestsReport = SAMPLE.parse().expect("parse"); assert_eq!(r.schema_version, 1); assert_eq!(r.entries.len(), 3); assert_eq!(r.summary.actually_proven, 1); diff --git a/src/assail/analyzer.rs b/src/assail/analyzer.rs index bc69fc4..1b75185 100644 --- a/src/assail/analyzer.rs +++ b/src/assail/analyzer.rs @@ -285,8 +285,7 @@ fn c_alloc_result_is_null_checked(lines: &[&str], idx: usize) -> bool { .next() .and_then(|lhs| { lhs.split(|c: char| !c.is_alphanumeric() && c != '_') - .filter(|s| !s.is_empty()) - .last() + .rfind(|s| !s.is_empty()) }) .map(str::to_string) } else { diff --git a/src/diagnostics.rs b/src/diagnostics.rs index 76da97e..f89ec6b 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -41,11 +41,11 @@ pub fn run_self_diagnostics(manifest: &Manifest) -> Result<()> { checks.push(check_verisimdb(Path::new("verisimdb-data/verisimdb"))); checks.push(check_file( "ambush timeline spec", - Path::new("docs/ambush-timeline.md"), + Path::new("docs/ambush-timeline.adoc"), )); checks.push(check_file( "panll export guide", - Path::new("docs/panll-export.md"), + Path::new("docs/panll-export.adoc"), )); checks.push(check_watcher("Hypatia scanner", HYPATIA_ENV)); diff --git a/src/main.rs b/src/main.rs index 93079c8..d09d67a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,12 +22,9 @@ mod axial; #[cfg(feature = "http")] mod bridge; mod campaign; -mod comment_marker; mod diagnostics; -mod ffi_kind; mod groove; mod i18n; -mod jit_context; mod kanren; mod kin; mod mass_panic; @@ -60,6 +57,7 @@ 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/safe_path.rs b/src/safe_path.rs index e79b525..12e008d 100644 --- a/src/safe_path.rs +++ b/src/safe_path.rs @@ -62,7 +62,7 @@ const SAFE_PUNCT: &[char] = &['-', '_', '.', '~']; /// assert!(!has_traversal("foo..bar")); // .. inside a name is not a segment /// ``` pub fn has_traversal(path: &str) -> bool { - for segment in path.split(|c| c == '/' || c == '\\') { + for segment in path.split(['/', '\\']) { if segment == ".." { return true; } @@ -100,22 +100,17 @@ pub fn sanitize_filename(filename: &str) -> String { } } -/// 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) -} - -/// 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 // ───────────────────────────────────────────────────────────────── @@ -288,7 +283,7 @@ mod tests { #[test] fn prop_has_traversal_operational(s in ".*") { let split_says: bool = s - .split(|c| c == '/' || c == '\\') + .split(['/', '\\']) .any(|seg| seg == ".."); prop_assert_eq!( has_traversal(&s), diff --git a/src/scan/cargodeny.rs b/src/scan/cargodeny.rs index 01d2673..59ee87e 100644 --- a/src/scan/cargodeny.rs +++ b/src/scan/cargodeny.rs @@ -8,7 +8,7 @@ // feedback_no_automated_licence_edits. use crate::types::{Severity, WeakPoint, WeakPointCategory}; -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use std::path::{Path, PathBuf}; use std::process::Command; @@ -181,18 +181,12 @@ pub fn run_cargo_deny(config: &CargoDenyConfig) -> Result> { .map(|c| code_to_category(&c)) .unwrap_or(WeakPointCategory::Advisory); - let location = diagnostic - .location - .map(|loc| { - if let (Some(ref file), Some(line)) = (&loc.file, loc.line) { - Some(format!("{}:{}", file, line)) - } else if let Some(ref file) = loc.file { - Some(file.clone()) - } else { - None - } + let location = diagnostic.location.and_then(|loc| { + loc.file.map(|file| match loc.line { + Some(line) => format!("{}:{}", file, line), + None => file, }) - .flatten(); + }); // Parse file and line from location string let (file, line) = if let Some(ref loc) = location { @@ -286,13 +280,13 @@ pub fn parse_sarif_output(sarif_json: &str) -> Result> { // SARIF structure: { runs: [{ results: [{ ... }] }] } let runs = v["runs"] .as_array() - .ok_or_else(|| anyhow!("no runs in SARIF"))?; + .ok_or_else(|| anyhow::anyhow!("no runs in SARIF"))?; let mut weak_points = Vec::new(); for run in runs { let results = run["results"] .as_array() - .ok_or_else(|| anyhow!("no results in SARIF run"))?; + .ok_or_else(|| anyhow::anyhow!("no results in SARIF run"))?; for result in results { let message = result["message"]["text"] .as_str() diff --git a/src/storage/mod.rs b/src/storage/mod.rs index 2c4fbd1..1c551ba 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -235,7 +235,7 @@ fn build_hexad(report: &AssaultReport) -> Result { let id = format!( "pa-{}-{}", now.format("%Y%m%d%H%M%S"), - &uuid_from_timestamp(now.timestamp_millis()) + uuid_from_timestamp(now.timestamp_millis()) ); let critical_count = report @@ -382,7 +382,7 @@ fn build_assemblyline_hexad( let id = format!( "pa-asmline-{}-{}", now.format("%Y%m%d%H%M%S"), - &uuid_from_timestamp(now.timestamp_millis()) + uuid_from_timestamp(now.timestamp_millis()) ); let document = serde_json::to_value(report)?; @@ -510,7 +510,7 @@ pub fn build_finding_hexads( let run_id = format!( "pa-asmline-{}-{}", now.format("%Y%m%d%H%M%S"), - &uuid_from_timestamp(now.timestamp_millis()) + uuid_from_timestamp(now.timestamp_millis()) ); let mut hexads = Vec::new(); @@ -540,7 +540,7 @@ pub fn build_finding_hexads( now.format("%Y%m%d%H%M%S"), repo_idx, wp_idx, - &uuid_from_timestamp(now.timestamp_millis()), + uuid_from_timestamp(now.timestamp_millis()), ); let document = serde_json::json!({ @@ -691,7 +691,7 @@ pub fn build_crosslang_hexads( run_ts, repo_idx, int_idx, - &uuid_from_timestamp(now.timestamp_millis()), + uuid_from_timestamp(now.timestamp_millis()), ); let document = serde_json::json!({ @@ -780,7 +780,7 @@ pub fn build_campaign_hexad(semantic: CampaignSemantic) -> PanicAttackHexad { let hexad_id = format!( "pa-campaign-{}-{}", now.format("%Y%m%d%H%M%S"), - &uuid_from_timestamp(now.timestamp_millis()) + uuid_from_timestamp(now.timestamp_millis()) ); PanicAttackHexad { diff --git a/tests/readiness.rs b/tests/readiness.rs index a9b9afd..9de3355 100644 --- a/tests/readiness.rs +++ b/tests/readiness.rs @@ -114,8 +114,8 @@ fn readiness_d_assail_runs() -> Result<(), Box> { #[test] fn readiness_d_diagnostics_runs() { - let (ok, _stdout, stderr) = run(&["diagnostics"]); - assert!(ok, "diagnostics should succeed: {}", stderr); + let (ok, stdout, stderr) = run(&["diagnostics"]); + assert!(ok, "diagnostics should succeed: {stdout}\n{stderr}"); } #[test] @@ -316,12 +316,10 @@ fn cfg_test_panic() { )?; let clean_report = assail::analyze(dir.path())?; assert!( - clean_report + !clean_report .weak_points .iter() - .filter(|wp| wp.category == WeakPointCategory::PanicPath && !wp.suppressed) - .next() - .is_none(), + .any(|wp| wp.category == WeakPointCategory::PanicPath && !wp.suppressed), "excluded panic contexts must produce zero active findings" ); Ok(()) @@ -511,7 +509,13 @@ fn readiness_c_panll_runs() -> Result<(), Box> { #[test] fn readiness_c_diagnostics_output() { let (ok, stdout, stderr) = run(&["diagnostics"]); - assert!(ok, "diagnostics should succeed: {}", stderr); + assert!(ok, "diagnostics should succeed: {stdout}\n{stderr}"); + for guide in ["docs/ambush-timeline.adoc", "docs/panll-export.adoc"] { + assert!( + stdout.contains(&format!("{guide} exists")), + "diagnostics should find the shipped guide {guide}: {stdout}" + ); + } assert!( stdout.contains("panicbot integration"), "diagnostics should check panicbot integration readiness, got: {}", From cb067e21cf5f008b105fd77ca268b81079c9bb68 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 22:09:37 +0000 Subject: [PATCH 21/28] fix(deps): bump zbus_xml to 5.2.1 and zvariant to 5.15.1, dropping quick-xml 0.38 zbus_xml 5.2.1 builds on quick-xml 0.41, so the last copy of quick-xml 0.38.4 (RUSTSEC-2026-0194, RUSTSEC-2026-0195) leaves the lockfile. Lockfile only; semver-compatible: zbus_names 4.3.5, zbus_xml 5.2.1, zvariant 5.15.1, zvariant_derive 5.15.1, plus their new transitive deps winnow 1.0.4, zcheapstr 1.1.0, zvariant_utils 4.2.0. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_019a95kk88TZ1iegsBK3kox3 --- Cargo.lock | 82 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 52 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5800f1c..5e0d690 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3214,16 +3214,6 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" -[[package]] -name = "quick-xml" -version = "0.38.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "quick-xml" version = "0.41.0" @@ -4036,7 +4026,7 @@ dependencies = [ "indexmap", "toml_datetime", "toml_parser", - "winnow", + "winnow 0.7.14", ] [[package]] @@ -4045,7 +4035,7 @@ version = "1.0.7+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "247eaa3197818b831697600aadf81514e577e0cba5eab10f7e064e78ae154df1" dependencies = [ - "winnow", + "winnow 0.7.14", ] [[package]] @@ -4465,7 +4455,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "338e30461b3a2b67d70eb30a6d89f8e0c93a833e07d2ae89085cd070c4a00ac0" dependencies = [ "proc-macro2", - "quick-xml 0.41.0", + "quick-xml", "quote", ] @@ -5008,6 +4998,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" +dependencies = [ + "memchr", +] + [[package]] name = "wit-bindgen" version = "0.51.0" @@ -5135,7 +5134,7 @@ dependencies = [ "uds_windows", "uuid", "windows-sys 0.61.2", - "winnow", + "winnow 0.7.14", "zbus_macros", "zbus_names", "zvariant", @@ -5177,32 +5176,41 @@ dependencies = [ "syn 2.0.117", "zbus_names", "zvariant", - "zvariant_utils", + "zvariant_utils 3.3.0", ] [[package]] name = "zbus_names" -version = "4.3.1" +version = "4.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" +checksum = "812a12be4ead7a59e18ea8f047d08add0066b73bc62fc1467066a2472ae088dd" dependencies = [ "serde", - "winnow", + "winnow 1.0.4", "zvariant", ] [[package]] name = "zbus_xml" -version = "5.1.0" +version = "5.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "441a0064125265655bccc3a6af6bef56814d9277ac83fce48b1cd7e160b80eac" +checksum = "d1586c021a01ca0a9216dcd874e546382e156a5cbab5fab6cb5f10087e22682a" dependencies = [ - "quick-xml 0.38.4", "serde", + "winnow 1.0.4", "zbus_names", "zvariant", ] +[[package]] +name = "zcheapstr" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1afec51604565183aeb5c54c20aeab286120d4e4460f7f76e3e8bb8c0d99473" +dependencies = [ + "serde", +] + [[package]] name = "zerocopy" version = "0.8.39" @@ -5306,29 +5314,30 @@ dependencies = [ [[package]] name = "zvariant" -version = "5.10.0" +version = "5.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5708299b21903bbe348e94729f22c49c55d04720a004aa350f1f9c122fd2540b" +checksum = "80bf6d6cd6a4b601acb64cfb8284cdc643fcfa75ef26ae3c46ec8aa4e8a1fbb7" dependencies = [ "endi", "enumflags2", "serde", - "winnow", + "winnow 1.0.4", + "zcheapstr", "zvariant_derive", - "zvariant_utils", + "zvariant_utils 4.2.0", ] [[package]] name = "zvariant_derive" -version = "5.10.0" +version = "5.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b59b012ebe9c46656f9cc08d8da8b4c726510aef12559da3e5f1bf72780752c" +checksum = "ef2672456ba1cb54b5eaec136e63135b3c7495fb938e53b28a62e633496d486d" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.117", - "zvariant_utils", + "syn 3.0.3", + "zvariant_utils 4.2.0", ] [[package]] @@ -5341,5 +5350,18 @@ dependencies = [ "quote", "serde", "syn 2.0.117", - "winnow", + "winnow 0.7.14", +] + +[[package]] +name = "zvariant_utils" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad0294361a320b694a328460dc73add56c306150f5cb6bfafc44446120008a3" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn 3.0.3", + "winnow 1.0.4", ] From 3bfc1fc3809ed1942daa4363a0583cb6586ec30d Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 22:14:04 +0000 Subject: [PATCH 22/28] fix(tests): restore the panic-surface control assertion and drop the re-added unused import after the 0f68c62 merge The text merge of 0f68c62 combined CodeRabbit's leading negation with this branch's .find(..).is_none(), inverting the readiness control; it is now the single !any(..) form. main.rs had its removed comment_marker/ffi_kind import re-added by the same merge. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_019a95kk88TZ1iegsBK3kox3 --- src/main.rs | 1 - tests/readiness.rs | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 23f1459..adede31 100644 --- a/src/main.rs +++ b/src/main.rs @@ -55,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/tests/readiness.rs b/tests/readiness.rs index 2b65ed8..9de3355 100644 --- a/tests/readiness.rs +++ b/tests/readiness.rs @@ -319,8 +319,7 @@ fn cfg_test_panic() { !clean_report .weak_points .iter() - .find(|wp| wp.category == WeakPointCategory::PanicPath && !wp.suppressed) - .is_none(), + .any(|wp| wp.category == WeakPointCategory::PanicPath && !wp.suppressed), "excluded panic contexts must produce zero active findings" ); Ok(()) From 8f2ceafc3a40764d907475ee176aaef3a0cf70ab Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 22:23:31 +0000 Subject: [PATCH 23/28] fix(tests): restore the stack's safe_path test helpers; the 0f68c62 merge left two unused CodeRabbit's hunk added an all_chars_safe helper beside this branch's is_safe_char, and the merge kept both with neither caller, which cargo clippy --all-targets -- -D warnings rejects as dead code in the lib test target. The file is the version CI passed at 5db5d90. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_019a95kk88TZ1iegsBK3kox3 --- src/safe_path.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/safe_path.rs b/src/safe_path.rs index f60b699..ec06273 100644 --- a/src/safe_path.rs +++ b/src/safe_path.rs @@ -118,12 +118,6 @@ 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 // ───────────────────────────────────────────────────────────────── From 7c3d0542f42810d6044f83263a762882fdebfdb6 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Mon, 19 Oct 2026 23:24:43 +0100 Subject: [PATCH 24/28] Update .github/workflows/scorecard.yml Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com> --- .github/workflows/scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 0ba37a53932f8c7ddac438c1f6ad166c08ffeba5 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 22:23:31 +0000 Subject: [PATCH 25/28] fix(tests): restore the stack's safe_path test helpers; the 0f68c62 merge left two unused CodeRabbit's hunk added an all_chars_safe helper beside this branch's is_safe_char, and the merge kept both with neither caller, which cargo clippy --all-targets -- -D warnings rejects as dead code in the lib test target. The file is the version CI passed at 5db5d90. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_019a95kk88TZ1iegsBK3kox3 --- src/safe_path.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/safe_path.rs b/src/safe_path.rs index f60b699..ec06273 100644 --- a/src/safe_path.rs +++ b/src/safe_path.rs @@ -118,12 +118,6 @@ 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 // ───────────────────────────────────────────────────────────────── From e7a6e452ea2a796c3218b674d46dc9e86150cde4 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Mon, 19 Oct 2026 22:27:44 +0000 Subject: [PATCH 26/28] chore: pin Chapel CI actions to commit SHAs and clarify JIT context documentation --- .github/workflows/actions.lock | 15 ++++++++++----- .github/workflows/chapel-ci.yml | 34 ++++++++++++++++----------------- src/assail/mod.rs | 3 ++- 3 files changed, 29 insertions(+), 23 deletions(-) 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/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 From a7777db2cf56545e3b787b76454465633094703a Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 17:52:31 +0000 Subject: [PATCH 27/28] fix(diagnostics): look for the .adoc docs, not the retired .md names docs/ambush-timeline.md and docs/panll-export.md were converted to .adoc (estate docs policy) but the self-diagnostics check still demanded the .md files, so `panic-attack diagnostics` exited non-zero and both diagnostics readiness tests failed. Point the check, README.adoc and the bridge module docs at the .adoc files that exist. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019a95kk88TZ1iegsBK3kox3 --- README.adoc | 4 ++-- src/bridge/mod.rs | 2 +- src/bridge/registry.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) 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/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}; From 74a4654339b48d3b4365d75c1d181fc8a2fed3b1 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Mon, 19 Oct 2026 22:29:36 +0000 Subject: [PATCH 28/28] docs: clarify Rust function behavior, error conditions, and hexad ID guarantees --- src/aggregate/proven_tests.rs | 11 +++++++++-- src/assail/analyzer.rs | 18 ++++++++---------- src/diagnostics.rs | 6 ++++++ src/report/gui.rs | 3 +++ src/storage/mod.rs | 33 ++++++++++++++++++++------------- 5 files changed, 46 insertions(+), 25 deletions(-) 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/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/report/gui.rs b/src/report/gui.rs index 9502c4c..549560b 100644 --- a/src/report/gui.rs +++ b/src/report/gui.rs @@ -120,6 +120,9 @@ 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(ui, |ui| { ui.horizontal(|ui| { 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!(