How to Implement DevSecOps: Pipeline Security Step by Step
Integrate security into your CI/CD pipeline. Covers SAST, DAST, dependency scanning, container scanning, secrets detection, and compliance gates.
Security as an afterthought is a breach waiting to happen. DevSecOps shifts security left — making it part of every commit, build, and deploy. This guide shows you exactly where to add each security check in your pipeline.
The DevSecOps Pipeline
Commit → Build → Test → Security Scan → Deploy → Monitor
│ │ │ │ │ │
Secrets SAST Unit Dependency Container Runtime
Scan Scan Tests Scan Scan Detection
Step 1: Secrets Detection (Pre-Commit)
Catch secrets before they ever reach the repository.
1.1 Install and Configure
# Install git-secrets (AWS)
brew install git-secrets
git secrets --install
git secrets --register-aws
# Install gitleaks (general purpose)
brew install gitleaks
# Create .gitleaks.toml configuration
cat > .gitleaks.toml << 'EOF'
title = "Custom Gitleaks Config"
[[rules]]
id = "api-key-generic"
description = "Generic API Key"
regex = '''(?i)api[_-]?key\s*[:=]\s*['"]?([a-zA-Z0-9]{32,})['"]?'''
tags = ["key", "api"]
[[rules]]
id = "jwt-token"
description = "JSON Web Token"
regex = '''eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}'''
tags = ["jwt"]
[allowlist]
paths = ['''\.test\.''', '''\.spec\.''', '''__tests__''']
EOF
1.2 Add Pre-Commit Hook
# .pre-commit-config.yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.0
hooks:
- id: gitleaks
# Install
pip install pre-commit
pre-commit install
Step 2: Static Analysis — SAST (Build Stage)
SAST scans your source code for vulnerabilities without executing it.
GitHub Actions Integration
# .github/workflows/security.yml
name: Security Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
sast:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Semgrep — SAST scanner
- name: Semgrep Scan
uses: returntocorp/semgrep-action@v1
with:
config: >-
p/security-audit
p/owasp-top-ten
p/javascript
p/python
# CodeQL (GitHub native)
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: javascript, python
- name: Autobuild
uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
Step 3: Dependency Scanning (Build Stage)
90% of your code is dependencies. Scan them.
dependency-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# npm audit for Node.js
- name: npm Audit
run: npm audit --production --audit-level=high
# Trivy for comprehensive scanning
- name: Trivy FS Scan
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
severity: 'HIGH,CRITICAL'
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload Trivy Results
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-results.sarif'
Manual Check
# Python
pip-audit --output json | jq '.[] | select(.fix_versions != [])'
# Node.js
npx better-npm-audit audit --level high
# Go
govulncheck ./...
# .NET
dotnet list package --vulnerable --include-transitive
Step 4: Container Security Scanning (Pre-Deploy)
container-scan:
runs-on: ubuntu-latest
needs: [build]
steps:
- name: Build Container
run: docker build -t myapp:${{ github.sha }} .
- name: Trivy Container Scan
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:${{ github.sha }}'
severity: 'HIGH,CRITICAL'
exit-code: '1' # Fail the pipeline on HIGH/CRITICAL
- name: Dockle — Dockerfile Best Practices
uses: erzz/dockle-action@v1
with:
image: 'myapp:${{ github.sha }}'
failure-threshold: warn
Dockerfile Hardening
# Use distroless or minimal base
FROM cgr.dev/chainguard/node:latest
# Don't run as root
USER nonroot
# Set filesystem to read-only where possible
COPY --chown=nonroot:nonroot . /app
WORKDIR /app
# Drop all capabilities
# (handled at runtime via --cap-drop ALL)
Step 5: Dynamic Application Security Testing — DAST (Post-Deploy)
DAST tests your running application for vulnerabilities.
dast:
runs-on: ubuntu-latest
needs: [deploy-staging]
steps:
- name: OWASP ZAP Scan
uses: zaproxy/action-full-scan@v0.7.0
with:
target: 'https://staging.myapp.com'
rules_file_name: '.zap/rules.tsv'
allow_issue_writing: false
# Manual ZAP scan
docker run -v $(pwd):/zap/wrk/:rw \
ghcr.io/zaproxy/zaproxy:stable \
zap-full-scan.py \
-t https://staging.myapp.com \
-r zap-report.html \
-x zap-report.xml
Step 6: Compliance Gates
Block deployments that don’t meet security standards.
compliance-gate:
runs-on: ubuntu-latest
needs: [sast, dependency-scan, container-scan]
steps:
- name: Check Security Results
run: |
# Custom gate logic
CRITICAL=$(cat security-results.json | jq '.critical_count')
HIGH=$(cat security-results.json | jq '.high_count')
if [ "$CRITICAL" -gt 0 ]; then
echo "❌ BLOCKED: $CRITICAL critical vulnerabilities found"
exit 1
fi
if [ "$HIGH" -gt 5 ]; then
echo "⚠️ WARNING: $HIGH high vulnerabilities found"
echo "Review required before deployment"
exit 1
fi
echo "✅ Security gate passed"
DevSecOps Maturity Model
| Level | Practices | Automation |
|---|---|---|
| Level 1 | Manual code reviews, annual pen test | None |
| Level 2 | SAST in CI, dependency scanning | Partial — build-time only |
| Level 3 | Full pipeline security, DAST in staging | Full — pre-deploy gates |
| Level 4 | Runtime detection, chaos engineering | Full + monitoring |
| Level 5 | Threat modeling, red team exercises | Full + adaptive response |
Implementation Checklist
- Pre-commit hooks: gitleaks + secrets detection
- SAST: Semgrep or CodeQL in CI
- Dependency scanning: Trivy or npm audit
- Container scanning: Trivy + Dockle
- DAST: OWASP ZAP against staging
- Compliance gate blocking critical/high findings
- Security results uploaded to SARIF/dashboard
- Incident response runbook for security findings
- Quarterly security review cadence
:::note[Source] This guide is derived from operational intelligence at Garnet Grid Consulting. For CI/CD security audits, visit garnetgrid.com. :::