Lesson 2 of 9 · 20m
Anatomy of a Security-Aware Pipeline
One pipeline, five stages, and the rule that decides where each control lives: fast and deterministic early, slow and probabilistic late.
A CI pipeline has at least five distinct stages, and a control placed in the wrong stage is either useless or hated. Map the stages before you buy or install anything.
The stages and what fits in each
- Pre-commit — seconds of budget. Formatting, linting, and secrets detection (
gitleaks protect). Only controls that are fast and near-zero false positives earn a spot here; anything else trains developers to reach for--no-verify. - Pull request — a few minutes. Incremental SAST on the diff, dependency audit on changed lockfiles, unit-level security tests. This is where blocking makes sense: the change is open and the author is present.
- Post-merge / scheduled — full-repo SAST, deep dependency scans, DAST against a staging deploy. The slow, thorough, occasionally noisy work lives here and reports to a triage queue — not to a red X.
- Build / publish — container image scanning, SBOM generation, artifact signing. These run on the artifact you're about to ship, not the source.
- Runtime — monitoring deployed images and alerting on new CVEs in shipped dependencies. Code doesn't change after deploy, but the threat landscape does: last Tuesday's clean image has three new CVEs today.
A realistic GitHub Actions skeleton for the PR stage:
jobs:
secrets:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- run: gitleaks detect --source . --redact --exit-code 1
sast:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: semgrep ci --sarif --output semgrep.sarif
deps:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: osv-scanner --lockfile=package-lock.jsonThe ordering rule
Fast and deterministic first, slow and probabilistic later. A PR check that takes twenty minutes or flakes half the time will be bypassed within a sprint — I've watched it happen at three different companies, and the developers were right every time.
Field notes
fetch-depth: 0matters: secrets scanners need full history, not just the merge commit.- Run the same scanners in CI even if developers have pre-commit hooks — hooks are opt-out, CI is not.
- Scheduled (cron) scans cover the "new CVE in old code" case; a good pipeline config has both PR triggers and a schedule.