devsecops
DevSecOps Pipeline: Security in CI/CD
Advanced
17 minutes
October 2026
CloudOpsGuide Team
DevSecOps Pipeline: Security in CI/CD
Build a security-first CI/CD pipeline with SAST, DAST, dependency scanning, container scanning, and secrets detection — from commit to production.
Table of Contents
- Pipeline Security Layers
- Stage-by-Stage Security
- Complete Pipeline Example
- Tool Selection Matrix
- Quality Gates
- Best Practices
Pipeline Security Layers
Commit ──► Pre-commit ──► SAST ──► Deps ──► Build ──► Container ──► DAST ──► Deploy
(secrets) scan scan image scan test monitor
Each layer catches different threats — a complete pipeline layers them all.
Stage-by-Stage Security
1. Pre-Commit — Secrets Detection
Stop secrets before they enter the repo:
# Install gitleaks pre-commit hook
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash
gitleaks git --pre-commit --staged --verbose
if [ $? -ne 0 ]; then
echo "⚠️ Secret detected! Commit blocked."
exit 1
fi
EOF
chmod +x .git/hooks/pre-commit
2. SAST — Static Analysis
Scan source code for vulnerabilities:
stage('SAST') {
steps {
sh 'semgrep scan --config auto . --sarif -o semgrep.sarif'
sh 'sonar-scanner -Dsonar.projectKey=myapp -Dsonar.sources=.'
}
}
3. Dependency Scanning
Check third-party packages for CVEs:
# npm
npm audit --audit-level=high
# Python
pip install safety && safety check
# Go
go list -m all | nancy sleuth
# Multi-language — Snyk
snyk test --all-projects
4. Container Scanning
Scan built images before pushing:
# Trivy — fastest option
trivy image myapp:latest --severity HIGH,CRITICAL
# Grype — alternative
grype myapp:latest --fail-on high
5. DAST — Dynamic Testing
Test the running application:
# OWASP ZAP baseline scan
docker run -t owasp/zap2docker-stable zap-baseline.py \
-t http://staging.example.com -r zap-report.html
6. Secrets in CI/CD
# Scan repo history for leaked secrets
gitleaks git --verbose .
trufflehog git file://. --only-verified
Complete Pipeline Example
pipeline {
agent any
stages {
stage('Checkout') {
steps { checkout scm }
}
// Stage 1: Secrets Scan
stage('Secrets Scan') {
steps {
sh 'gitleaks git --verbose . || true'
sh 'trufflehog git file://. --only-verified || true'
}
}
// Stage 2: SAST
stage('SAST') {
parallel {
stage('Semgrep') {
steps {
sh 'semgrep scan --config auto . --sarif -o semgrep.sarif || true'
}
}
stage('SonarQube') {
steps {
sh 'sonar-scanner -Dsonar.projectKey=myapp -Dsonar.sources=. || true'
}
}
}
}
// Stage 3: Dependencies
stage('Dependency Scan') {
steps {
sh 'npm audit --audit-level=high || true'
sh 'snyk test --severity-threshold=high || true'
}
}
// Stage 4: Build
stage('Build') {
steps {
sh 'docker build -t myapp:${BUILD_NUMBER} .'
}
}
// Stage 5: Container Scan
stage('Container Scan') {
steps {
sh '''
trivy image myapp:${BUILD_NUMBER} \
--severity HIGH,CRITICAL \
--exit-code 0 \
-o trivy-report.txt
'''
}
}
// Stage 6: Deploy to Staging
stage('Deploy Staging') {
when { branch 'main' }
steps {
sh 'kubectl apply -f k8s/staging/ -n staging'
}
}
// Stage 7: DAST
stage('DAST') {
when { branch 'main' }
steps {
sh '''
docker run --rm -t owasp/zap2docker-stable zap-baseline.py \
-t http://staging.example.com \
-r zap-report.html || true
'''
}
}
// Stage 8: Security Gate
stage('Security Gate') {
steps {
script {
// Check results and fail pipeline if critical issues found
def critical = sh(
script: 'grep -c "CRITICAL" trivy-report.txt || true',
returnStdout: true
).trim() as int
if (critical > 0) {
error("${critical} critical vulnerabilities found — deployment blocked")
}
}
}
}
// Stage 9: Deploy Production
stage('Deploy Production') {
when { branch 'main' }
input { message 'Deploy to production?' }
steps {
sh 'kubectl apply -f k8s/production/ -n production'
}
}
}
post {
always {
archiveArtifacts artifacts: '**/*.sarif,**/*.html,**/*.txt', allowEmptyArchive: true
}
}
}
Tool Selection Matrix
| Layer | Recommended | Alternative |
|---|---|---|
| Secrets | Gitleaks, TruffleHog | GitGuardian |
| SAST | Semgrep, SonarQube | Checkmarx, CodeQL |
| Dependencies | Snyk, npm audit | Dependabot, Renovate |
| Container | Trivy | Grype, Clair |
| DAST | OWASP ZAP | Burp Suite, Nikto |
| IaC | Checkov, tfsec | Terrascan, KICS |
| License | FOSSA, ScanCode | Snyk License |
Quality Gates
Block on Critical Findings
// Only pass if no critical issues
stage('Security Gate') {
steps {
script {
def criticals = sh(
script: 'jq "[.Results[] | select(.Vulnerabilities[]?.Severity == \"CRITICAL\")] | length" trivy-results.json',
returnStdout: true
).trim() as int
if (criticals > 0) {
error("${criticals} critical vulnerabilities — deploy blocked")
}
}
}
}
Warning vs Blocking
| Severity | Action |
|---|---|
| CRITICAL | Block pipeline, alert team |
| HIGH | Block production, warn staging |
| MEDIUM | Warn, don't block |
| LOW | Log for tracking |
// Smart gate: block prod on HIGH+, warn staging
if (params.ENVIRONMENT == 'prod' && findings.HIGH > 0) {
error("HIGH+ findings in production build")
}
Best Practices
1. Shift Left — Scan Early
Cost of fixing a bug:
Pre-commit: £0 ← cheapest
CI build: £10
Staging: £100
Production: £1000+ ← most expensive
2. Baseline Before Blocking
# First run: accept existing issues
semgrep scan --config auto . --baseline-commit main
# Only flag NEW issues going forward
3. Automate Remediation
# Auto-fix what's fixable
npm audit fix --force
terraform fmt
eslint --fix .
4. Scan the Pipeline Itself
# Checkov for Terraform
checkov -d terraform/
# tfsec
tfsec terraform/
# Kubesec for K8s
kubesec scan k8s/
5. Supply Chain Security
# Verify image signatures
cosign verify myregistry/myapp:latest --key cosign.pub
# Generate SBOM
syft packages myapp:latest -o spdx-json > sbom.json
# Scan SBOM for vulnerabilities
grype sbom:sbom.json
Metrics That Matter
- MTTR (Mean Time to Remediate) — how fast critical findings get fixed
- Coverage — % of code/images scanned
- False Positive Rate — tune rules quarterly
- Escape Rate — vulnerabilities found in prod vs caught earlier
Related Articles
Last Updated: October 2026
Author: CloudOpsGuide Team
Difficulty: Advanced
Estimated Reading Time: 17 minutes