Skip to content

Commit 6a20ea8

Browse files
itcmsgrclaude
andcommitted
fix(v1.100 PR-23): replace PR-22-era auto-elevate gate with PR-23 consent-refusal gates
The uninstall canonization workflow had a gate asserting that `--mode=uninstall` without --dry-run auto-elevates to dry-run and exits 0. That was the correct PR-22 behaviour but is a contract violation under PR-23 — the auto-elevate shim was removed and the correct post-PR-23 behaviour is an explicit refusal demanding --dry-run OR --confirm-mutation. Replace the single obsolete gate with two PR-23 gates: - G3-UN-CONSENT-REQUIRED: bare `--mode=uninstall` must exit non-zero with a message mentioning the two-flag choice, touch NOTHING on disk. - G3-UN-CONSENT-BOTH-FLAGS: `--mode=uninstall --dry-run --confirm-mutation` must also refuse with a "mutually exclusive" message. Together these enforce the consent model at the CLI surface: neither → refused dry-run → observational confirm-mutation → apply both → refused No code changes — CI gate update only. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 5d95885 commit 6a20ea8

1 file changed

Lines changed: 45 additions & 16 deletions

File tree

.github/workflows/ci-uninstall-canonization.yml

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -398,36 +398,65 @@ jobs:
398398
echo "G3-UN-PLAN-RENDERS PASS — all mandatory contract-language elements present"
399399
400400
# ------------------------------------------------------------------
401-
# G3-UN-PLAN-RENDERS negative path — invoking --mode=uninstall
402-
# WITHOUT --dry-run must still not mutate. The flags validator
403-
# forces --dry-run on in PR-22; this confirms that elevation works.
401+
# G3-UN-CONSENT-REQUIRED (PR-23) — --mode=uninstall with NEITHER
402+
# --dry-run NOR --confirm-mutation must REFUSE explicitly (exit
403+
# non-zero) and touch NOTHING. This replaces the PR-22-era
404+
# "auto-elevate" gate: the shim was removed in PR-23, and the
405+
# correct post-PR-23 behaviour for this bare invocation is a
406+
# hard refusal.
404407
# ------------------------------------------------------------------
405-
- name: G3-UN-PLAN-RENDERSno-mutation even without explicit dry-run
408+
- name: G3-UN-CONSENT-REQUIRED--mode=uninstall without consent flag refuses
406409
shell: bash
407410
run: |
408411
set -Eeuo pipefail
409-
# PR-22A boundary repair: the earlier gate only snapshot
410-
# /etc/nftban + /usr/lib/nftban + /usr/sbin/nftban*. It did
411-
# NOT cover /var/lib/nftban/ — the exact directory that leaked
412-
# three writes in PR #480. The snapshot now includes all
413-
# three protected trees. No exceptions.
414412
before=$(find /etc/nftban /usr/lib/nftban /usr/sbin/nftban* /var/lib/nftban -type f 2>/dev/null | sort | xargs -r sha256sum 2>/dev/null | sort || true)
415413
set +e
416-
sudo ./bin/nftban-installer --mode=uninstall \
417-
--state-dir=/var/lib/nftban/state 2>&1 | tee /tmp/uninstall-implicit.out
418-
rc=${PIPESTATUS[0]}
414+
out=$(sudo ./bin/nftban-installer --mode=uninstall \
415+
--state-dir=/var/lib/nftban/state 2>&1)
416+
rc=$?
419417
set -e
418+
echo "$out"
420419
after=$(find /etc/nftban /usr/lib/nftban /usr/sbin/nftban* /var/lib/nftban -type f 2>/dev/null | sort | xargs -r sha256sum 2>/dev/null | sort || true)
421420
if [[ "$before" != "$after" ]]; then
422-
echo "::error::PR-22 contract violation — --mode=uninstall touched protected filesystem"
421+
echo "::error::G3-UN-CONSENT-REQUIRED FAIL — refused run touched protected filesystem"
423422
diff <(echo "$before") <(echo "$after") || true
424423
exit 1
425424
fi
426-
if [[ "$rc" -ne 0 ]]; then
427-
echo "::error::--mode=uninstall (no explicit dry-run) must exit 0 in PR-22 scope"
425+
if [[ "$rc" -eq 0 ]]; then
426+
echo "::error::G3-UN-CONSENT-REQUIRED FAIL — bare --mode=uninstall exited 0, must REFUSE"
427+
exit 1
428+
fi
429+
if ! echo "$out" | grep -qE "requires exactly one of --dry-run OR --confirm-mutation"; then
430+
echo "::error::G3-UN-CONSENT-REQUIRED FAIL — refusal message did not mention the two-flag choice"
431+
exit 1
432+
fi
433+
echo "G3-UN-CONSENT-REQUIRED PASS — bare --mode=uninstall refused cleanly, no filesystem changes"
434+
435+
# ------------------------------------------------------------------
436+
# G3-UN-CONSENT-BOTH-FLAGS (PR-23) — --mode=uninstall with BOTH
437+
# --dry-run AND --confirm-mutation must also refuse. The two
438+
# flags are semantically incompatible; accepting both would be
439+
# a silent contract drift.
440+
# ------------------------------------------------------------------
441+
- name: G3-UN-CONSENT-BOTH-FLAGS — --dry-run + --confirm-mutation refuses
442+
shell: bash
443+
run: |
444+
set -Eeuo pipefail
445+
set +e
446+
out=$(sudo ./bin/nftban-installer --mode=uninstall --dry-run --confirm-mutation \
447+
--state-dir=/var/lib/nftban/state 2>&1)
448+
rc=$?
449+
set -e
450+
echo "$out"
451+
if [[ "$rc" -eq 0 ]]; then
452+
echo "::error::G3-UN-CONSENT-BOTH-FLAGS FAIL — passing both flags exited 0, must REFUSE"
453+
exit 1
454+
fi
455+
if ! echo "$out" | grep -qE "mutually exclusive"; then
456+
echo "::error::G3-UN-CONSENT-BOTH-FLAGS FAIL — refusal message did not mention mutual exclusion"
428457
exit 1
429458
fi
430-
echo "G3-UN-PLAN-RENDERS no-mutation PASS — no files changed under /etc/nftban, /usr/lib/nftban, /usr/sbin/nftban*, /var/lib/nftban"
459+
echo "G3-UN-CONSENT-BOTH-FLAGS PASS — both-flag combination refused"
431460
432461
# ------------------------------------------------------------------
433462
# G3-UN-HISTORY-PURITY — regression guard for audit finding A.3.

0 commit comments

Comments
 (0)