DevSecOps: Integrating security into CI/CD pipelines
A Practical Guide to DevSecOps Implementation: How to integrate security testing into CI/CD pipelines, which tools to use for SAST, DAST, SCA, and container scanning, and how to incorporate security findings into the development workflow. Includes concrete examples using GitLab CI and GitHub Actions.
Table of Contents (8 sections)
DevSecOps is not a technology—it is a cultural shift. Security does not belong at the end of the release process ("security gate"), but rather in every single step of development. Every developer is responsible for security; the security team empowers rather than blocks.
The DevSecOps Principle: Shift Left
Traditional (DevOps without security):
Code → Build → Test → Staging → Security Review → Release
↑
Security finds issues here
→ expensive, late, blocking
DevSecOps (Shift Left):
Commit → [SAST + Secret Scan + SCA] → Build → [Container Scan]
→ Test → [DAST] → Staging → [IAST + Pentest] → Release
↑ ↑
Security here here too!
→ inexpensive, early, automated
Cost factor for bug fixing (IBM NIST study):
Found in code: 1x
Found in testing: 6x
Found in production: 30x
Found via customer report: 100x
→ Every security bug found in CI/CD is 30x cheaper!
The DevSecOps Pipeline - Overview
Stages of a complete DevSecOps pipeline:
┌────────────────────────────────────────────────────────────────┐
│ PRE-COMMIT (Locally on developer's machine) │
│ • Secret Detection (gitleaks, git-secrets) │
│ • Linting (eslint security rules) │
│ • Dependency Check (npm audit, safety) │
└────────────────────────────────────────────────────────────────┘
↓
┌────────────────────────────────────────────────────────────────┐
│ CI: SOURCE CODE ANALYSIS │
│ • SAST (Semgrep, CodeQL, Bandit, SonarQube) │
│ • SCA - Software Composition Analysis (Snyk, OWASP Dependency-Check) │
│ • Secret Scanning (TruffleHog, GitLab Secret Detection) │
│ • License Compliance (FOSSA, ClearlyDefined) │
└────────────────────────────────────────────────────────────────┘
↓
┌────────────────────────────────────────────────────────────────┐
│ CI: BUILD & CONTAINER │
│ • Container Image Scanning (Trivy, Grype, Snyk) │
│ • SBOM Generation (Syft) - Bill of Materials for all dependencies │
│ • Image Signing (Cosign, Notary) - Supply Chain Security │
│ • Infrastructure as Code Scanning (Checkov, tfsec, Terrascan) │
└────────────────────────────────────────────────────────────────┘
↓
┌────────────────────────────────────────────────────────────────┐
│ CI/CD: DYNAMIC TESTS │
│ • DAST (OWASP ZAP, Nuclei) │
│ • API Security Testing (Postman/Newman Security, ZAP API) │
│ • IAST - Runtime Instrumentation (Contrast Security) │
└────────────────────────────────────────────────────────────────┘
↓
┌────────────────────────────────────────────────────────────────┐
│ PRODUCTION: RUNTIME SECURITY │
│ • WAF (ModSecurity, AWS WAF, Cloudflare) │
│ • RASP - Runtime Application Self-Protection │
│ • Container Runtime Security (Falco, Aqua Security) │
│ • Vulnerability Management (continuous CVE monitoring) │
└────────────────────────────────────────────────────────────────┘
SAST - Static Code Analysis in CI/CD
SAST tools and their strengths:
Semgrep (open source, recommended):
→ Rule-based, very fast, low false positives
→ Community rules for 20+ languages
→ Custom rules possible (specific business logic)
GitLab CI:
sast:
stage: security
image: returntocorp/semgrep:latest
script:
- semgrep --config=auto
--config=p/owasp-top-ten
--config=p/javascript
--json
--output=semgrep-findings.json
./src
artifacts:
reports:
sast: semgrep-findings.json # GitLab SAST report integration!
---
Semgrep Custom Rule Example (SQL Injection):
rules:
- id: sql-injection-risk
patterns:
- pattern: |
$DB.query("..." + $USER_INPUT + "...")
- pattern-not: |
$DB.query($QUERY, [$PARAMS]) # Prepared statements OK
message: |
Possible SQL injection: User input is embedded directly into the query.
Use prepared statements!
severity: ERROR
languages: [javascript, typescript]
metadata:
cwe: "CWE-89"
owasp: "A03:2021"
---
SonarQube (with Security plugin):
# sonar-scanner in CI/CD:
sonar:
stage: security
image: sonarsource/sonar-scanner-cli:latest
variables:
SONAR_HOST_URL: "https://sonar.intern.firma.de"
SONAR_TOKEN: "$SONAR_TOKEN" # As a CI/CD secret!
script:
- sonar-scanner
-Dsonar.projectKey=myapp
-Dsonar.sources=./src
-Dsonar.security.sources=src
only:
- main
- merge_requests
---
GitHub Actions with CodeQL:
name: CodeQL Security Analysis
on: [push, pull_request]
jobs:
analyze:
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- uses: actions/checkout@v4
- uses: github/codeql-action/init@v3
with:
languages: javascript, python
queries: security-and-quality
- uses: github/codeql-action/autobuild@v3
- uses: github/codeql-action/analyze@v3
SCA - Checking dependencies for vulnerabilities
Software Composition Analysis (SCA) checks third-party dependencies:
Snyk (commercial, very powerful):
snyk test # Node.js dependencies
snyk test --file=requirements.txt # Python
snyk test --docker myapp:latest # Container image
snyk monitor # Continuous monitoring
# GitLab CI:
dependency_scanning:
stage: security
image: snyk/snyk:node
script:
- snyk test --severity-threshold=high
allow_failure: false
---
OWASP Dependency-Check (Open Source):
# Docker:
docker run --rm \
-v $(pwd):/src \
owasp/dependency-check:latest \
--project "MyApp" \
--scan /src \
--format HTML \
--out /src/dependency-check-report
---
npm audit (built-in):
npm audit --audit-level=high
npm audit fix # Automatic fixes
npm audit fix --force # Accept breaking changes
# In package.json scripts:
"scripts": {
"audit": "npm audit --audit-level=moderate"
}
---
pip-audit (Python):
pip-audit -r requirements.txt
pip-audit --fix # Install secure versions
---
Supply Chain Security with SBOM:
# Syft - Generate SBOM (Software Bill of Materials):
syft myapp:latest -o spdx-json=sbom.spdx.json
# Grype - Check SBOM for CVEs:
grype sbom:sbom.spdx.json
# Cosign - Sign container image:
cosign sign --key cosign.key myregistry.io/myapp:v1.2.3
cosign verify --key cosign.pub myregistry.io/myapp:v1.2.3
DAST - Dynamic testing in staging
OWASP ZAP in CI/CD (recommended for DAST):
Baseline scan (passive, fast, < 2 minutes):
dast_baseline:
stage: dast
image: owasp/zap2docker-stable:latest
script:
- mkdir -p /zap/wrk/
- zap-baseline.py
-t https://staging.myapp.de
-g gen.conf
-r zap-baseline-report.html
-J zap-baseline-report.json
-x zap-baseline-report.xml
-I # Fail on alerts
artifacts:
paths:
- zap-baseline-report.html
Full Scan (active, slower, finds more):
dast_full:
stage: dast
script:
- zap-full-scan.py
-t https://staging.myapp.de
-r zap-full-report.html
-I
only:
- main
---
Authenticated ZAP scan:
dast_authenticated:
stage: dast
image: owasp/zap2docker-stable:latest
variables:
TARGET_URL: "https://staging.myapp.de"
LOGIN_URL: "https://staging.myapp.de/login"
USERNAME: "${TEST_USERNAME}" # CI/CD Secret
PASSWORD: "${TEST_PASSWORD}" # CI/CD Secret
script:
- zap.sh -daemon -host 0.0.0.0 -port 8080
-config api.disablekey=true &
- sleep 5 # Let ZAP start
# Configure form-based authentication via the ZAP API
- python3 scripts/configure_zap_auth.py
- zap-cli active-scan --scanners sqli,xss $TARGET_URL
- zap-cli report -o zap-report.html -f html
---
Nuclei (fast template-based scanning):
nuclei_scan:
stage: dast
image: projectdiscovery/nuclei:latest
script:
- nuclei -u https://staging.myapp.de
-t cves/
-t exposures/
-t vulnerabilities/
-severity critical,high
-json-export nuclei-findings.json
-exit-code 1 # Fail on findings
Infrastructure as Code (IaC) Security
IaC files configured incorrectly just once → hundreds of deployments compromised!
Checkov (most comprehensive IaC scanner):
# Terraform:
checkov -d ./terraform --framework terraform
# Kubernetes YAML:
checkov -d ./kubernetes --framework kubernetes
# Docker:
checkov -f Dockerfile --framework dockerfile
# In CI/CD:
iac_security:
stage: security
image: bridgecrew/checkov:latest
script:
- checkov -d . --soft-fail-on MEDIUM
--hard-fail-on HIGH,CRITICAL
artifacts:
reports:
junit: checkov-results.xml
---
tfsec (Terraform-focused):
tfsec ./terraform --format checkstyle > tfsec-report.xml
Known IaC Findings:
□ S3 Bucket: Public Access not blocked (MEDIUM-CRITICAL)
□ Security Group: 0.0.0.0/0 allowed on port 22 (SSH) (HIGH)
□ RDS: Multi-AZ not enabled (MEDIUM)
□ CloudTrail: not enabled (HIGH)
□ KMS Key: Rotation not enabled (MEDIUM)
□ IAM Policy: Wildcard (*) Actions (HIGH)
Security Gates - When to stop the pipeline?
Severity Matrix for CI/CD Decisions:
SAST SCA Container DAST IaC
Critical: STOP STOP STOP STOP STOP
High: STOP STOP STOP STOP STOP
Medium: WARN WARN WARN WARN WARN
Low: INFO INFO INFO INFO INFO
STOP = Pipeline fails, no deployment
WARN = Finding is logged, deployment still possible
INFO = Reporting only
Exception Management:
→ False positives must be documented
→ .semgrepignore / .trivyignore for deliberate exceptions
→ Audit trail: who approved the exception, why, expiration date?
→ Quarterly review of all exceptions
Example: .trivyignore:
# CVE-2023-XXXXX is not exploitable in our deployment
# because: [reason]
# Exception approved by: Max Muster, 2026-01-15
# Expiration: 2026-04-15 (then re-evaluate!)
CVE-2023-XXXXX
---
Security Dashboard for Teams:
→ SAST findings: Trend over time (increasing or decreasing?)
→ Mean Time to Remediate (MTTR): How quickly are bugs fixed?
→ Security Debt: Total open findings (Risk Backlog)
→ Security Test Coverage: How much code is covered by security tests?
→ Compliance: OWASP categories covered
DevSecOps Maturity Level and Implementation Strategy
Phases of DevSecOps implementation:
Phase 1 (Months 1–2): Establish visibility
□ SAST in non-blocking mode (reports only)
□ Enable secret scanning (all repos!)
□ npm audit / pip-audit as a report
→ Goal: Understand baseline risk, inform developers
Phase 2 (Months 3–4): Block Critical Issues
□ Block pipeline for Critical + High SAST findings
□ Known malware in dependencies → STOP
□ Secrets detected → STOP (always!)
□ Containers: Critical CVEs → STOP
→ Goal: Prevent the worst findings
Phase 3 (Months 5–6): DAST and IaC
□ ZAP baseline in staging pipeline
□ IaC scanning for Terraform/CloudFormation
□ SBOM generation for all images
→ Goal: Runtime vulnerabilities and infrastructure misconfigurations
Phase 4 (Months 7–12): Optimization and Culture
□ Custom security rules (company-specific patterns)
□ Security champions in every team
□ Security KPIs on the team dashboard
□ Developer security training
→ Goal: Security as a matter of course
Security Champions Program:
→ One volunteer developer per team = Security Champion
→ Monthly Security Champion meetings
→ Direct channel to the security team
→ Conduct security reviews of MRs
→ Increase security awareness within the team Questions about this topic?
Our experts advise you free of charge and without obligation.
About the Author
Geschäftsführender Gesellschafter der AWARE7 GmbH mit langjähriger Expertise in Informationssicherheit, Penetrationstesting und IT-Risikomanagement. Absolvent des Masterstudiengangs Internet-Sicherheit an der Westfälischen Hochschule (if(is), Prof. Norbert Pohlmann). Bestseller-Autor im Wiley-VCH Verlag und Lehrbeauftragter der ASW-Akademie. Einschätzungen zu Cybersecurity und digitaler Souveränität erschienen u.a. in Welt am Sonntag, WDR, Deutschlandfunk und Handelsblatt.
10 Publikationen
- Einsatz von elektronischer Verschlüsselung - Hemmnisse für die Wirtschaft (2018)
- Kompass IT-Verschlüsselung - Orientierungshilfen für KMU (2018)
- IT Security Day 2025 - Live Hacking: KI in der Cybersicherheit (2025)
- Live Hacking - Credential Stuffing: Finanzrisiken jenseits Ransomware (2025)
- Keynote: Live Hacking Show - Ein Blick in die Welt der Cyberkriminalität (2025)
- Analyse von Angriffsflächen bei Shared-Hosting-Anbietern (2024)
- Gänsehaut garantiert: Die schaurigsten Funde aus dem Leben eines Pentesters (2022)
- IT Security Zertifizierungen — CISSP, T.I.S.P. & Co (Live-Webinar) (2023)
- Sicherheitsforum Online-Banking — Live Hacking (2021)
- Nipster im Netz und das Ende der Kreidezeit (2017)