DevSecOps basics aren’t about adding “more security tools.” They’re about adding the right checks at the right points in CI/CD, with clear ownership and a plan to avoid alert fatigue. This guide shows a lightweight, practical workflow: secrets, dependency/supply-chain risk, SAST/IaC/container scans, and how to turn findings into fixes (instead of noise).
Quickstart: 5 high-impact DevSecOps wins
If you only do a few things, do these. They’re intentionally biased toward low effort and high payoff so you can improve security without turning CI/CD into a slow, flaky gate.
1) Stop secrets before they land in Git
The fastest “security incident” is an accidentally committed token. Add a pre-commit/pre-push secret scan and a CI scan as a backstop.
- Enable a secret scanner locally (fast feedback)
- Scan PRs in CI (prevent bypass)
- Store secrets in a real secret manager (not .env in repos)
2) Review dependencies on every PR
Supply-chain risk lives in third-party packages. Make “what changed?” visible and enforce basic guardrails.
- Run dependency diff review on PRs
- Block known-malicious packages quickly
- Prefer automated updates + small PRs
3) Add a single SAST scan (and tune it)
Start with one SAST tool, keep it stable, and tune rules to your codebase. “More tools” is how teams get alert fatigue.
- Run SAST on pull requests
- Fail the build only on high/critical issues first
- Use a baseline so you don’t inherit old debt as noise
4) Scan IaC + container images where it matters
Misconfigurations ship faster than exploits. Catch them at the manifest/Dockerfile level.
- IaC scan: Terraform/K8s/Helm
- Image scan: OS + app dependencies
- Gate on “easy wins” (privileged pods, latest tags, root)
5) Decide your “fail policy” (this prevents chaos)
Security in CI/CD fails when teams can’t tell what’s urgent. Pick a simple triage policy so developers know what to do next.
| Finding type | Fail PR? | Default action |
|---|---|---|
| Leaked secret (valid token) | Yes | Revoke/rotate immediately, add detection rule |
| Critical vuln with exploit path | Yes | Patch/upgrade, mitigate, add regression test |
| Medium/low vuln | No (initially) | Create backlog ticket, fix in batch updates |
| Misconfiguration (root/privileged/public bucket) | Yes | Fix config, add policy-as-code rule |
Aim for a pipeline that prevents obvious failures (secrets, critical vulns, dangerous configs) and creates useful tickets for everything else. If your first iteration blocks developers all day, DevSecOps won’t survive.
Overview: what DevSecOps is (and what it isn’t)
DevSecOps basics can be summed up as: treat security like a normal part of delivery. That means security checks are automated, reproducible, and close to where changes happen—usually in pull requests and CI.
DevSecOps is…
- Security as code: policies, scans, and gates defined in version control
- Shift-left: catch issues before deploy (cheaper to fix)
- Continuous: scan regularly, not only during audits
- Risk-based: fix what matters most first
DevSecOps is not…
- Running every scanner on every commit (slow + noisy)
- Failing PRs on thousands of legacy findings (instant rebellion)
- “Security’s job” only (ownership needs to be shared)
- One-time hardening (drift happens)
What this post covers
- Where to place controls in CI/CD: secrets, dependencies, SAST, IaC, containers
- How to avoid alert fatigue: baselines, severities, and sane “fail rules”
- How to make findings actionable: ownership, SLAs, and small fixes
- How to think about supply-chain security without drowning in buzzwords
A developer should be able to answer: “Why did this fail?” and “What’s the fix?” in under 5 minutes. If the pipeline can’t do that, it’s noise.
Core concepts: the mental models that keep DevSecOps sane
Most teams don’t fail at DevSecOps because security is “too hard.” They fail because the pipeline becomes slow, noisy, and unclear. These concepts help you build a workflow that scales.
1) The “three layers” model: code → artifact → runtime
Think of software delivery as three layers that need different controls:
| Layer | What it is | Typical controls |
|---|---|---|
| Code | Source + configs + IaC | Secret scanning, SAST, IaC scanning, code review rules |
| Build artifact | Packages, containers, releases | Dependency/SCA scans, SBOM, image scanning, signing |
| Runtime | Deployed system | Runtime policies, monitoring, patch cadence, least privilege |
The goal of CI/CD security is to reduce what reaches runtime, because runtime fixes are slower, riskier, and more expensive.
2) Controls vs gates: “visibility” comes before “blocking”
A control is any automated check that produces a finding. A gate is a control that blocks progress (fails a job/PR/release). Early on, you want more visibility than gates—then you gate only on things that are clear, high risk, and easy to fix.
Good candidates for gates
- Committed secrets or credentials
- Critical vulnerabilities with a known upgrade/fix
- Dangerous IaC patterns (privileged containers, public buckets)
- Unsigned/untrusted artifacts (if you enforce provenance)
Good candidates for “report only”
- Medium/low vulnerabilities (initially)
- Style-level SAST findings until rules are tuned
- Informational config suggestions
- Legacy findings (until you set a baseline)
3) SAST vs SCA: different problems, different outputs
Teams often mix these up, then wonder why results feel random.
| Category | What it finds | Example | Best place in CI/CD |
|---|---|---|---|
| SAST (Static Application Security Testing) | Security bugs in your code | SQL injection pattern, unsafe deserialization | PR checks (fast feedback), nightly deep scans |
| SCA / dependency scanning | Risk in third-party packages | Vulnerable library version, license risk | PR dependency diff + scheduled scans |
4) Secret scanning is a workflow, not a tool
The best “secret scanning” setup has three lines of defense: a local scan (fast), CI scan (enforced), and operational hygiene (rotation + least privilege). Scanning alone is not enough if tokens never expire and services are over-permissioned.
5) Supply-chain basics: know what you ship and where it came from
“Supply-chain security” sounds big, but the basics are approachable: track dependencies, scan artifacts, and add integrity checks. A practical path looks like:
- Dependency review: what changed in this PR?
- SBOM: what’s inside the build artifact?
- Signing/attestation: can we prove this artifact came from our pipeline?
If “security findings” feel endless, the answer is usually not “work harder.” It’s: baseline old issues, gate only on the highest risk, and make fixes small and routine (especially dependency updates).
Step-by-step: add CI/CD security without turning it into a wall
This is a practical sequence you can apply to most teams and codebases. The theme is consistent: start small, make results actionable, and gate only what you can defend.
Step 1 — Define what you’re protecting (a 20-minute threat sketch)
You don’t need a full threat model to start, but you do need a target. Write down the top assets and failure modes so your pipeline focuses on what matters.
- Assets: customer data, credentials, payment flows, admin actions, build secrets
- Attack surfaces: public APIs, auth flows, CI tokens, dependencies, container runtime
- Worst outcomes: data exfiltration, account takeover, production compromise
- Top constraints: build time budget (e.g., +2–5 minutes), “no flaky gates” rule
Step 2 — Map controls to pipeline stages (so you don’t over-scan)
A clean DevSecOps pipeline uses the right checks at the right time. Put “fast checks” on PRs and run deeper scans on schedules or release candidates.
| Stage | What to run | Why it fits here |
|---|---|---|
| Pre-commit / pre-push | Secret scanning, formatting, quick lint | Fastest feedback, prevents “oops” commits |
| Pull request | Dependency review, SAST (tuned), IaC checks | Blocks risky changes before merge |
| Build artifact | SBOM, image scanning, signing | Validates what you actually ship |
| Nightly / scheduled | Deep SAST, full dependency scan, wide IaC scan | Finds issues without slowing PRs |
Step 3 — Add secret scanning (local + CI) and a rotation playbook
Secret scanning is the best first control because it prevents a whole class of incidents with minimal debate. The key is having a clear “what now?” when a secret is detected: revoke/rotate, remove from history if needed, and add guardrails to prevent repeats.
Tokens should be scoped (least privilege) and rotatable. If rotation is painful, developers will avoid it—so incidents become bigger.
Step 4 — Dependency hygiene: make updates routine, not heroic
Most teams either ignore dependency risk or panic-update everything during an incident. A better approach is boring: frequent small updates, a dependency diff on PRs, and clear severity rules.
What to enforce on PRs
- Show dependency changes (new/removed packages)
- Block known malicious packages quickly
- Prefer pinned versions / lockfiles
- Require review for new “core” dependencies
What to handle on a schedule
- Batch medium/low upgrades weekly
- Regenerate lockfiles consistently
- Re-scan repos for new advisories
- Track “time to patch” for critical issues
Step 5 — Add one SAST scan and tune it for signal
SAST is valuable when it catches real bug patterns and is integrated into review. It becomes painful when results are vague, duplicative, or unowned. Start with one scanner, keep it stable, and tune rules until findings feel legitimate.
A simple “SAST success” rubric
- Findings point to a file/line with a concrete remediation
- False positives are manageable (and can be suppressed with justification)
- High-severity issues can fail PRs without constant exceptions
- Engineers can reproduce and validate fixes locally
Step 6 — Scan IaC and containers (and gate on dangerous defaults)
Infrastructure-as-code and container builds are where “small mistakes” become big exposures: public storage, overly permissive IAM, privileged pods, or images full of vulnerabilities. The quickest win is to gate on the top misconfigurations that are almost always wrong.
Step 7 — A practical CI example (PR-focused, low chaos)
Below is an example workflow that combines dependency review, secret scanning, SAST, and container/file system scanning. Adapt it to your CI system and language stack. The important part isn’t the exact tools—it’s the shape: fast checks on PRs, actionable output, and a clear fail policy.
name: Security (PR)
on:
pull_request:
branches: [ "main" ]
permissions:
contents: read
pull-requests: read
security-events: write
concurrency:
group: security-${{ github.ref }}
cancel-in-progress: true
jobs:
security:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
# 1) Dependency diffs on PRs (fast signal)
- name: Dependency review
uses: actions/dependency-review-action@v4
with:
fail-on-severity: high
# 2) Secrets scanning (CI backstop)
- name: Gitleaks scan
uses: gitleaks/gitleaks-action@v2
env:
GITLEAKS_ENABLE_COMMENTS: "false"
# 3) SAST (example with CodeQL)
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: "javascript,python"
- name: Autobuild
uses: github/codeql-action/autobuild@v3
- name: Analyze
uses: github/codeql-action/analyze@v3
with:
category: "/language:multi"
# 4) Scan repo + Dockerfiles/IaC for obvious issues (fast scan)
- name: Trivy filesystem scan
uses: aquasecurity/trivy-action@0.24.0
with:
scan-type: "fs"
scan-ref: "."
severity: "CRITICAL,HIGH"
ignore-unfixed: true
exit-code: "1"
- PR-focused checks are chosen for fast runtime and clear ownership.
- It fails on high-impact issues by default (secrets/high/critical), not everything.
- It can be expanded later (nightly deep scans, SBOM generation, signing) without changing developer habits.
Step 8 — Add a local “pre-push” safety net (optional, very effective)
CI is the ultimate enforcement point, but local checks reduce cycle time. Keep local checks fast and non-flaky. The goal is to catch the obvious mistakes before the PR even exists.
#!/usr/bin/env bash
set -euo pipefail
echo "[devsecops] Running fast pre-push checks..."
# 1) Secrets (prevent accidental token pushes)
if command -v gitleaks >/dev/null 2>&1; then
gitleaks detect --no-git --redact --exit-code 1
else
echo "[warn] gitleaks not found; install it for secret scanning."
fi
# 2) Dependency quick checks (best-effort; adapt to your stack)
if [ -f package-lock.json ] || [ -f pnpm-lock.yaml ] || [ -f yarn.lock ]; then
if command -v npm >/dev/null 2>&1; then
npm audit --audit-level=high || true
fi
fi
if [ -f requirements.txt ] || [ -f poetry.lock ] || [ -f pyproject.toml ]; then
if command -v pip-audit >/dev/null 2>&1; then
pip-audit -l || true
else
echo "[warn] pip-audit not found; consider installing for Python dependency checks."
fi
fi
# 3) Repo scan for high-risk misconfigs (quick)
if command -v trivy >/dev/null 2>&1; then
trivy fs --severity HIGH,CRITICAL --exit-code 1 --ignore-unfixed .
else
echo "[warn] trivy not found; consider installing for quick scans."
fi
echo "[devsecops] Done."
Step 9 — Prevent dangerous configs with policy-as-code (small rules, big wins)
The cleanest way to avoid repeated security review comments is to encode rules. Policy-as-code turns “we should never do that” into “CI blocks it consistently.” Here’s a minimal example policy that rejects Kubernetes manifests that run privileged or as root, and discourages unpinned image tags like latest.
package kubernetes.security
deny[msg] {
input.kind == "Pod"
c := input.spec.containers[_]
c.securityContext.privileged == true
msg := sprintf("privileged container is not allowed: %s", [c.name])
}
deny[msg] {
input.kind == "Pod"
c := input.spec.containers[_]
not c.securityContext.runAsNonRoot
msg := sprintf("containers must set runAsNonRoot=true: %s", [c.name])
}
deny[msg] {
input.kind == "Pod"
c := input.spec.containers[_]
endswith(c.image, ":latest")
msg := sprintf("do not use :latest tag, pin a version: %s", [c.name])
}
- Start in “report only” mode for a week and review false positives.
- Gate on 2–5 rules that are almost always correct (privileged, root, public exposure).
- Document “break glass” exceptions with a time limit (so exceptions expire).
Step 10 — Make findings actionable (ownership + SLAs + less noise)
Tools don’t fix issues—people do. The difference between a useful pipeline and chaos is whether findings are routed, prioritized, and tracked.
A lightweight triage policy you can copy
| Severity | Default SLA | Who owns it | Typical workflow |
|---|---|---|---|
| Critical | 24–72 hours | Service owner + security reviewer | Patch/mitigate immediately, consider hotfix release |
| High | 7–14 days | Service owner | Upgrade dependency, fix code, add test/guardrail |
| Medium | 30–90 days | Service owner | Batch fixes, handle during routine dependency updates |
| Low | Best effort | Backlog | Fix when touching nearby code, or suppress with reason |
Common mistakes (and how to fix them)
DevSecOps goes sideways in predictable ways. Here are the big ones—plus practical fixes that don’t require a giant security team.
Mistake 1 — Turning CI into a “scanner festival”
Adding every tool creates slow builds and duplicated findings. Developers stop trusting results.
- Fix: pick one tool per category (SAST, secrets, deps, IaC/image) and get it stable.
- Fix: run deep scans on schedules, not on every PR.
Mistake 2 — Failing PRs on years of inherited debt
If your first run finds 2,000 issues and you fail the build, the pipeline will be disabled by Friday.
- Fix: baseline existing findings; gate on “new issues only.”
- Fix: run a focused cleanup sprint for the top critical/high items.
Mistake 3 — No ownership (findings go nowhere)
“Security found it” doesn’t mean anyone fixes it. Without routing, alerts become noise.
- Fix: route findings to service owners automatically.
- Fix: define SLAs by severity (even if simple).
Mistake 4 — No “what now?” for secrets
Many teams detect secrets but don’t rotate them quickly, so the risk remains.
- Fix: create a short rotation playbook: revoke → replace → audit → prevent repeat.
- Fix: use least-privilege and short-lived tokens where possible.
Mistake 5 — Treating medium/low vulns as urgent
If everything is urgent, nothing is. Teams will ignore the system entirely.
- Fix: gate on critical/high first; schedule medium/low remediation.
- Fix: focus on exploitability + exposure, not only severity labels.
Mistake 6 — Policy is tribal knowledge
If the rule only lives in someone’s head (or a wiki), it will be forgotten.
- Fix: encode top rules as policy-as-code and enforce in CI.
- Fix: add clear exception paths with time limits (“break glass”).
Flaky gates and unclear failures. If the pipeline blocks a team for hours and nobody can explain why, you’ll get “temporary bypasses” that become permanent.
FAQ
What is the simplest DevSecOps setup that actually works?
Secrets scanning + dependency review + one tuned SAST scan + a small set of IaC/container gates. Keep PR checks fast, fail only on high-impact issues at first, and route everything else to tickets with clear owners.
Should security scans fail the build?
Yes, but selectively. Start by failing builds on issues that are high risk and unambiguous: leaked secrets, critical vulnerabilities with known fixes, and clearly dangerous misconfigurations. Use baselines for legacy debt and roll out stricter gating gradually.
How do we avoid alert fatigue in CI/CD security?
Baseline old findings, reduce duplication, and enforce clear severities. Make sure each tool produces actionable output, and don’t gate on noisy categories until rules are tuned. A “new findings only” policy is often the biggest unlock.
What’s the difference between SAST, SCA, and container scanning?
SAST finds issues in your code, SCA finds risk in third-party dependencies, and container scanning checks the built artifact (OS packages + included libraries). They complement each other and should be placed at different points in the pipeline.
Do small teams need DevSecOps, or is this only for enterprises?
Small teams need it even more because they have less time to respond to incidents. A lightweight pipeline prevents the most common avoidable failures (secrets, risky dependencies, dangerous configs) without requiring a dedicated security department.
How often should we patch dependencies?
Frequently, in small increments. Weekly scheduled updates (plus immediate handling for critical/high items) is a good baseline. The goal is to make patching routine so you’re not doing massive upgrades under pressure.
What if a scan blocks a release but the fix isn’t ready?
Have a documented “break glass” policy. Require an explicit exception, a mitigation plan (feature flag, WAF rule, config hardening, reduced permissions), and a time limit so the exception expires. “We’ll fix later” without a deadline is how risk becomes permanent.
Cheatsheet: DevSecOps basics checklist
Scan this when you’re setting up or reviewing CI/CD security. If you can’t do everything, do the top line items first.
Must-have (start here)
- Secret scanning: local + CI backstop
- Dependency review on PRs
- One SAST tool, tuned for your codebase
- IaC scanning for obvious dangerous defaults
- Fail policy: gate on secrets + critical/high only (initially)
- Ownership: findings routed to service owners
Nice-to-have (next iteration)
- Scheduled deep scans (nightly/weekly)
- SBOM generation for releases
- Artifact signing / provenance attestations
- Policy-as-code for repeatable infra rules
- Dashboards: SLA tracking and worst offenders
- “Break glass” exception process with expiry
A minimal “PR gate” rule set
- Block: committed secrets, critical/high vulns with known fix, privileged/root containers, public exposure configs
- Warn: medium/low vulns, informational SAST findings, non-exploitable issues
- Baseline: existing legacy findings; fail only on new regressions
- Measure: time-to-fix for critical/high issues, plus recurring root causes
Decide how much CI time you’re willing to spend on security checks (e.g., +2 minutes on PRs). Put fast checks on PRs, and run heavier jobs on schedules or release candidates.
Wrap-up: security that ships
The practical goal of DevSecOps basics is not “perfect security.” It’s predictable delivery: fewer preventable incidents, faster fixes when issues are real, and a pipeline developers trust.
If you do one thing today
- Add secret scanning and enforce it in CI
- Decide a clear fail policy (start small)
- Route findings to owners with a default SLA
If you do one thing this week
- Add dependency review on PRs
- Pick one SAST tool and tune it
- Gate on 2–5 dangerous IaC/container patterns
Every control should answer: What does this catch? How often is it true? and What’s the fix? If you can’t answer those, the control will become noise.
Next: explore the related posts below to go deeper on supply-chain attacks, practical threat modeling, and hardening patterns you can apply beyond CI/CD.
Quiz
Quick self-check (demo). This quiz is auto-generated for cyber / security / devsecops.