DevSecOps: How to Integrate Security into Your CI/CD Pipeline in 2026
Shifting security left means more than running a scanner in your pipeline. Learn how to build security gates, automate threat detection, and create a DevSecOps culture that catches vulnerabilities before they reach production.

Video transcript
Picture this: a vulnerability ships to production on a Friday afternoon, and by Monday morning your incident response team is in full crisis mode. Here's the uncomfortable truth. Security can't be a gate at the end anymore. When you leave security for last, you're fighting against time, budget, and developer momentum. Vulnerabilities that cost five dollars to fix in code review can cost five thousand to remediate after deployment. That's not just expensive. That's a career conversation. Think of S A S T like a spell checker built into your code editor. It scans your source code in real time as developers write, catching logic flaws and injection vulnerabilities before they ever get committed. No waiting for a separate security team to review later. D A S T works like a security tester who actually runs your application and throws real attack patterns at it. It crawls your running application, tests A P I endpoints, and finds what an attacker would find. You catch runtime issues that static analysis alone will miss. S C A is your supply chain bodyguard. It audits every open source library you pull in, checking against known vulnerability databases like O W A S P dependency check. One compromised dependency can compromise your entire system. Automation makes that checking instant, not quarterly. Start small: add one security tool to your next pipeline run and watch how it fits into your workflow. The goal isn't perfection on day one. It's building momentum. Read the complete guide at protego dot me.
Security Belongs in the Pipeline, Not After It
The traditional software development lifecycle treated security as a phase that happened after development: a security team reviewed code, ran penetration tests, and signed off before production. This model has two fundamental problems: it's slow (bottlenecks at the security review gate) and it's late (finding vulnerabilities in finished software is expensive to fix).
DevSecOps integrates security into every phase of development and delivery. The goal isn't to make developers into security engineers; it's to automate the security checks that can be automated, surface findings where developers work, and make secure development the path of least resistance.
This guide covers how to build a security pipeline that catches real issues without creating so much noise that developers start ignoring alerts.
The DevSecOps Security Pipeline Architecture
A mature DevSecOps pipeline runs different security checks at each stage based on speed, feedback value, and when the information is available:
The key principle: faster checks run earlier, slower checks run later. A 3-second secret scan on every commit is valuable. A 20-minute DAST scan runs after deployment to staging.
Stage 1: Pre-Commit Hooks (Developer Workstation)
Pre-commit hooks catch issues before code leaves the developer's machine. This is the cheapest place to find problems: no CI minutes consumed, immediate feedback.
Setup with pre-commit Framework
# .pre-commit-config.yaml
repos:
# Detect hardcoded secrets
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.0
hooks:
- id: gitleaks
# General secret patterns
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: detect-private-key
- id: check-added-large-files
args: ['--maxkb=1000']
- id: check-merge-conflict
- id: no-commit-to-branch
args: ['--branch', 'main', '--branch', 'master']
# Python security (if applicable)
- repo: https://github.com/PyCQA/bandit
rev: 1.7.6
hooks:
- id: bandit
args: ['-ll'] # Only medium and high severityCritical: pre-commit hooks can be bypassed with git commit --no-verify. They're the first line of defense, not the last. The CI pipeline must enforce the same checks independently.
Stage 2: Static Application Security Testing (SAST)
SAST analyzes source code for security vulnerabilities without executing it. It catches injection flaws, insecure configurations, and dangerous API usage patterns at code review time.
Top SAST Tools by Language
| Language | Tool | Notes |
|---|---|---|
| JavaScript/TypeScript | Semgrep, SonarQube, ESLint security rules | Semgrep has excellent JS/TS rules |
| Python | Bandit, Semgrep, Pylint security | Bandit is fast and CI-friendly |
| Go | Gosec, Semgrep | Gosec covers Go-specific patterns |
| Java | SpotBugs + FindSecBugs, SonarQube | SpotBugs is free, SonarQube has deeper rules |
| C/C++ | Flawfinder, Cppcheck, Coverity | Coverity is paid but industry standard |
| Multi-language | Semgrep, CodeQL, Snyk Code | CodeQL is free for open source |
GitHub Actions: Semgrep SAST
# .github/workflows/sast.yml
name: SAST Security Scan
on:
pull_request:
branches: [main, master]
push:
branches: [main, master]
jobs:
semgrep:
name: Semgrep SAST
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- uses: actions/checkout@v4
- name: Run Semgrep
uses: semgrep/semgrep-action@v1
with:
config: >-
p/security-audit
p/owasp-top-ten
p/javascript
p/typescript
p/secrets
env:
SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
- name: Upload SARIF to GitHub Security
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: semgrep.sarifTuning Signal-to-Noise Ratio
Raw SAST output is often unusable: hundreds of findings, many false positives. Tune aggressively:
# semgrep.yml - ignore low-value rules
rules:
- id: project-overrides
paths:
exclude:
- "**/*.test.ts" # Skip test files
- "**/fixtures/**" # Skip test fixtures
- "**/vendor/**" # Skip vendored codeStart with a high-severity-only gate that blocks PRs. Add medium severity as informational warnings. Gradually migrate informational to blocking as you resolve the backlog.
Stage 3: Software Composition Analysis (SCA)
SCA scans your dependencies for known vulnerabilities (CVEs), license compliance issues, and outdated packages. It's one of the highest ROI security controls because most production vulnerabilities are in third-party libraries, not first-party code.
GitHub Actions: Dependency Review
# .github/workflows/sca.yml
name: Dependency Security Review
on:
pull_request:
branches: [main]
jobs:
dependency-review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Dependency Review
uses: actions/dependency-review-action@v4
with:
fail-on-severity: high # Block on high/critical CVEs
deny-licenses: GPL-3.0, AGPL # Block GPL in proprietary code
allow-licenses: MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause, ISC
snyk:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Snyk SCA Scan
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high --fail-on=allDependency Update Automation
Block new vulnerabilities from entering via Dependabot:
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: npm
directory: "/"
schedule:
interval: weekly
day: monday
time: "09:00"
open-pull-requests-limit: 10
groups:
security-updates:
applies-to: security-updates
patterns: ["*"]Stage 4: Secrets Scanning
Hardcoded secrets (API keys, passwords, tokens, certificates) in source code are one of the most common and impactful security failures. Once committed to git history, a secret is compromised, even if later deleted.
GitHub Secret Scanning + Push Protection
Enable in repo settings → Security → Secret scanning → Push protection. This blocks pushes containing known secret patterns from 200+ providers before they enter the repo.
Gitleaks in CI
# .github/workflows/secrets.yml
name: Secret Scanning
on: [push, pull_request]
jobs:
gitleaks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for baseline scans
- name: Gitleaks Scan
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}Custom Rules for Your Environment
# .gitleaks.toml
[[rules]]
id = "company-internal-token"
description = "Internal API token pattern"
regex = '''COMPANY_[A-Z0-9]{32}'''
severity = "critical"
tags = ["internal", "api"]
[[allowlist]]
description = "Test fixtures"
paths = ['''(?i)test''', '''(?i)fixture''', '''(?i)mock''']Stage 5: Infrastructure as Code (IaC) Security
If your infrastructure is defined in Terraform, CloudFormation, Kubernetes manifests, or Helm charts, those files can contain security misconfigurations just like application code.
Checkov for Terraform
# .github/workflows/iac-security.yml
name: IaC Security Scan
on:
pull_request:
paths:
- '**.tf'
- '**.yaml'
- '**.yml'
- 'Dockerfile'
- 'docker-compose*.yml'
jobs:
checkov:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Checkov IaC Scan
uses: bridgecrewio/checkov-action@master
with:
directory: .
framework: terraform,kubernetes,dockerfile,github_actions
soft_fail: false
output_format: sarif
output_file_path: checkov.sarif
skip_check: CKV_AWS_18 # Example: skip specific check if accepted risk
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: checkov.sarifCommon Terraform findings Checkov catches:
- S3 buckets without encryption or public access blocks
- Security groups with 0.0.0.0/0 ingress on sensitive ports
- RDS instances without deletion protection or encryption
- IAM policies with wildcard (*) actions
- CloudTrail not enabled
Stage 6: Container Image Scanning
Every container image in production should be scanned for OS package vulnerabilities and secret leaks before deployment.
Trivy in CI
# .github/workflows/container-scan.yml
name: Container Security Scan
on:
push:
branches: [main]
jobs:
trivy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build Image
run: docker build -t app:${{ github.sha }} .
- name: Trivy Vulnerability Scan
uses: aquasecurity/trivy-action@master
with:
image-ref: app:${{ github.sha }}
format: sarif
output: trivy-results.sarif
severity: CRITICAL,HIGH
exit-code: '1' # Fail on CRITICAL/HIGH
vuln-type: os,library
ignore-unfixed: true # Don't fail on unfixed CVEs
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: trivy-results.sarifImage Hardening Checklist
# Security-hardened Dockerfile pattern
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
FROM node:20-alpine
# Run as non-root user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --from=builder --chown=appuser:appgroup /app .
USER appuser
# No SUID/SGID binaries
RUN find / -perm /6000 -type f -exec chmod a-s {} \; 2>/dev/null || true
EXPOSE 3000
CMD ["node", "server.js"]Stage 7: Dynamic Application Security Testing (DAST)
DAST tests a running application by sending crafted HTTP requests and analyzing responses. Unlike SAST, it finds runtime vulnerabilities that only appear when the app is actually executing: authentication bypasses, business logic flaws, server misconfigurations.
OWASP ZAP in CI
# .github/workflows/dast.yml
name: DAST Scan
on:
workflow_run:
workflows: ["Deploy to Staging"]
types: [completed]
jobs:
zap-scan:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
- name: ZAP API Scan
uses: zaproxy/action-api-scan@v0.7.0
with:
target: https://staging.yourapp.com/api/openapi.json
rules_file_name: .zap/rules.tsv
cmd_options: '-a -j' # Include ajax spider
- name: ZAP Full Scan
uses: zaproxy/action-full-scan@v0.10.0
with:
target: https://staging.yourapp.com
rules_file_name: .zap/rules.tsv
fail_action: trueDAST is the noisiest scan type. Configure a baseline rules file to suppress known false positives, and run it asynchronously (don't block the deployment, but create issues from findings).
The Security Gate Policy
The most important DevSecOps decision is: what blocks the pipeline vs. what's informational?
| Severity | Recommended Gate |
|---|---|
| Critical CVE (CVSS 9.0+) in dependency | Block PR merge |
| High CVE (CVSS 7.0-8.9) in dependency | Block PR merge |
| Hardcoded secret | Block PR merge + alert security team |
| Critical SAST finding (injection, XXE) | Block PR merge |
| High SAST finding | Block PR merge |
| Medium SAST finding | Warning, doesn't block |
| IaC critical misconfiguration | Block PR merge |
| Medium CVE (unfixed) | Warning, informational |
| License violation | Block PR merge |
Start conservative: block only critical severity across all scan types. Gradually add high severity as your security backlog shrinks. Teams that try to enforce medium severity gates from day one get alert fatigue and start suppressing findings indiscriminately.
SBOM Generation and Artifact Signing
In 2026, producing a Software Bill of Materials (SBOM) and signing your artifacts is a compliance expectation for many regulated industries and government supply chains.
# Add to your release workflow
- name: Generate SBOM
uses: anchore/sbom-action@v0
with:
image: your-registry/app:${{ github.sha }}
format: spdx-json
output-file: sbom.spdx.json
- name: Sign Image with Cosign
uses: sigstore/cosign-installer@v3
- name: Sign Container Image
run: |
cosign sign --yes \
--key env://COSIGN_PRIVATE_KEY \
your-registry/app:${{ github.sha }}
env:
COSIGN_PRIVATE_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }}
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}Building the DevSecOps Culture
Tools are the easy part. The harder part is organizational change.
Make security findings appear where developers work. SARIF integration with GitHub Security tab means developers see findings in pull requests, not in a separate security portal they'll never open.
Provide context, not just flags. A vulnerability alert that says "Use parameterized queries" with a link to the affected line and an explanation is actionable. A vulnerability alert that says "SQL Injection detected" with a file path is not.
Track security debt like technical debt. Create a Security issues project in your issue tracker. Triage findings weekly. Don't let the backlog grow unbounded; a finding that's been open for 18 months will never be fixed.
Measure what matters.
- Mean time to remediate critical findings (target: <7 days)
- Percentage of PRs passing all security gates on first attempt (target: >90%)
- New critical/high CVEs introduced per sprint (target: 0)
- Secret leak incidents (target: 0)
DevSecOps isn't a tool you install; it's a discipline you build over time. Start with the highest-value controls (secrets scanning, critical CVE blocking), get developer buy-in by reducing friction, and expand coverage incrementally. The teams that succeed treat security findings as software quality issues, not compliance checkboxes.
References
- [OWASP DevSecOps Guideline](https://owasp.org/www-project-devsecops-guideline/): DevSecOps integration framework and toolchain guidance
- [NIST SP 800-218: Secure Software Development Framework](https://csrc.nist.gov/publications/detail/sp/800-218/final): SSDF controls for secure CI/CD pipelines
- [GitHub Advanced Security documentation](https://docs.github.com/en/get-started/learning-about-github/about-github-advanced-security): SAST, secret scanning, and dependency review
- [OpenSSF Scorecard](https://securityscorecards.dev/): Automated security best practices assessment for open source
Frequently Asked Questions
What is DevSecOps and how does it differ from DevOps?
DevSecOps integrates security practices throughout the entire software development lifecycle rather than treating security as a phase that happens after development. In a traditional DevOps pipeline, security reviews occur near the end; in DevSecOps, security scanning runs automatically at every stage: SAST during code commit, SCA during dependency checks, secrets scanning in pre-commit hooks, IaC scanning during infrastructure reviews, and DAST against deployed environments. The goal is to find and fix vulnerabilities when they are cheapest to remediate, which is before code reaches production.
What does SAST, DAST, and SCA mean in a DevSecOps pipeline?
SAST (Static Application Security Testing) analyzes source code without executing it, finding issues like SQL injection, XSS vulnerabilities, and insecure cryptographic usage. DAST (Dynamic Application Security Testing) tests a running application by sending malicious inputs and analyzing responses, finding runtime vulnerabilities that static analysis misses. SCA (Software Composition Analysis) scans third-party dependencies for known CVEs and license compliance issues. All three serve different purposes and complement rather than replace each other in a complete DevSecOps pipeline.
How do I add secrets scanning to a GitHub Actions pipeline?
Enable GitHub Advanced Security secret scanning at the organization or repository level, which automatically scans all commits and alerts on detected credentials. For additional coverage before code is even committed, add pre-commit hooks using detect-secrets or gitleaks to developer workstations. In your CI pipeline, add a secrets scanning step using truffleHog or gitleaks as a workflow step that fails the build if committed secrets are detected. Push protection (GHAS feature) blocks pushes at the git level before secrets can enter the repository.
What is shift-left security in DevSecOps?
Shift-left security means moving security testing earlier in the development process, closer to where code is written. The name comes from moving security activities to the left side of a traditional waterfall timeline. In practice, it means developers run security checks in their IDE, pre-commit hooks catch secrets and obvious vulnerabilities before code is pushed, and PR checks run SAST and SCA before code is merged. Finding a vulnerability in a PR takes minutes to fix; finding the same vulnerability in production requires incident response, patch deployment, and potentially breach disclosure.
How do I implement security gates in CI/CD without blocking developers?
Start with reporting-only mode for all new security checks: surface findings as PR comments and dashboard metrics without blocking merges. After 2-4 weeks of data, identify which finding categories have the highest severity and lowest false-positive rate. Enable blocking only for critical severity findings in these categories first. Gradually raise the bar as your security backlog shrinks and developer trust in the scanner quality grows. Teams that try to enforce medium-severity gates on day one generate alert fatigue and developers start suppressing findings indiscriminately.
Get weekly security insights
Cloud security, zero trust, and identity guides — straight to your inbox.
Microsoft Cloud Solution Architect
Cloud Solution Architect with deep expertise in Microsoft Azure and a strong background in systems and IT infrastructure. Passionate about cloud technologies, security best practices, and helping organizations modernize their infrastructure.
Share this article
Questions & Answers
Related Articles
Need Help with Your Security?
Our team of security experts can help you implement the strategies discussed in this article.
Contact Us