ci(devsecops): enforce guardrail gates in blocking mode (#554)

Phase C enforcement for the DevSecOps Guardrails epic (jmservera/SquadScope-Coordinator#33), closes #545. Flips the Phase-A warning-only jobs to blocking now that the Phase B baseline (#543) is clean. - lint.yml: drop job/step continue-on-error; `ruff check .` is blocking and a new `ruff format --check .` step enforces formatter-owned line length. - checkov.yml: drop --soft-fail and continue-on-error; new IaC/Actions misconfigurations fail the build. Accepted findings use justified inline `# checkov:skip`. SARIF upload retained (if: always()). - security-scanning.yml: drop continue-on-error on zizmor-scan; default-persona findings now fail the build. - pytest already blocks via the `CI / Python` job (no change needed). Updated docs/devsecops/*-baseline.md Phase C status. Verified locally: ruff, checkov (no --soft-fail), zizmor (regular persona), and `pytest tests/` (1209 passed) are all green. Repo admins: mark these as required checks in branch protection — `Lint / Ruff`, `Checkov / Checkov IaC/container scan`, `Security Scanning / GitHub Actions Security Scan (zizmor)`, `CI / Python`. Depends on #543 and #544. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Juan Manuel Servera committed Jun 27, 2026 at 00:35 UTC 27aca33c951772fd6f3a3cdd8674af2630401961
6 files changed +40 -35
.github/workflows/checkov.yml
+16 -14
@@ -1,10 +1,11 @@
1 -# Checkov lint — Phase A (warning-only / non-blocking)
1 +# Checkov lint — Phase C (blocking / enforced)
2 #
3 -# DevSecOps Guardrails epic (jmservera/SquadScope-Coordinator#33), issue #541.
4 -# Scans IaC, containers and GitHub Actions workflows for misconfigurations.
5 -# Phase A is NON-BLOCKING: the job uses --soft-fail and continue-on-error, and
6 -# uploads SARIF to Code Scanning for visibility. Findings are not fixed
7 -# (Phase B) nor enforced (Phase C) here.
3 +# DevSecOps Guardrails epic (jmservera/SquadScope-Coordinator#33), issues #541
4 +# (baseline), #543 (Phase B fixes), #545 (Phase C enforcement). The Phase B
5 +# baseline is clean (0 failed; 4 justified inline skips), so this job is now
6 +# BLOCKING: it fails the build on any new IaC/container/Actions misconfiguration.
7 +# Accepted findings must carry a justified inline `# checkov:skip=<id>:<reason>`.
8 +# Mark "Checkov / Checkov IaC/container scan" as a required check.
9
10 name: Checkov
11
@@ -25,10 +26,8 @@ concurrency:
26
27 jobs:
28 checkov:
28 - name: Checkov IaC/container scan (warning-only)
29 + name: Checkov IaC/container scan
30 runs-on: ubuntu-latest
30 - # Phase A: non-blocking. Report only — never fail the build.
31 - continue-on-error: true
31 permissions:
32 contents: read
33 security-events: write
@@ -46,7 +45,9 @@ jobs:
45 - name: Install Checkov (pinned)
46 run: python -m pip install checkov==3.2.533
47
49 - - name: Run Checkov (soft-fail, SARIF)
48 + - name: Run Checkov (blocking, SARIF)
49 + # Phase C: blocking. No --soft-fail — a failed check fails the build.
50 + # Accepted findings must use a justified inline `# checkov:skip`.
51 run: |
52 checkov \
53 --directory . \
@@ -57,18 +58,19 @@ jobs:
58 --skip-path resources \
59 --skip-path themes \
60 --compact \
60 - --soft-fail \
61 --output cli \
62 --output sarif \
63 --output-file-path console,checkov-results.sarif
64
65 - - name: Checkov baseline summary
65 + - name: Checkov summary
66 if: always()
67 run: |
68 {
69 - echo "### Checkov baseline (warning-only)"
69 + echo "### Checkov scan"
70 echo ""
71 - echo "See the job log for the full report. Findings are non-blocking in Phase A."
71 + echo "See the job log for the full report. This check is blocking (Phase C):"
72 + echo "new misconfigurations fail the build; accepted findings carry a"
73 + echo "justified inline \`# checkov:skip=<id>:<reason>\`."
74 } >> "$GITHUB_STEP_SUMMARY"
75
76 - name: Upload SARIF to GitHub Code Scanning
.github/workflows/lint.yml
+13 -13
@@ -1,9 +1,9 @@
1 -# Ruff lint — Phase A (warning-only / non-blocking)
1 +# Ruff lint — Phase C (blocking / enforced)
2 #
3 -# DevSecOps Guardrails epic (jmservera/SquadScope-Coordinator#33), issue #540.
4 -# This job reports Python lint violations as GitHub annotations only. It is
5 -# intentionally NON-BLOCKING (continue-on-error) for Phase A — it must never
6 -# fail a build. Enforcement is deferred to Phase C.
3 +# DevSecOps Guardrails epic (jmservera/SquadScope-Coordinator#33), issues #540
4 +# (baseline), #543 (Phase B fixes), #545 (Phase C enforcement). The Phase B
5 +# baseline is clean, so this job is now BLOCKING: it fails the build on any new
6 +# lint violation or unformatted code. Mark "Lint / Ruff" as a required check.
7
8 name: Lint
9
@@ -24,10 +24,8 @@ concurrency:
24
25 jobs:
26 ruff:
27 - name: Ruff (warning-only)
27 + name: Ruff
28 runs-on: ubuntu-latest
29 - # Phase A: non-blocking. Annotations only — do not fail the build.
30 - continue-on-error: true
29 steps:
30 - name: Checkout code
31 uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
@@ -43,16 +41,18 @@ jobs:
41 run: python -m pip install ruff==0.15.7
42
43 - name: Ruff check (GitHub annotations)
46 - # Phase A: warning-only. Tolerate violations so the check stays green
47 - # while still emitting annotations + the statistics summary below.
48 - continue-on-error: true
44 + # Phase C: blocking. Fails the build on any lint violation.
45 run: ruff check . --output-format=github
46
51 - - name: Ruff baseline statistics (summary)
47 + - name: Ruff format check
48 + # Line length is owned by the formatter; enforce it here (blocking).
49 + run: ruff format --check .
50 +
51 + - name: Ruff statistics (summary)
52 if: always()
53 run: |
54 {
55 - echo "### Ruff baseline (warning-only)"
55 + echo "### Ruff statistics"
56 echo ""
57 echo '```'
58 ruff check . --statistics || true
.github/workflows/security-scanning.yml
+5 -5
@@ -88,11 +88,11 @@ jobs:
88 contents: read
89 security-events: write
90 actions: read
91 - # Phase A (DevSecOps Guardrails epic, issue #542): warning-only.
92 - # continue-on-error keeps zizmor NON-BLOCKING; findings surface as Code
93 - # Scanning alerts/annotations only. Enforcement is deferred to Phase C.
94 - # See docs/devsecops/zizmor-baseline.md for the current baseline.
95 - continue-on-error: true
91 + # Phase C (DevSecOps Guardrails epic, issue #545): BLOCKING.
92 + # continue-on-error has been dropped so zizmor findings (default persona)
93 + # fail the build. The Phase B baseline (#543) is clean on the default
94 + # persona, so this gate is green today and catches new findings going
95 + # forward. See docs/devsecops/zizmor-baseline.md.
96 steps:
97 - name: Checkout repository
98 uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
docs/devsecops/checkov-baseline.md
+2 -1
@@ -65,4 +65,5 @@ checkov --file .github/workflows/ci.yml
65
66 - **Phase A:** baseline + non-blocking CI + SARIF upload. ✅
67 - **Phase B:** triage findings; justified suppressions or fixes. ✅ 0 failed (4 justified skips).
68 -- **Phase C:** blocking required status check (#545) for new misconfigurations.
68 +- **Phase C:** blocking CI gate (#545). ✅ The Checkov job dropped `--soft-fail`
69 + + `continue-on-error`; new misconfigurations fail the build. Mark it required.
docs/devsecops/ruff-baseline.md
+2 -1
@@ -66,4 +66,5 @@ ruff format .
66
67 - **Phase A:** baseline + non-blocking CI annotations. ✅
68 - **Phase B:** fix violations — ✅ all categories resolved; `ruff check`/`ruff format --check` clean.
69 -- **Phase C:** pre-push hooks (#544) + blocking required status check (#545).
69 +- **Phase C:** pre-push hooks (#544) + blocking CI gate (#545). ✅ `Lint / Ruff`
70 + runs `ruff check` and `ruff format --check` in blocking mode; mark it required.
docs/devsecops/zizmor-baseline.md
+2 -1
@@ -78,4 +78,5 @@ zizmor $(find .github/workflows -maxdepth 1 -type f \
78
79 - **Phase A:** confirm non-blocking + SARIF wiring; record baseline. ✅
80 - **Phase B:** fix High-severity excessive-permissions + concurrency-limits. ✅ (info/low nits deferred above)
81 -- **Phase C:** blocking enforcement — drop `continue-on-error` (#545).
81 +- **Phase C:** blocking enforcement (#545). ✅ Dropped `continue-on-error` on the
82 + `zizmor-scan` job; default-persona findings now fail the build. Mark it required.