--- phase: 16-ci-dependency-audit-and-security-checks plan: "05" subsystem: ci tags: [gitea-ci, gitleaks, security, pnpm-audit, dependency-audit, gate] dependency_graph: requires: ["16-02", "16-03", "16-04"] provides: ["security job in ci.yml", "gate wired with security check"] affects: [".gitea/workflows/ci.yml"] tech_stack: added: [] patterns: ["security CI job parallel to fast-checks", "individual needs.X.result check (Gitea #31007)", "base.sha probe with git merge-base fallback"] key_files: modified: - path: .gitea/workflows/ci.yml role: CI workflow — security job added; gate aggregator updated decisions: - "D-12: gitleaks always runs on every PR via unconditional steps (no job-level if:); only pnpm audit/outdated are code-gated at step level" - "D-14/D-15: security wired into gate with individual needs.security.result check — must be 'success', not 'success-or-skipped', because gitleaks always runs" - "A2/OQ-1: base.sha probe step with git merge-base fallback guards against empty base.sha on some Gitea versions" metrics: duration: 7 completed: "2026-06-13T12:23:36Z" tasks_completed: 2 files_modified: 1 --- # Phase 16 Plan 05: CI Security Job — Summary **One-liner:** Dedicated `security` CI job (gitleaks always + audit/outdated code-gated) wired into the `gate` aggregator with an individual `needs.security.result` success check. ## What Was Built A new `security` job was added to `.gitea/workflows/ci.yml`, placed between `harness` and `gate` in the file order (runs in parallel with `fast-checks`). The job: - Runs on every PR (`if: github.event_name == 'pull_request'`), `needs: [changes]` - Uses `actions/checkout@v4` with `fetch-depth: 0` (Pitfall 3 — base.sha must be locally present) - Has a "Probe PR base/head SHA" step that reads `github.event.pull_request.base.sha` and falls back to `git merge-base origin/${{ github.base_ref }} HEAD` if empty (Assumption A2 / OQ-1), exporting `BASE_SHA` and `HEAD_SHA` to `$GITHUB_ENV` - Installs gitleaks v8.30.1 from GitHub releases (pinned, no `actions/cache` per D-PROBE-04), then runs `gitleaks git --log-opts="--no-merges ${BASE_SHA}..${HEAD_SHA}" --config .gitleaks.toml --baseline-path scripts/gitleaks-baseline.json --exit-code 1` — both gitleaks steps have **no `if:`** (D-12: blocking on every PR) - The pnpm setup-node / corepack / install / check-audit / check-outdated steps each carry `if: needs.changes.outputs.code == 'true'` (step-level, so gitleaks still runs on doc-only PRs) The `gate` aggregator was updated: - `needs:` expanded to `[fast-checks, changes, api, harness, security]` - An individual `if [ "${{ needs.security.result }}" != "success" ]` check was inserted between the fast-checks check and the api/harness for loop - Security is **not** folded into the for loop — it must always succeed (cannot be skipped) ## Commits | Hash | Message | Files | |------|---------|-------| | 61b7586 | ci(16-05): add security job (gitleaks always; audit/outdated code-gated) | .gitea/workflows/ci.yml | | f0f7d8a | ci(16-05): wire security job into gate aggregator | .gitea/workflows/ci.yml | ## Deviations from Plan None — plan executed exactly as written. The task described a "Probe PR base/head SHA" step as a separate explicit step (per PLAN.md acceptance criteria A2/OQ-1). This matches the PLAN.md requirement and was implemented accordingly. The PATTERNS.md skeleton showed a simpler inline version; the PLAN.md explicitly required the probe step with fallback, so the PLAN.md was authoritative. ## Threat Coverage | Threat | Mitigation | Status | |--------|-----------|--------| | T-16-15 — secret introduced in PR diff | gitleaks runs unconditionally, gate blocks on non-success | Mitigated | | T-16-16 — unwaived High/Critical advisory merges to main | check-audit.mjs code-gated, gate blocks | Mitigated | | T-16-17 — gitleaks scans nothing (empty range) | fetch-depth:0 + base.sha probe + merge-base fallback | Mitigated | | T-16-SC — gitleaks binary substitution | version pinned to 8.30.1 from gitleaks/gitleaks official releases | Accepted residual (no checksum) | ## Known Stubs None. ## Threat Flags None — this plan adds only CI workflow steps and does not introduce new network endpoints, auth paths, or schema changes. ## Self-Check: PASSED - `.gitea/workflows/ci.yml` — FOUND - Commit 61b7586 (add security job) — FOUND - Commit f0f7d8a (wire gate) — FOUND - `16-05-SUMMARY.md` — FOUND