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.
This commit is contained in:
Jakub Zych
2026-09-24 00:12:28 +02:00
parent eb40d1bdc4
commit 034f63907d

View File

@@ -56,6 +56,7 @@ usage() {
usage: usage:
check-phase8.sh run the complete gate (08-10 Task 3 only) 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 --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 <stage> deliberate RED self-test (08-09 Task 1) check-phase8.sh --red-contract <stage> deliberate RED self-test (08-09 Task 1)
EOF EOF
exit 2 exit 2
@@ -421,6 +422,20 @@ stage_unchanged_client_diff() {
done 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() { stage_security_review() {
local review="$ROOT/.planning/phases/08-oauth2-1-authorization-server/08-SECURITY-REVIEW.md" local review="$ROOT/.planning/phases/08-oauth2-1-authorization-server/08-SECURITY-REVIEW.md"
if [[ ! -f "$review" ]]; then if [[ ! -f "$review" ]]; then
@@ -431,6 +446,17 @@ stage_security_review() {
echo "refuse: 08-SECURITY-REVIEW.md is not status: verified" >&2 echo "refuse: 08-SECURITY-REVIEW.md is not status: verified" >&2
exit 1 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() { run_full_gate() {
@@ -466,6 +492,15 @@ main() {
--contract-self-test) --contract-self-test)
run_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) --red-contract)
[[ $# -ge 2 ]] || usage [[ $# -ge 2 ]] || usage
run_red_contract "$2" run_red_contract "$2"