Skip to content

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.

Pipeline control map

The stages and what fits in each

  1. 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.
  2. 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.
  3. 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.
  4. Build / publish — container image scanning, SBOM generation, artifact signing. These run on the artifact you're about to ship, not the source.
  5. 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.json

The 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: 0 matters: 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.