Text size
Tried & Tested6 min read

Security in the pipeline, not after it

Scanning code, dependencies and images on every commit, without annoying developers.

Security in the pipeline, not after it
AI-generated editorial illustration

For a long time, security worked like this: the team builds something for months, then a security review or penetration test happens just before launch, finds a list of problems, and everyone panics. Fixing things at the end is slow, expensive and stressful.

"Shift left" means moving security checks earlier, into the developer's workflow and the CI pipeline. Problems are found minutes after they're introduced, when they're cheapest to fix. That's the heart of DevSecOps.

The scanner types, simply

There's an alphabet soup of security scanning. Here's what each one does:

  • SAST (Static Application Security Testing): reads your source code looking for insecure patterns. SQL injection, hardcoded secrets, unsafe functions.
  • SCA (Software Composition Analysis): checks your dependencies for known vulnerabilities and license issues.
  • Secret scanning: looks for passwords, API keys and tokens in code and history.
  • IaC scanning: checks Terraform, Kubernetes manifests, Helm charts and Dockerfiles for misconfigurations.
  • Container image scanning: finds vulnerable packages in your images.
  • DAST (Dynamic Application Security Testing): attacks a running application from the outside, like a (polite) hacker would.

The tools I use

You don't need expensive tools to start. Excellent open-source options:

Type Open-source tools Notes
SAST Semgrep, CodeQL CodeQL is free for public repos on GitHub
SCA Trivy, Grype, OSV-Scanner, Dependabot Dependabot is built into GitHub
Secrets gitleaks, TruffleHog, GitHub push protection Scan history once, then every push
IaC Checkov, Trivy, KICS Trivy covers images, deps and IaC in one tool
Images Trivy, Grype Scan in CI and continuously in the registry
DAST OWASP ZAP Run against staging or preview environments

Commercial platforms (Snyk, GitHub Advanced Security, GitLab Ultimate, Wiz, Aikido and many others) bundle these with dashboards, prioritisation and fix suggestions.

A pipeline with security built in

A sensible layout:

  1. Pre-commit (on the developer's machine): secret scanning, fast linting. Catch the obvious before it's even pushed.
  2. Pull request: SAST, SCA, IaC scanning, with results as comments on the PR.
  3. Build: image scanning, SBOM generation, signing.
  4. Staging / preview: DAST against the running app.
  5. Continuously: rescan images and SBOMs in production for newly discovered vulnerabilities.

Trivy alone covers a surprising amount:

trivy fs --scanners vuln,secret,misconfig .
trivy image --severity HIGH,CRITICAL --ignore-unfixed registry.example.com/shop-api:1.4.2

Security findings are like bugs. The sooner you find them, the cheaper they are, and the less anyone panics.

Handling false alarms

This is where most DevSecOps efforts fail. Turn on five scanners with default settings, and developers drown in hundreds of findings. Many are low-risk, unreachable or false positives. After a week, people ignore all of them.

What helps:

  • Start with high and critical findings only. Expand later.
  • Ignore unfixable vulnerabilities for now (there's no patch yet), but track them.
  • Use reachability where tools support it: is the vulnerable function actually called?
  • Tune rules and suppress false positives with a documented reason, in code (like .trivyignore or inline comments).
  • Show only new findings in pull requests, not the whole backlog. Fix the backlog separately.
  • One place for results: GitHub Code Scanning, GitLab's security dashboard, or DefectDojo, instead of five different reports.

Block or just report?

Should security findings fail the pipeline?

My approach:

  • Block on things that are clear-cut and fixable: committed secrets, critical vulnerabilities with a fix available, severe misconfigurations like public buckets or privileged containers.
  • Report everything else, with visibility and a deadline for fixing.
  • Start in report-only mode for new scanners, tune them, then switch to blocking for the most important rules.

Also give developers a way to accept a risk temporarily, with a reason and an expiry date. A hard block with no escape just leads to people disabling the scanner.

Make it a team habit

Tools are half the story. The other half:

  • Security champions in each team who care a bit more and help others.
  • Fix suggestions in the pull request, so developers know what to do.
  • Celebrate fixes, not just count findings.

So, where do I start?

Add secret scanning with push protection, Trivy for dependencies and images, and Semgrep or CodeQL for code. Report only high and critical issues at first. Once the noise is under control, start blocking on the most important ones.

My take: security works best when it's just another check in the pipeline, as normal as a failing test, and just as quick to fix.

— Desly