Introduction
Traditional security approachesโwhere security teams review code only at the end of developmentโare no longer sufficient. Modern development teams ship code multiple times per day, making manual security reviews impossible. DevSecOps solves this by “shifting security left”โintegrating security into every phase of the development lifecycle.
This guide covers how to build security into your CI/CD pipeline, protecting your applications from code to production.
What Is DevSecOps?
The Basic Concept
DevSecOps integrates security practices into DevOps processes, ensuring security is everyone’s responsibility, not just the security team’s. It automates security checks, making them as routine as unit tests.
Key Terms
- Shift-Left: Moving security earlier in the development lifecycle
- SAST: Static Application Security Testing (analyzing source code)
- DAST: Dynamic Application Security Testing (analyzing running applications)
- SCA: Software Composition Analysis (finding vulnerable dependencies)
- Container Scanning: Checking container images for vulnerabilities
- Secret Scanning: Finding exposed credentials in code
- SBOM: Software Bill of Materials
Why DevSecOps Matters in 2025-2026
| Security Approach | Vulnerabilities Found | Time to Fix | Cost |
|---|---|---|---|
| Traditional (end-of-cycle) | 30% | Weeks | High |
| DevSecOps (automated) | 80%+ | Hours | Low |
Security in CI/CD Pipeline
Pipeline Stages
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ CI/CD Pipeline โ
โโโโโโโโโโโโโโฌโโโโโโโโโโโโโฌโโโโโโโโโโโโโฌโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโค
โ Build โ Test โ Staging โ Deploy โ Runtime โ
โโโโโโโโโโโโโโผโโโโโโโโโโโโโผโโโโโโโโโโโโโผโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโค
โ SAST โ SCA โ DAST โ Container โ Runtime โ
โ Linting โ Integration| Pen Test โ Scanning โ Protection โ
โ Secrets โ Tests โ API Sec โ Signing โ WAF โ
โ SBOM โ Unit Tests โ โ Policy โ Monitoring โ
โโโโโโโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโ
Implementation
Step 1: SAST - Static Analysis
# .github/workflows/sast.yml
name: Static Application Security Testing
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Semgrep
uses: returntocorp/semgrep-action@v1
with:
config: auto
- name: Run Bandit
run: |
pip install bandit
bandit -r ./src -f json -o bandit-report.json
- name: Run SonarCloud
uses: SonarSource/sonarcloud-github-action@master
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
Step 2: Dependency Scanning (SCA)
# .github/workflows/sca.yml
name: Dependency Scanning
on:
push:
branches: [main]
schedule:
- cron: '0 0 * * 0' # Weekly
jobs:
dependency-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: OWASP Dependency Check
uses: dependency-check/dependency-check-action@v2
with:
project: 'my-app'
assembly: 'false'
format: 'JSON'
output: './reports'
- name: npm audit
run: npm audit --json > npm-audit.json
- name: Safety (Python)
run: |
pip install safety
safety check --json > safety-report.json
- name: Trivy (Containers)
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
severity: 'CRITICAL,HIGH'
format: 'json'
Step 3: Secret Scanning
# .github/workflows/secrets.yml
name: Secret Scanning
on:
push:
branches: [main, develop]
pull_request:
jobs:
secrets-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: GitLeaks
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: TruffleHog
uses: trufflesecurity/trufflehog@main
with:
base: ${{ github.event.repository.default_branch }}
head: HEAD
extra_args: --json
Step 4: Container Scanning
# .github/workflows/container-scan.yml
name: Container Security
on:
push:
branches: [main]
tags: ['v*']
jobs:
build-and-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build container
run: |
docker build -t myapp:${{ github.sha }} .
- name: Trivy Scan
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:${{ github.sha }}'
severity: 'CRITICAL,HIGH'
exit-code: '1'
- name: Hadolint
run: |
docker run --rm -v ${{ github.workspace }}:/src hadolint/hadolint hadolint /src/Dockerfile
- name: Sign container
uses: sigstore/cosign-installer@v3
with:
cosign-release: 'v2.0.0'
- name: Sign and verify
run: |
cosign sign --yes myregistry.io/myapp:${{ github.sha }}
Step 5: DAST - Dynamic Analysis
# .github/workflows/dast.yml
name: Dynamic Security Testing
on:
push:
branches: [main]
schedule:
- cron: '0 2 * * *' # Nightly
jobs:
dast-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Start staging environment
run: |
docker-compose up -d
sleep 10
- name: OWASP ZAP Scan
uses: zaproxy/[email protected]
with:
target: 'http://localhost:8080'
- name: Nuclei Scan
run: |
go install github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
nuclei -list endpoints.txt -json -output nuclei-report.json
- name: Cleanup
if: always()
run: docker-compose down
Security Policies
###OPA Gatekeeper Policy
# policies/database.rego
package database
deny[msg] {
input.kind == "Deployment"
input.spec.template.spec.containers[_].env[_].valueFrom.secretKeyRef
msg = "Direct secret references in environment variables are not allowed. Use secrets mounting instead."
}
deny[msg] {
input.kind == "Deployment"
not input.spec.template.spec.securityContext
msg = "Security context must be defined for all containers."
}
deny[msg] {
input.kind == "Service"
input.spec.type == "LoadBalancer"
not input.spec.loadBalancerSourceRanges
msg = "LoadBalancer services must specify source IP ranges."
}
# Apply policy
kubectl apply -f - <<EOF
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
name: require-security-labels
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Namespace"]
parameters:
labels:
- key: "security-level"
- key: "data-classification"
EOF
Best Practices
1. Fail the Build for Critical Issues
# โ
Good: Critical issues fail the build
trivy:
image-ref: 'myapp:latest'
severity: 'CRITICAL,HIGH'
exit-code: '1' # Fail on critical/high
# โ ๏ธ Warning: Only fail for critical
trivy:
severity: 'CRITICAL'
exit-code: '1'
2. Use Trusted Base Images
# โ
Good: Specific version, minimal
FROM python:3.12-slim@sha256:abc123
# โ Bad: Moving tag
FROM python:3.12-slim
# โ Bad: Full tag
FROM python:3.12.0
3. Scan Infrastructure as Code
# .github/workflows/tfsec.yml
name: Terraform Security
on:
push:
paths:
- '**.tf'
- '**.tfvars'
jobs:
tfsec:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run tfsec
uses: aquasecurity/[email protected]
with:
soft_fail: true
4. Generate SBOM
# .github/workflows/sbom.yml
name: Software Bill of Materials
on:
push:
branches: [main]
workflow_dispatch:
jobs:
sbom:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Generate SBOM
uses:anchore/sbom-action@v0
with:
output-file: sbom.spdx.json
- name: Upload SBOM
uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.spdx.json
Common Pitfalls
1. Too Many False Positives
Wrong:
# Scans everything with default rules
trivy:
config: default
# No filtering = alert fatigue
Correct:
# Filter known issues
trivy:
vuln-type: 'os,library'
severity: 'CRITICAL,HIGH'
ignore-unfixed: true
exception:
- id: 'CVE-2021-12345'
reason: 'Mitigated by WAF'
2. Blocking Deployments for Warnings
Wrong:
# Fail for any finding
trivy:
exit-code: '1' # Any severity
Correct:
# Only block critical
trivy:
severity: 'CRITICAL'
exit-code: '1'
# Warnings: create issues but don't block
trivy:
severity: 'HIGH'
exit-code: '0' # Continue but warn
3. Not Fixing Findings
Wrong:
# Scan but ignore results
trivy:
severity: 'CRITICAL'
# No action on results!
Correct:
# Create tracking issues
trivy:
severity: 'CRITICAL'
create-issue: true
issue-labels: 'security,needs-triage'
Tools Comparison
| Category | Tools | Best For |
|---|---|---|
| SAST | Semgrep, SonarQube, Bandit | Code analysis |
| SCA | Dependabot, Snyk, Safety | Dependencies |
| DAST | OWASP ZAP, Nuclei | Running apps |
| Containers | Trivy, Clair, Hadolint | Images |
| Secrets | GitLeaks, TruffleHog | Credentials |
| IaC | tfsec, checkov, Terrascan | Terraform/K8s |
External Resources
Official Documentation
Tools
Learning Resources
Key Takeaways
- DevSecOps integrates security throughout the development lifecycle
- Shift-left moves security earlier in the process
- SAST/SCA/DAST cover different security testing needs
- Automate security checks in CI/CD pipelines
- Don’t block deployments for warningsโonly critical issues
- Generate SBOM for supply chain security
- False positives lead to alert fatigueโtune your tools
Comments