From 4fbb5679dd21a9ad9343cda903eb7dfb73a01e35 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 27 Oct 2026 05:56:38 +0000 Subject: [PATCH 1/2] fix(governance): clear symlinked and directory report-back entries before the download Codex on #296: the clear step used find -type f, so a committed *.json symlink under .gitbot-fleet/report-backs/ survived it; with the artifact download tolerated as absent, the renderer's *.json loop followed the symlink and accepted its target as evidence for an enrolled member. Every directory entry named *.json is now removed, whatever its type. Contract updated; new mutation fleet-symlinked-evidence puts the -type f filter back and must be rejected. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01J4QvDjqj5DHRRCrDiGeTmq --- .github/workflows/hypatia-scan-reusable.yml | 4 +++- scripts/check-governance-contract.sh | 2 +- scripts/tests/test-governance-contract.sh | 5 +++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/hypatia-scan-reusable.yml b/.github/workflows/hypatia-scan-reusable.yml index bef64c99..97f2e5e3 100644 --- a/.github/workflows/hypatia-scan-reusable.yml +++ b/.github/workflows/hypatia-scan-reusable.yml @@ -754,7 +754,9 @@ jobs: run: | set -euo pipefail mkdir -p .gitbot-fleet/report-backs - find .gitbot-fleet/report-backs -maxdepth 1 -name '*.json' -type f -print -delete + # Every entry named *.json goes, symlinks and directories included: a committed + # symlink survives a -type f filter and the renderer follows it to its target. + find .gitbot-fleet/report-backs -mindepth 1 -maxdepth 1 -name '*.json' -print -exec rm -rf -- {} + - name: Download the fleet report-backs if: ${{ always() && inputs.report-backs-artifact != '' }} diff --git a/scripts/check-governance-contract.sh b/scripts/check-governance-contract.sh index 990cb3ee..8379df4d 100644 --- a/scripts/check-governance-contract.sh +++ b/scripts/check-governance-contract.sh @@ -141,7 +141,7 @@ else fail 'the fleet report-backs must come from hyperpolymath/gitbot-fleet pinned to a 40-hex commit' fi if fleet_clear="$(step_block "$REUSABLE" 'Clear any checked-out fleet report-backs')"; then - contains "$fleet_clear" "find .gitbot-fleet/report-backs -maxdepth 1 -name '*.json' -type f -print -delete" \ + contains "$fleet_clear" "find .gitbot-fleet/report-backs -mindepth 1 -maxdepth 1 -name '*.json' -print -exec rm -rf -- {} +" \ 'a committed envelope can never pose as report-back evidence' clear_line=$(grep -nF -- '- name: Clear any checked-out fleet report-backs' "$REUSABLE" | cut -d: -f1 | head -n1) dl_line0=$(grep -nF -- '- name: Download the fleet report-backs' "$REUSABLE" | cut -d: -f1 | head -n1) diff --git a/scripts/tests/test-governance-contract.sh b/scripts/tests/test-governance-contract.sh index 052548aa..bf1bc4cb 100644 --- a/scripts/tests/test-governance-contract.sh +++ b/scripts/tests/test-governance-contract.sh @@ -14,7 +14,7 @@ reset_fixture() { } reset_fixture bash "$fixture/scripts/check-governance-contract.sh" > "$fixture/log" 2>&1 -for mutation in allowlist-addition baseline-zero-gate baseline-operator-gate baseline-grouping-gate sarif upstream-exception effective-grid jcs-manifest jcs-canonicalizer raw-ijson k9-pin k9-require k9-indeterminate k9-verdict scanner-advisory scanner-secrets scanner-trigger scorecard-trigger stale-standards-owner scorecard-owner deed-remote scorecard-remote deed-source-sha scorecard-source-sha scorecard-no-upload scorecard-quiet-reconcile cross-owner-reusable fleet-unpinned fleet-before-contract fleet-dropped-download fleet-download-after-render fleet-download-fatal fleet-committed-evidence fleet-ignores-cancel; do +for mutation in allowlist-addition baseline-zero-gate baseline-operator-gate baseline-grouping-gate sarif upstream-exception effective-grid jcs-manifest jcs-canonicalizer raw-ijson k9-pin k9-require k9-indeterminate k9-verdict scanner-advisory scanner-secrets scanner-trigger scorecard-trigger stale-standards-owner scorecard-owner deed-remote scorecard-remote deed-source-sha scorecard-source-sha scorecard-no-upload scorecard-quiet-reconcile cross-owner-reusable fleet-unpinned fleet-before-contract fleet-dropped-download fleet-download-after-render fleet-download-fatal fleet-committed-evidence fleet-symlinked-evidence fleet-ignores-cancel; do reset_fixture case "$mutation" in allowlist-addition) echo 'frontend/src/fork-only.ts' >> "$fixture/config/hypatia/upstream-typescript-allowlist.txt" ;; @@ -56,7 +56,8 @@ for mutation in allowlist-addition baseline-zero-gate baseline-operator-gate bas fleet-dropped-download) sed -i '/^ - name: Download the fleet report-backs$/,/^ path: .gitbot-fleet\/report-backs$/d' "$fixture/.github/workflows/hypatia-scan-reusable.yml" ;; fleet-download-after-render) awk '/^ - name: Download the fleet report-backs$/{hold=1} hold && /^ - name: Render the verdict grid$/{hold=0; print; printf "%s", buf; buf=""; next} hold{buf=buf $0 "\n"; next} {print}' "$fixture/.github/workflows/hypatia-scan-reusable.yml" > "$fixture/reordered.yml" && mv "$fixture/reordered.yml" "$fixture/.github/workflows/hypatia-scan-reusable.yml" ;; fleet-download-fatal) sed -i '/^ - name: Download the fleet report-backs$/,/^ path: .gitbot-fleet\/report-backs$/{/continue-on-error: true/d}' "$fixture/.github/workflows/hypatia-scan-reusable.yml" ;; - fleet-committed-evidence) sed -i '/^ - name: Clear any checked-out fleet report-backs$/,/-print -delete$/d' "$fixture/.github/workflows/hypatia-scan-reusable.yml" ;; + fleet-committed-evidence) sed -i '/^ - name: Clear any checked-out fleet report-backs$/,/-exec rm -rf -- {} +$/d' "$fixture/.github/workflows/hypatia-scan-reusable.yml" ;; + fleet-symlinked-evidence) sed -i "s|-mindepth 1 -maxdepth 1 -name '*.json' -print -exec rm -rf -- {} +|-maxdepth 1 -name '*.json' -type f -print -delete|" "$fixture/.github/workflows/hypatia-scan-reusable.yml" ;; fleet-ignores-cancel) sed -i "s|if: \${{ !cancelled() \&\& needs.workflow-contract.result == 'success' }}|if: \${{ always() \&\& needs.workflow-contract.result == 'success' }}|" "$fixture/.github/workflows/hypatia-scan.yml" ;; # A copy is only a copy while it still comes from the audited commit and # keeps the guards whose absence froze code scanning upstream. From 9736d56da4f5a9264bb5f227a520c8ea84c7155a Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 27 Oct 2026 05:57:54 +0000 Subject: [PATCH 2/2] test(governance): escape the glob in the symlink mutation so it mutates The sed pattern's unescaped '*' consumed the quote and never matched, so the fixture went unmutated and the suite reported the mutation as accepted. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01J4QvDjqj5DHRRCrDiGeTmq --- scripts/tests/test-governance-contract.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tests/test-governance-contract.sh b/scripts/tests/test-governance-contract.sh index bf1bc4cb..e0df9884 100644 --- a/scripts/tests/test-governance-contract.sh +++ b/scripts/tests/test-governance-contract.sh @@ -57,7 +57,7 @@ for mutation in allowlist-addition baseline-zero-gate baseline-operator-gate bas fleet-download-after-render) awk '/^ - name: Download the fleet report-backs$/{hold=1} hold && /^ - name: Render the verdict grid$/{hold=0; print; printf "%s", buf; buf=""; next} hold{buf=buf $0 "\n"; next} {print}' "$fixture/.github/workflows/hypatia-scan-reusable.yml" > "$fixture/reordered.yml" && mv "$fixture/reordered.yml" "$fixture/.github/workflows/hypatia-scan-reusable.yml" ;; fleet-download-fatal) sed -i '/^ - name: Download the fleet report-backs$/,/^ path: .gitbot-fleet\/report-backs$/{/continue-on-error: true/d}' "$fixture/.github/workflows/hypatia-scan-reusable.yml" ;; fleet-committed-evidence) sed -i '/^ - name: Clear any checked-out fleet report-backs$/,/-exec rm -rf -- {} +$/d' "$fixture/.github/workflows/hypatia-scan-reusable.yml" ;; - fleet-symlinked-evidence) sed -i "s|-mindepth 1 -maxdepth 1 -name '*.json' -print -exec rm -rf -- {} +|-maxdepth 1 -name '*.json' -type f -print -delete|" "$fixture/.github/workflows/hypatia-scan-reusable.yml" ;; + fleet-symlinked-evidence) sed -i "s|-mindepth 1 -maxdepth 1 -name '\*.json' -print -exec rm -rf -- {} +|-maxdepth 1 -name '*.json' -type f -print -delete|" "$fixture/.github/workflows/hypatia-scan-reusable.yml" ;; fleet-ignores-cancel) sed -i "s|if: \${{ !cancelled() \&\& needs.workflow-contract.result == 'success' }}|if: \${{ always() \&\& needs.workflow-contract.result == 'success' }}|" "$fixture/.github/workflows/hypatia-scan.yml" ;; # A copy is only a copy while it still comes from the audited commit and # keeps the guards whose absence froze code scanning upstream.