Skip to content

Commit 73af32a

Browse files
committed
ci: Add ASVS re-affirmation gate script
1 parent 595b1ad commit 73af32a

2 files changed

Lines changed: 85 additions & 6 deletions

File tree

.github/scripts/asvs-gate.sh

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
#!/usr/bin/env bash
2+
# OWASP ASVS V12 re-affirmation gate.
3+
#
4+
# Fails the PR if TLS-relevant code changed without an updated audit-log
5+
# entry in docs/security/owasp-asvs.md.
6+
#
7+
# TLS-relevance predicate (OR):
8+
# - changed file matches src/main/java/com/retailsvc/http/internal/.*(Ssl|Tls|Https).*\.java
9+
# - any changed .java file under src/main/java/ adds or removes a line
10+
# importing javax.net.ssl.* or com.sun.net.httpserver.Https*
11+
#
12+
# Local invocation:
13+
# BASE_SHA=$(git merge-base origin/master HEAD) HEAD_SHA=HEAD .github/scripts/asvs-gate.sh
14+
15+
set -euo pipefail
16+
17+
: "${BASE_SHA:?BASE_SHA env var required}"
18+
: "${HEAD_SHA:?HEAD_SHA env var required}"
19+
20+
CHECKLIST="docs/security/owasp-asvs.md"
21+
22+
changed_files=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA")
23+
24+
tls_paths=$(printf '%s\n' "$changed_files" \
25+
| grep -E '^src/main/java/com/retailsvc/http/internal/.*(Ssl|Tls|Https).*\.java$' || true)
26+
27+
import_diff=$(git diff -U0 "$BASE_SHA" "$HEAD_SHA" -- 'src/main/java/**/*.java' \
28+
| grep -E '^[+-]import (javax\.net\.ssl\.|com\.sun\.net\.httpserver\.Https)' || true)
29+
30+
if [ -z "$tls_paths" ] && [ -z "$import_diff" ]; then
31+
echo "ASVS gate: no TLS-relevant changes."
32+
exit 0
33+
fi
34+
35+
triggers=""
36+
if [ -n "$tls_paths" ]; then
37+
triggers+=" (path) $(echo "$tls_paths" | tr '\n' ' ')"$'\n'
38+
fi
39+
if [ -n "$import_diff" ]; then
40+
triggers+=" (import) $(echo "$import_diff" | head -5)"$'\n'
41+
fi
42+
43+
if ! printf '%s\n' "$changed_files" | grep -qx "$CHECKLIST"; then
44+
cat >&2 <<EOF
45+
::error title=ASVS V12 gate::TLS-relevant code changed but $CHECKLIST was not updated.
46+
47+
Triggered by:
48+
$triggers
49+
Required action:
50+
1. Open $CHECKLIST
51+
2. Confirm each ASVS 5.0 L2 control still holds (update Status / Evidence rows if not)
52+
3. Append a dated line to ## Audit log, e.g.:
53+
- **$(date -u +%Y-%m-%d)** — Re-affirmed after change to <file>.java (this PR); all controls hold
54+
55+
This gate exists so TLS changes can't silently drift away from the documented controls.
56+
See $CHECKLIST for the policy.
57+
EOF
58+
exit 1
59+
fi
60+
61+
added_audit_line=$(git diff "$BASE_SHA" "$HEAD_SHA" -- "$CHECKLIST" \
62+
| grep -E '^\+- \*\*[0-9]{4}-[0-9]{2}-[0-9]{2}\*\* — ' || true)
63+
64+
if [ -z "$added_audit_line" ]; then
65+
cat >&2 <<EOF
66+
::error title=ASVS V12 gate::$CHECKLIST was updated but no new audit-log line was added.
67+
68+
Triggered by:
69+
$triggers
70+
The gate requires a new line in the ## Audit log section matching:
71+
- **YYYY-MM-DD** — <free-text re-affirmation, e.g. "Re-affirmed after change to X.java (this PR); all controls hold">
72+
73+
Touch-only changes to the checklist do not satisfy the gate. The dated line must be added.
74+
EOF
75+
exit 1
76+
fi
77+
78+
echo "ASVS gate: TLS-relevant changes re-affirmed in $CHECKLIST."
79+
exit 0

docs/superpowers/plans/2026-05-21-owasp-asvs.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -211,13 +211,13 @@ SKIP=commitlint git commit -m "chore: Add OWASP ASVS 5.0 L2 self-assessment for
211211

212212
- Create: `.github/scripts/asvs-gate.sh` (executable, mode 0755)
213213

214-
- [ ] **Step 1: Ensure the scripts directory exists**
214+
- [x] **Step 1: Ensure the scripts directory exists**
215215

216216
```bash
217217
mkdir -p .github/scripts
218218
```
219219

220-
- [ ] **Step 2: Write the gate script**
220+
- [x] **Step 2: Write the gate script**
221221

222222
Write `.github/scripts/asvs-gate.sh`:
223223

@@ -303,13 +303,13 @@ echo "ASVS gate: TLS-relevant changes re-affirmed in $CHECKLIST."
303303
exit 0
304304
```
305305

306-
- [ ] **Step 3: Make the script executable**
306+
- [x] **Step 3: Make the script executable**
307307

308308
```bash
309309
chmod 0755 .github/scripts/asvs-gate.sh
310310
```
311311

312-
- [ ] **Step 4: Smoke-test the script against the current branch**
312+
- [x] **Step 4: Smoke-test the script against the current branch**
313313

314314
The branch will have modified `TlsHttpsConfigurator`-related paths (Task 1 created the new internal file → matches the path-glob predicate), and Task 2 already added an audit-log line. So the gate must say "re-affirmed".
315315

@@ -325,7 +325,7 @@ ASVS gate: TLS-relevant changes re-affirmed in docs/security/owasp-asvs.md.
325325

326326
(Exit code 0.)
327327

328-
- [ ] **Step 5: Smoke-test the failure paths via temporary commits**
328+
- [x] **Step 5: Smoke-test the failure paths via temporary commits**
329329

330330
The gate compares `BASE_SHA..HEAD_SHA`, so we exercise the negative branches by stacking one or two throwaway commits on the current branch and pointing `BASE_SHA` at the right ancestor. Throwaway commits use `-c core.hooksPath=/dev/null` to bypass pre-commit (they never leave the local repo).
331331

@@ -367,7 +367,7 @@ Expected: the script prints "$CHECKLIST was updated but no new audit-log line wa
367367

368368
If either scenario unexpectedly passes the gate (exit 0), the script's logic has a hole — STOP and debug before continuing.
369369

370-
- [ ] **Step 6: Commit the script**
370+
- [x] **Step 6: Commit the script**
371371

372372
```bash
373373
git add .github/scripts/asvs-gate.sh

0 commit comments

Comments
 (0)