From 826e9e1bddda1a9cebd7cfc30330903031bafa29 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 16:54:21 +0000 Subject: [PATCH 01/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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 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 15/27] 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 16/27] 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 17/27] 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 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 18/27] 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 19/27] 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 20/27] 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 21/27] 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 22/27] 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 23/27] 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 24/27] 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 25/27] 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 26/27] 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!( From 8b6573969d8bf405d4def191a1d73b443a4e3914 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Tue, 20 Oct 2026 01:32:40 +0100 Subject: [PATCH 27/27] fix(deps): move zbus_xml to 5.2.1, clearing the quick-xml 0.38 advisories (#231) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stacked on #222 (← #221 ← #220 ← #218). ## What Lockfile-only bump of `zbus_xml` 5.1.0 → 5.2.1. This removes the last `quick-xml 0.38.4` from `Cargo.lock` and clears: - RUSTSEC-2026-0194: quadratic attribute checking in quick-xml - RUSTSEC-2026-0195: unbounded namespace allocation in quick-xml ## Why no upstream PR or `[patch]` is needed The handoff plan assumed every `zbus_xml` 5.x release still used `quick-xml ^0.38`. crates.io shows otherwise: | zbus_xml | XML parser | |---|---| | 5.1.0 (was locked) | quick-xml ^0.38 | | 5.1.1 | quick-xml ^0.39 | | 5.2.0, 5.2.1 | winnow ^1.0 (no quick-xml) | `zbus-lockstep 0.5.2` (via `atspi-common 0.13`) requires `zbus_xml ^5.0.2`, so 5.2.1 is a semver-compatible resolution. The fix is already published upstream; the lockfile simply had not picked it up. ## Side effects in the lockfile All behind the opt-in `gui` feature. Default builds are unchanged. - `zvariant` / `zvariant_derive` 5.10.0 → 5.15.1 - `zbus_names` 4.3.1 → 4.3.5 - adds `winnow 1.0.4`, `zvariant_utils 4.2.0`, `zcheapstr 1.1.0`, `syn 3.0.3` (proc-macro only) Every new crate declares `rust-version` ≤ 1.87, the floor the previously locked `zvariant 5.10.0` already set. The `gui` MSRV does not move. ## Verification (local, Rust 1.99.0) - `cargo audit`: before, 2 vulnerabilities, exit 1. After, 0 vulnerabilities, exit 0, with zero ignores and no `audit.toml`. - The same five `unmaintained` warnings appear before and after: ttf-parser, unsafe-libyaml, zbus_names, zvariant, zvariant_derive. - `cargo check --all-features --all-targets`: pass - `cargo clippy --all-features --all-targets -- -D warnings`: pass - `cargo test --all-features`: all suites pass ## Follow-up (not in this PR) `zvariant`, `zvariant_derive` and `zbus_names` gained `unmaintained` notices on 2026-10-16 because they were folded into zbus 6. These are warnings, not failures. They clear only when accesskit/atspi move to zbus 6. --- _Generated by [Claude Code](https://claude.ai/code/session_01Jma5ixtpeZyJRmfoSsC98u)_ Co-authored-by: Claude