Integrate security into every stage of your CI/CD pipeline. Learn SAST, DAST, SCA, container scanning, IaC security scanning, and security gates for DevSecOps.
DevSecOps: Shift Left Security
Security in CI/CD Pipeline
Code Commit → SAST → SCA → Build → Container Scan → Deploy → DAST → Monitor
SAST (Static Application Security Testing)
# GitHub Actions - SAST pipeline
name: Security Pipeline
on: [push, pull_request]
jobs:
sast:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
# Semgrep SAST
- name: Semgrep SAST
uses: returntocorp/semgrep-action@v1
with:
config: "p/owasp-top-ten p/nodejs"
auditOn: push
env:
SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
# ESLint security rules
- name: ESLint Security
run: |
npm install eslint-plugin-security eslint-plugin-no-secrets
npx eslint --plugin security --rule 'security/detect-eval-with-expression: error' src/
# Bandit for Python
- name: Bandit Python SAST
if: hashFiles('**/*.py') != ''
run: |
pip install bandit
bandit -r . -f json -o bandit-report.json -ll
cat bandit-report.json
# Upload SARIF to GitHub Security
- name: Upload SAST results
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: sast-results.sarif
SCA (Software Composition Analysis)
sca:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Snyk vulnerability scanning
- name: Snyk SCA
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
command: test
args: --severity-threshold=high --json > snyk-results.json
# License compliance
- name: License Check
run: |
npx license-checker --onlyAllow "MIT;ISC;BSD-2-Clause;BSD-3-Clause;Apache-2.0" --excludePrivatePackages --failOn "GPL-2.0;GPL-3.0;AGPL-3.0"
# OWASP Dependency Check
- name: OWASP Dependency Check
uses: dependency-check/Dependency-Check_Action@main
with:
project: 'myapp'
path: '.'
format: 'SARIF'
args: --failOnCVSS 8
Infrastructure as Code Security
iac-security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Checkov - Terraform/K8s/Dockerfile scanning
- name: Checkov IaC Scan
uses: bridgecrewio/checkov-action@v12
with:
directory: infrastructure/
framework: terraform,kubernetes,dockerfile
soft_fail: false
output_format: sarif
output_file_path: checkov-results.sarif
# Trivy for Terraform
- name: Trivy IaC Scan
run: |
trivy config infrastructure/ --exit-code 1 --severity HIGH,CRITICAL --format sarif -o trivy-iac.sarif
# tfsec for Terraform
- name: tfsec
uses: aquasecurity/tfsec-action@v1.0.0
with:
working_directory: infrastructure/terraform/
Container Security Scanning
container-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t myapp:${{ github.sha }} .
- name: Trivy container scan
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:${{ github.sha }}
format: sarif
output: trivy-container.sarif
severity: CRITICAL,HIGH
exit-code: 1
- name: Hadolint Dockerfile lint
uses: hadolint/hadolint-action@v3.1.0
with:
dockerfile: Dockerfile
failure-threshold: error
DAST (Dynamic Application Security Testing)
dast:
runs-on: ubuntu-latest
needs: [deploy-staging]
steps:
- name: OWASP ZAP Baseline Scan
uses: zaproxy/action-baseline@v0.10.0
with:
target: https://staging.myapp.com
rules_file_name: .zap/rules.tsv
fail_action: true
- name: Nuclei DAST
uses: projectdiscovery/nuclei-action@main
with:
target: https://staging.myapp.com
flags: "-severity critical,high -t cves/ -t exposures/"
Security Quality Gates
// security-gate.js - Fail deployment if security issues found
const fs = require('fs')
function checkSecurityGates() {
const gates = []
// Check SAST results
const sast = JSON.parse(fs.readFileSync('semgrep-results.json'))
const criticalFindings = sast.results.filter(r => r.extra.severity === 'ERROR')
gates.push({
name: 'SAST',
passed: criticalFindings.length === 0,
details: `${criticalFindings.length} critical findings`,
})
// Check SCA
const snyk = JSON.parse(fs.readFileSync('snyk-results.json'))
const criticalVulns = snyk.vulnerabilities?.filter(v => v.severity === 'critical') || []
gates.push({
name: 'SCA',
passed: criticalVulns.length === 0,
details: `${criticalVulns.length} critical vulnerabilities`,
})
// Report
const failed = gates.filter(g => !g.passed)
if (failed.length > 0) {
console.error('SECURITY GATES FAILED:')
failed.forEach(g => console.error(` - ${g.name}: ${g.details}`))
process.exit(1)
}
console.log('All security gates passed!')
}
checkSecurityGates()
DevSecOps Maturity Model
| Level |
Practices |
| 1 - Basic |
Dependency scanning, SAST |
| 2 - Developing |
Container scanning, IaC checks |
| 3 - Defined |
DAST, security gates, policy as code |
| 4 - Managed |
Threat modeling, security chaos |
| 5 - Optimized |
Continuous security, purple teaming |