Skip to main content
โšก Calmops

DevSecOps: Building Security into Your CI/CD Pipeline

Introduction

Traditional security approachesโ€”where security teams review code only at the end of developmentโ€”are no longer sufficient. Modern development teams ship code multiple times per day, making manual security reviews impossible. DevSecOps solves this by “shifting security left”โ€”integrating security into every phase of the development lifecycle.

This guide covers how to build security into your CI/CD pipeline, protecting your applications from code to production.


What Is DevSecOps?

The Basic Concept

DevSecOps integrates security practices into DevOps processes, ensuring security is everyone’s responsibility, not just the security team’s. It automates security checks, making them as routine as unit tests.

Key Terms

  • Shift-Left: Moving security earlier in the development lifecycle
  • SAST: Static Application Security Testing (analyzing source code)
  • DAST: Dynamic Application Security Testing (analyzing running applications)
  • SCA: Software Composition Analysis (finding vulnerable dependencies)
  • Container Scanning: Checking container images for vulnerabilities
  • Secret Scanning: Finding exposed credentials in code
  • SBOM: Software Bill of Materials

Why DevSecOps Matters in 2025-2026

Security Approach Vulnerabilities Found Time to Fix Cost
Traditional (end-of-cycle) 30% Weeks High
DevSecOps (automated) 80%+ Hours Low

Security in CI/CD Pipeline

Pipeline Stages

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                      CI/CD Pipeline                                  โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚   Build    โ”‚   Test     โ”‚   Staging  โ”‚  Deploy    โ”‚    Runtime      โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ SAST       โ”‚ SCA        โ”‚ DAST       โ”‚ Container  โ”‚ Runtime         โ”‚
โ”‚ Linting    โ”‚ Integration| Pen Test  โ”‚ Scanning   โ”‚ Protection      โ”‚
โ”‚ Secrets    โ”‚ Tests      โ”‚ API Sec    โ”‚ Signing    โ”‚ WAF             โ”‚
โ”‚ SBOM       โ”‚ Unit Tests โ”‚            โ”‚ Policy     โ”‚ Monitoring      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Implementation

Step 1: SAST - Static Analysis

# .github/workflows/sast.yml
name: Static Application Security Testing

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  security-scan:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Run Semgrep
        uses: returntocorp/semgrep-action@v1
        with:
          config: auto
          
      - name: Run Bandit
        run: |
          pip install bandit
          bandit -r ./src -f json -o bandit-report.json
          
      - name: Run SonarCloud
        uses: SonarSource/sonarcloud-github-action@master
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

Step 2: Dependency Scanning (SCA)

# .github/workflows/sca.yml
name: Dependency Scanning

on:
  push:
    branches: [main]
  schedule:
    - cron: '0 0 * * 0'  # Weekly

jobs:
  dependency-check:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: OWASP Dependency Check
        uses: dependency-check/dependency-check-action@v2
        with:
          project: 'my-app'
          assembly: 'false'
          format: 'JSON'
          output: './reports'
          
      - name: npm audit
        run: npm audit --json > npm-audit.json
        
      - name: Safety (Python)
        run: |
          pip install safety
          safety check --json > safety-report.json
          
      - name: Trivy (Containers)
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'fs'
          severity: 'CRITICAL,HIGH'
          format: 'json'

Step 3: Secret Scanning

# .github/workflows/secrets.yml
name: Secret Scanning

on:
  push:
    branches: [main, develop]
  pull_request:

jobs:
  secrets-scan:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: GitLeaks
        uses: gitleaks/gitleaks-action@v2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          
      - name: TruffleHog
        uses: trufflesecurity/trufflehog@main
        with:
          base: ${{ github.event.repository.default_branch }}
          head: HEAD
          extra_args: --json

Step 4: Container Scanning

# .github/workflows/container-scan.yml
name: Container Security

on:
  push:
    branches: [main]
    tags: ['v*']

jobs:
  build-and-scan:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Build container
        run: |
          docker build -t myapp:${{ github.sha }} .
          
      - name: Trivy Scan
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: 'myapp:${{ github.sha }}'
          severity: 'CRITICAL,HIGH'
          exit-code: '1'
          
      - name: Hadolint
        run: |
          docker run --rm -v ${{ github.workspace }}:/src hadolint/hadolint hadolint /src/Dockerfile
          
      - name: Sign container
        uses: sigstore/cosign-installer@v3
        with:
          cosign-release: 'v2.0.0'
          
      - name: Sign and verify
        run: |
          cosign sign --yes myregistry.io/myapp:${{ github.sha }}

Step 5: DAST - Dynamic Analysis

# .github/workflows/dast.yml
name: Dynamic Security Testing

