DevSecOps embeds security into every stage of the software development lifecycle. Rather than running security assessments at the end of a release cycle, DevSecOps shifts security left into development and CI/CD pipelines. This article covers how to integrate SAST, DAST, dependency scanning, container scanning, and policy-as-code into your pipelines.
Shift-Left Security
The shift-left principle moves security testing earlier in the development process. Finding and fixing a vulnerability during development costs 10 times less than fixing it in production, and 100 times less than fixing it after a breach.
Security Gates in the Pipeline
A mature DevSecOps pipeline has security gates at every stage:
Code Commit -> SAST -> Dependency Scan -> Build -> Container Scan ->
Integration Test -> DAST -> Staging -> Policy Check -> Production
Each gate can pass, fail with a warning, or fail and block the pipeline. The severity determines the action: critical and high findings block the pipeline, while medium and low findings create tickets for the development team.
Static Application Security Testing (SAST)
SAST analyzes source code without executing it. It identifies vulnerabilities like SQL injection, cross-site scripting (XSS), buffer overflows, and insecure cryptographic usage.
Popular SAST Tools
Pipeline Integration
# GitHub Actions: SAST with Semgrep
name: SAST Scan
on:
pull_request:
branches: [main]
jobs:
semgrep:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: semgrep/semgrep-action@v1
with:
config: p/default
audit_on: push
- name: Check for blocking findings
run: |
if [ "${{ steps.semgrep.outputs.results }}" != "" ]; then
echo "SAST found vulnerabilities. Fix before merging."
exit 1
fi
SAST Best Practices
Dependency Scanning
Modern applications use dozens of open-source libraries. Each library introduces transitive dependencies with potential vulnerabilities.
Software Bill of Materials (SBOM)
An SBOM is a formal inventory of all software components in an application. Generate SBOMs in CycloneDX or SPDX format.
# Generate SBOM with Syft
syft packages . -o cyclonedx-json > sbom.json
# Scan SBOM for vulnerabilities with Grype
grype sbom:./sbom.json
Dependency Scanning Tools
# Dependabot configuration
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
labels:
- "dependencies"
- "security"
Dependency Scanning Policy
Dynamic Application Security Testing (DAST)
DAST tests the running application from the outside, simulating real-world attack patterns. It discovers vulnerabilities that SAST misses, such as authentication bypass, session management flaws, and business logic errors.
DAST Tools
Pipeline Integration
# DAST with OWASP ZAP in CI/CD
services:
zap:
image: ghcr.io/zaproxy/zaproxy:stable
options: --user root
steps:
- name: Run ZAP Scan
run: |
docker run -v $(pwd):/zap/wrk:rw \
ghcr.io/zaproxy/zaproxy:stable \
zap-full-scan.py \
-t https://staging.example.com \
-r zap_report.html \
-z "-config spider.maxDuration=10"
- name: Check ZAP Results
run: |
if grep -q "High" zap_report.html; then
echo "DAST found High severity issues"
exit 1
fi
DAST Best Practices
Container Scanning
Container images bundle applications with their runtime dependencies. Vulnerabilities in base images or installed packages become part of the container.
Scanning Pipeline Integration
# Dockerfile with security best practices
FROM alpine:3.19 AS builder
RUN apk add --no-cache build-base
FROM alpine:3.19
RUN apk add --no-cache ca-certificates && \
addgroup -S app && adduser -S app -G app
COPY --from=builder /app/bin/server /app/server
USER app
EXPOSE 8080
# Container scanning with Trivy in CI/CD
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t myapp:${{ github.sha }} .
- name: Scan with Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:${{ github.sha }}
format: sarif
output: trivy-results.sarif
severity: CRITICAL,HIGH
- name: Upload results
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: trivy-results.sarif
Container Security Practices
Policy-as-Code
Policy-as-code defines security and compliance rules as executable code. Policies are versioned, reviewed, and enforced automatically in the pipeline.
Open Policy Agent (OPA) and Rego
OPA enforces policies across the software lifecycle. Rego is OPA's policy language.
# Rego policy: Require encryption at rest
package kubernetes.storage
deny[msg] {
volume := input.volumes[_]
volume.persistentVolumeClaim
not volume.encrypted
msg := sprintf("Volume %v must have encryption enabled", [volume.name])
}
Policy Gates in the Pipeline
# Conftest: Policy enforcement for Kubernetes manifests
steps:
- name: Check Kubernetes policies
run: |
conftest test deployment.yaml \
--policy policies/ \
--namespace kubernetes
- name: Check Terraform policies
run: |
conftest test main.tf \
--policy policies/terraform/
Common Policy Rules
Building a DevSecOps Culture
Tools alone do not make DevSecOps successful. The human elements matter more.
Conclusion
DevSecOps shifts security from a final gate to an integrated part of the development process. SAST catches code-level vulnerabilities, dependency scanning manages supply chain risk, DAST validates runtime behavior, container scanning secures artifacts, and policy-as-code enforces standards. Together, these practices make security a natural part of delivering software, not an obstacle to it.