DevSecOps isn't about slowing down releases with security gates—it's about finding vulnerabilities earlier when they're cheaper to fix. A bug found in production costs 100x more to remediate than one caught in development.
This guide shows how to build security into every stage of your CI/CD pipeline without sacrificing deployment velocity.
What Is DevSecOps?
DevSecOps integrates security practices into the DevOps workflow:
- Shift left - Find issues earlier in the development cycle
- Automate - Security checks run automatically, not manually
- Continuous - Every commit and deployment gets scanned
- Collaborative - Security is everyone's responsibility, not just the security team
The goal: deploy faster AND more securely by catching issues before they reach production.
The DevSecOps Pipeline
A mature DevSecOps pipeline includes security at every stage:
[Code] → [Build] → [Test] → [Deploy] → [Monitor]
↓ ↓ ↓ ↓ ↓
SAST SCA DAST IaC Runtime
Secrets Container API Config SIEM
Linting Signing Pentest Policy Alerts
Stage 1: Secure Code (Pre-Commit)
Catch issues before code enters the repository.
Pre-Commit Hooks
Run security checks locally before commits:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: detect-private-key
- id: detect-aws-credentials
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.0
hooks:
- id: gitleaks
- repo: https://github.com/PyCQA/bandit
rev: 1.7.6
hooks:
- id: bandit
args: ["-r", "src/"]
- repo: https://github.com/aquasecurity/tfsec
rev: v1.28.4
hooks:
- id: tfsec
Secret Scanning
Prevent credentials from entering version control:
Gitleaks configuration:
# .gitleaks.toml
title = "Custom Gitleaks Config"
[[rules]]
id = "api-key"
description = "API Key"
regex = '''(?i)(api[_-]?key|apikey)['":\s]*[=:]\s*['"]?([a-z0-9]{32,})['"]?'''
tags = ["api", "key"]
[[rules]]
id = "aws-access-key"
description = "AWS Access Key"
regex = '''AKIA[0-9A-Z]{16}'''
tags = ["aws", "key"]
Stage 2: Static Analysis (Build)
Analyze code without executing it.
SAST (Static Application Security Testing)
Scan source code for vulnerabilities:
GitHub Actions example:
name: Security Scan
on: [push, pull_request]
jobs:
sast:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Semgrep for multi-language SAST
- name: Semgrep Scan
uses: returntocorp/semgrep-action@v1
with:
config: >-
p/security-audit
p/owasp-top-ten
p/cwe-top-25
# CodeQL for deeper analysis
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: javascript, python
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
Popular SAST tools:
| Tool | Languages | Best For |
|---|---|---|
| Semgrep | 30+ languages | Speed, custom rules |
| CodeQL | 10+ languages | Deep analysis, GitHub integration |
| SonarQube | 25+ languages | Quality + security |
| Snyk Code | 10+ languages | Developer experience |
| Checkmarx | 25+ languages | Enterprise, compliance |
SCA (Software Composition Analysis)
Scan dependencies for known vulnerabilities:
- name: Dependency Check
uses: dependency-check/Dependency-Check_Action@main
with:
project: 'MyApp'
path: '.'
format: 'SARIF'
args: >-
--failOnCVSS 7
--enableRetired
- name: Snyk Dependencies
uses: snyk/actions/node@master
with:
args: --severity-threshold=high
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
What SCA finds:
- Known CVEs in dependencies
- Outdated packages
- License compliance issues
- Transitive dependency risks
Stage 3: Container Security (Package)
Secure container images before they ship.
Image Scanning
container-scan:
runs-on: ubuntu-latest
steps:
- name: Build Image
run: docker build -t myapp:${{ github.sha }} .
- name: Trivy Scan
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:${{ github.sha }}'
format: 'sarif'
severity: 'CRITICAL,HIGH'
exit-code: '1'
- name: Grype Scan
uses: anchore/scan-action@v3
with:
image: 'myapp:${{ github.sha }}'
fail-build: true
severity-cutoff: high
Image Signing
Ensure only trusted images deploy:
- name: Sign Image
run: |
cosign sign --key env://COSIGN_KEY \
${{ env.REGISTRY }}/myapp:${{ github.sha }}
env:
COSIGN_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }}
Stage 4: Infrastructure Security (Deploy)
Scan infrastructure configurations before deployment.
IaC Scanning
infrastructure-scan:
runs-on: ubuntu-latest
steps:
- name: Checkov IaC Scan
uses: bridgecrewio/checkov-action@master
with:
directory: terraform/
framework: terraform
output_format: sarif
- name: KICS Scan
uses: checkmarx/[email protected]
with:
path: ./
fail_on: high
output_formats: 'sarif'
Kubernetes Manifest Scanning
- name: Kubesec Scan
uses: controlplaneio/[email protected]
with:
input: k8s/deployment.yaml
- name: Polaris Audit
run: |
polaris audit --audit-path ./k8s/ \
--format json \
--set-exit-code-on-danger
Stage 5: Dynamic Testing (Test)
Test running applications for vulnerabilities.
DAST (Dynamic Application Security Testing)
Scan running applications for OWASP Top 10 issues:
dast:
runs-on: ubuntu-latest
steps:
- name: Start Application
run: docker-compose up -d
- name: Wait for App
run: sleep 30
- name: ZAP Baseline Scan
uses: zaproxy/[email protected]
with:
target: 'http://localhost:8080'
rules_file_name: '.zap/rules.tsv'
- name: ZAP Full Scan
uses: zaproxy/[email protected]
with:
target: 'http://localhost:8080'
API Security Testing
- name: API Security Test
run: |
# Postman/Newman for API tests
newman run api-security-tests.json \
--environment staging.json \
--reporters cli,junit
# OWASP Juice Shop example
nuclei -u http://localhost:8080 \
-t api/ \
-severity critical,high
Stage 6: Runtime Security (Monitor)
Continuous monitoring after deployment.
Security Monitoring Integration
# Send findings to SIEM
- name: Export to SIEM
run: |
# Convert SARIF to your SIEM format
cat results.sarif | jq '.runs[].results[]' | \
while read finding; do
curl -X POST ${{ secrets.SIEM_WEBHOOK }} \
-H "Content-Type: application/json" \
-d "$finding"
done
Continuous Scanning
Schedule regular scans of deployed infrastructure:
name: Scheduled Security Scan
on:
schedule:
- cron: '0 2 * * *' # Daily at 2 AM
jobs:
scan-production:
runs-on: ubuntu-latest
steps:
- name: Scan Production Images
run: |
trivy image --severity HIGH,CRITICAL \
${{ secrets.REGISTRY }}/myapp:production
- name: Cloud Security Posture
run: |
prowler aws --severity high critical \
--output-formats json
Sample Complete Pipeline
name: DevSecOps Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
# Stage 1: Secret Scanning
secrets:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Gitleaks
uses: gitleaks/gitleaks-action@v2
# Stage 2: SAST
sast:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Semgrep
uses: returntocorp/semgrep-action@v1
with:
config: p/security-audit
# Stage 3: SCA
sca:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Snyk
uses: snyk/actions/node@master
with:
args: --severity-threshold=high
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
# Stage 4: Container Scan
container:
needs: [secrets, sast, sca]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build
run: docker build -t myapp:${{ github.sha }} .
- name: Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:${{ github.sha }}'
exit-code: '1'
severity: 'CRITICAL,HIGH'
# Stage 5: IaC Scan
iac:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Checkov
uses: bridgecrewio/checkov-action@master
with:
directory: terraform/
# Stage 6: Deploy (only if all scans pass)
deploy:
needs: [container, iac]
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- name: Deploy to Production
run: echo "Deploying secure code..."
Metrics to Track
| Metric | Target |
|---|---|
| Mean Time to Remediate (MTTR) | < 7 days for critical |
| Vulnerabilities blocked pre-production | > 90% |
| False positive rate | < 10% |
| Pipeline pass rate | > 95% |
| Security scan coverage | 100% of deployments |
Frequently Asked Questions
How do I handle false positives?
False positives are inevitable. Create suppression files for known false positives, regularly review and update them, and track false positive rates as a metric. Most tools support inline comments to suppress specific findings.
Will security scans slow down my pipeline?
Well-designed security scans add 5-15 minutes to pipelines. Run scans in parallel, use incremental scanning where possible, and cache results. The time is worth it compared to fixing production vulnerabilities.
Should security block deployments?
Yes, for critical and high severity findings. Use a tiered approach: block critical issues, warn on medium issues, and log low issues for later review. Gradually increase strictness as your team matures.
How do I get developer buy-in for DevSecOps?
Make security frictionless. Provide clear remediation guidance, integrate findings into existing tools (IDE plugins, PR comments), and celebrate security wins. Developers adopt security when it's helpful, not punitive.
What's the most important security scan to add first?
Start with secret scanning and SCA—they have the highest signal-to-noise ratio and catch issues developers commonly miss. Add SAST next, then container scanning, then IaC scanning.
Take Action
- Add secret scanning - Prevent credentials from entering your repository
- Enable dependency scanning - Catch known CVEs in your dependencies
- Scan container images - Check for vulnerabilities before deployment
- Implement IaC scanning - Catch misconfigurations in infrastructure code
- Track metrics - Measure MTTR, blocked vulnerabilities, and coverage
For more cloud security guidance, see our comprehensive guide: 30 Cloud Security Tips for 2026.