on:
  push:
    branches: [main]
  schedule:
    - cron: '0 2 * * *'  # Nightly

jobs:
  dast-scan:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Start staging environment
        run: |
          docker-compose up -d
          sleep 10
          
      - name: OWASP ZAP Scan
        uses: zaproxy/[email protected]
        with:
          target: 'http://localhost:8080'
          
      - name: Nuclei Scan
        run: |
          go install github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
          nuclei -list endpoints.txt -json -output nuclei-report.json
          
      - name: Cleanup
        if: always()
        run: docker-compose down

Security Policies

###OPA Gatekeeper Policy

# policies/database.rego
package database

deny[msg] {
  input.kind == "Deployment"
  input.spec.template.spec.containers[_].env[_].valueFrom.secretKeyRef
  msg = "Direct secret references in environment variables are not allowed. Use secrets mounting instead."
}

deny[msg] {
  input.kind == "Deployment"
  not input.spec.template.spec.securityContext
  msg = "Security context must be defined for all containers."
}

deny[msg] {
  input.kind == "Service"
  input.spec.type == "LoadBalancer"
  not input.spec.loadBalancerSourceRanges
  msg = "LoadBalancer services must specify source IP ranges."
}
# Apply policy
kubectl apply -f - <<EOF
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
  name: require-security-labels
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Namespace"]
  parameters:
    labels:
      - key: "security-level"
      - key: "data-classification"
EOF

Best Practices

1. Fail the Build for Critical Issues

# โœ… Good: Critical issues fail the build
trivy:
  image-ref: 'myapp:latest'
  severity: 'CRITICAL,HIGH'
  exit-code: '1'  # Fail on critical/high
  
# โš ๏ธ Warning: Only fail for critical
trivy:
  severity: 'CRITICAL'
  exit-code: '1'

2. Use Trusted Base Images

# โœ… Good: Specific version, minimal
FROM python:3.12-slim@sha256:abc123

# โŒ Bad: Moving tag
FROM python:3.12-slim

# โŒ Bad: Full tag
FROM python:3.12.0

3. Scan Infrastructure as Code

# .github/workflows/tfsec.yml
name: Terraform Security

on:
  push:
    paths:
      - '**.tf'
      - '**.tfvars'

jobs:
  tfsec:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Run tfsec
        uses: aquasecurity/[email protected]
        with:
          soft_fail: true

4. Generate SBOM

# .github/workflows/sbom.yml
name: Software Bill of Materials

on:
  push:
    branches: [main]
  workflow_dispatch:

jobs:
  sbom:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Generate SBOM
        uses:anchore/sbom-action@v0
        with:
          output-file: sbom.spdx.json
          
      - name: Upload SBOM
        uses: actions/upload-artifact@v4
        with:
          name: sbom
          path: sbom.spdx.json

Common Pitfalls

1. Too Many False Positives

Wrong:

# Scans everything with default rules
trivy:
  config: default
  # No filtering = alert fatigue

Correct:

# Filter known issues
trivy:
  vuln-type: 'os,library'
  severity: 'CRITICAL,HIGH'
  ignore-unfixed: true
  exception:
    - id: 'CVE-2021-12345'
      reason: 'Mitigated by WAF'

2. Blocking Deployments for Warnings

Wrong:

# Fail for any finding
trivy:
  exit-code: '1'  # Any severity

Correct:

# Only block critical
trivy:
  severity: 'CRITICAL'
  exit-code: '1'
  
# Warnings: create issues but don't block
trivy:
  severity: 'HIGH'
  exit-code: '0'  # Continue but warn

3. Not Fixing Findings

Wrong:

# Scan but ignore results
trivy:
  severity: 'CRITICAL'
  # No action on results!

Correct:

# Create tracking issues
trivy:
  severity: 'CRITICAL'
  create-issue: true
  issue-labels: 'security,needs-triage'

Tools Comparison

Category Tools Best For
SAST Semgrep, SonarQube, Bandit Code analysis
SCA Dependabot, Snyk, Safety Dependencies
DAST OWASP ZAP, Nuclei Running apps
Containers Trivy, Clair, Hadolint Images
Secrets GitLeaks, TruffleHog Credentials
IaC tfsec, checkov, Terrascan Terraform/K8s

External Resources

Official Documentation

Tools

Learning Resources


Key Takeaways

  • DevSecOps integrates security throughout the development lifecycle
  • Shift-left moves security earlier in the process
  • SAST/SCA/DAST cover different security testing needs
  • Automate security checks in CI/CD pipelines
  • Don’t block deployments for warningsโ€”only critical issues
  • Generate SBOM for supply chain security
  • False positives lead to alert fatigueโ€”tune your tools

Comments