From 034f63907dfcd974bb019e44c37af6bb4c695ab4 Mon Sep 17 00:00:00 2001 From: Jakub Zych Date: Thu, 24 Sep 2026 00:12:28 +0200 Subject: [PATCH] feat(08-10): fail-closed 08-SECURITY-REVIEW.md checks in check-phase8.sh stage_security_review now also refuses a nonzero threats_open count and any missing required T-08-* threat row, not just a missing/unverified file. Adds --security-review-only, a focused mode running just this stage (Task 2's own verify command) with no services booted. --- scripts/check-phase8.sh | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/scripts/check-phase8.sh b/scripts/check-phase8.sh index e8f3b3a..a35cf21 100755 --- a/scripts/check-phase8.sh +++ b/scripts/check-phase8.sh @@ -56,6 +56,7 @@ usage() { usage: check-phase8.sh run the complete gate (08-10 Task 3 only) check-phase8.sh --contract-self-test syntax/source assertions only + check-phase8.sh --security-review-only fail-closed 08-SECURITY-REVIEW.md checks only (08-10 Task 2) check-phase8.sh --red-contract deliberate RED self-test (08-09 Task 1) EOF exit 2 @@ -421,6 +422,20 @@ stage_unchanged_client_diff() { done } +PHASE8_REQUIRED_THREATS=( + T-08-PKCE + T-08-CODE-REPLAY + T-08-REFRESH-REPLAY + T-08-OPEN-REDIRECT + T-08-SECRET-TIMING + T-08-SCOPE-CEILING + T-08-CROSS-USER + T-08-REQUEST-LEAK + T-08-DCR-FLOOD + T-08-SURFACE + T-08-SC +) + stage_security_review() { local review="$ROOT/.planning/phases/08-oauth2-1-authorization-server/08-SECURITY-REVIEW.md" if [[ ! -f "$review" ]]; then @@ -431,6 +446,17 @@ stage_security_review() { echo "refuse: 08-SECURITY-REVIEW.md is not status: verified" >&2 exit 1 } + grep -qE "^threats_open: 0$" "$review" || { + echo "refuse: 08-SECURITY-REVIEW.md does not declare threats_open: 0" >&2 + exit 1 + } + local t + for t in "${PHASE8_REQUIRED_THREATS[@]}"; do + grep -q -- "$t" "$review" || { + echo "refuse: 08-SECURITY-REVIEW.md is missing required threat row $t" >&2 + exit 1 + } + done } run_full_gate() { @@ -466,6 +492,15 @@ main() { --contract-self-test) run_contract_self_test ;; + --security-review-only) + # 08-10-PLAN.md Task 2's own focused verify: the fail-closed + # 08-SECURITY-REVIEW.md checks only, no services booted. Not part + # of the complete gate's stage sequence; kept out of + # PHASE8_STAGES/run_full_gate so --contract-self-test's "no + # pre-final full-run mode" check does not need to special-case it. + stage_security_review + echo "phase8 security-review-only check passed" + ;; --red-contract) [[ $# -ge 2 ]] || usage run_red_contract "$2"