Cloud Security22 min read

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.

I
Microsoft Cloud Solution Architect
DevSecOpsCI/CDSASTDASTSCAGitHub ActionsSecurity AutomationShift Left

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:

Loading diagram...

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 severity

Critical: 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

LanguageToolNotes
JavaScript/TypeScriptSemgrep, SonarQube, ESLint security rulesSemgrep has excellent JS/TS rules
PythonBandit, Semgrep, Pylint securityBandit is fast and CI-friendly
GoGosec, SemgrepGosec covers Go-specific patterns
JavaSpotBugs + FindSecBugs, SonarQubeSpotBugs is free, SonarQube has deeper rules
C/C++Flawfinder, Cppcheck, CoverityCoverity is paid but industry standard
Multi-languageSemgrep, CodeQL, Snyk CodeCodeQL 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.sarif

Tuning 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 code

Start 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=all

Dependency 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.sarif

Common 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.sarif

Image 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: true

DAST 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?

SeverityRecommended Gate
Critical CVE (CVSS 9.0+) in dependencyBlock PR merge
High CVE (CVSS 7.0-8.9) in dependencyBlock PR merge
Hardcoded secretBlock PR merge + alert security team
Critical SAST finding (injection, XXE)Block PR merge
High SAST findingBlock PR merge
Medium SAST findingWarning, doesn't block
IaC critical misconfigurationBlock PR merge
Medium CVE (unfixed)Warning, informational
License violationBlock 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.

I

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