DevOps

DevSecOps: Shift-Left Security in CI/CD Pipelines with SAST, DAST & Supply Chain Protection

Security vulnerabilities found in production cost 95× more to fix than those caught at commit time. DevSecOps eliminates this gap by weaving security gates into every stage of your CI/CD pipeline — from pre-commit hooks to runtime monitoring. This comprehensive guide covers every tool, technique, and automation pattern needed to build a production-grade DevSecOps pipeline in 2026.

Md Sanwar Hossain April 10, 2026 22 min read DevOps
DevSecOps shift-left security CI/CD pipeline with SAST DAST and supply chain protection

TL;DR — DevSecOps in One Sentence

"Integrate SAST (CodeQL, Semgrep) at build time, SCA (Snyk, Dependabot) for dependencies, container scanning (Trivy) at image build, DAST (OWASP ZAP) in staging, secrets detection (Gitleaks) at pre-commit, and SBOM + Cosign for supply chain trust — all automated in GitHub Actions. Fix security in code review, not in a post-breach incident."

Table of Contents

  1. What is DevSecOps? Shift-Left Security Explained
  2. The DevSecOps Pipeline: Security Gates at Every Stage
  3. SAST: Static Application Security Testing
  4. DAST: Dynamic Application Security Testing
  5. SCA: Software Composition Analysis & Dependency Scanning
  6. Container Image Scanning: From Build to Runtime
  7. Secret Detection & Prevention: Pre-Commit & CI
  8. SBOM: Software Bill of Materials for Compliance
  9. Supply Chain Security: SLSA, Sigstore/Cosign & in-toto
  10. Infrastructure as Code Security Scanning
  11. Complete DevSecOps Pipeline in GitHub Actions
  12. Conclusion & DevSecOps Maturity Checklist

1. What is DevSecOps? Shift-Left Security Explained

DevSecOps is the practice of integrating security controls, testing, and culture throughout the entire software development lifecycle — not as a gate at the end, but as a continuous layer woven into every phase from code authoring to production monitoring. The "shift-left" metaphor refers to moving security earlier (to the left) on a traditional timeline that reads: Design → Code → Build → Test → Deploy → Operate.

Traditional Security vs. Shift-Left Security

In the traditional "perimeter security" model, a dedicated security team performs penetration testing and vulnerability assessments as a separate phase — typically right before or after release. Developers have no visibility into security risks until a formal security review catches them late in the cycle, when context is stale and fixes are costly. The results are predictable: security becomes a bottleneck, developers resent it, and many findings get triaged as "accepted risk" because there's no time to fix them.

Shift-left security changes the economics entirely. According to the IBM Systems Sciences Institute, a bug fixed during design costs $1 to remediate. The same bug found in integration testing costs $10. In production, that cost skyrockets to $100 or more. For security vulnerabilities, the asymmetry is even more extreme — a production breach can cost millions in remediation, regulatory fines, and reputational damage.

The DevSecOps Cultural Shift

DevSecOps is not just a toolchain change — it is a cultural transformation. Security moves from a gatekeeping function to a shared responsibility. Developers own security in their code. Platform engineers embed security into CI/CD pipelines. Security engineers write policies as code and build tooling that gives developers fast, actionable feedback in the tools they already use (IDEs, pull requests, Slack). The goal is to make security frictionless enough that developers choose the secure path by default.

The Business Case: Cost of Fixing Security Bugs

  • Pre-commit / IDE: ~$80 per finding (developer self-service, 5–10 minute fix)
  • CI build gate: ~$240 per finding (PR review context still fresh, same sprint)
  • Pre-deploy / staging: ~$960 per finding (regression testing required, cross-team coordination)
  • Production: ~$7,600+ per finding (rollback, hotfix, incident response, customer impact)
  • Post-breach: $4.35M average total breach cost (IBM Cost of Data Breach Report 2025)

2. The DevSecOps Pipeline: Security Gates at Every Stage

A mature DevSecOps pipeline implements security controls at four primary stages: pre-commit, CI build, CD pre-deploy, and runtime. Each stage catches different classes of vulnerabilities with different tools and trade-offs in speed vs. depth.

DevSecOps shift-left security pipeline: pre-commit to runtime stages with tool mapping
DevSecOps Shift-Left Pipeline — security tools and cost of fixing at each stage. Source: mdsanwarhossain.me
Pipeline Stage Security Controls Latency Feedback Loop
Pre-Commit (IDE / hooks) Secrets scan, lint-based SAST, IaC format < 5 sec Instant
CI Build (PR / merge) SAST (CodeQL), SCA, container scan, secrets 2–10 min PR comment
CD Pre-Deploy (staging) DAST (ZAP), pen test, SBOM, signing 15–60 min Deployment gate
Runtime (production) WAF, RASP, Falco, SIEM, vuln mgmt Continuous Alert / incident

The key design principle is fail fast at the cheapest stage. A critical secret leaked in a pre-commit hook costs a developer 30 seconds to fix. The same secret discovered in a production security audit triggers an incident response requiring key rotation across dozens of services, notifications to compliance teams, and potentially breach disclosure.

3. SAST: Static Application Security Testing

Static Application Security Testing (SAST) analyzes source code, bytecode, or binary without executing the application. It finds vulnerabilities like SQL injection, XSS, path traversal, insecure deserialization, and hardcoded credentials by pattern-matching against a ruleset or using data-flow analysis to trace tainted inputs to dangerous sinks.

SAST Tools: SonarQube, Semgrep, and CodeQL Compared

Tool Analysis Type Languages CI Speed Best For
CodeQL (GitHub) Data-flow / taint Java, JS, Python, C++, Go, Ruby 3–8 min Deep vulnerability detection, GitHub repos
Semgrep Pattern matching + AST 30+ languages < 30 sec Custom rules, fast CI, secrets, OWASP Top 10
SonarQube Pattern + data-flow 25+ languages 2–6 min Enterprise dashboards, quality gates, code smells
SpotBugs / FindSecBugs Bytecode analysis Java / JVM < 1 min Java Spring Boot, existing Maven/Gradle builds

Semgrep Custom Rule for Java Security

One of Semgrep's most powerful features is writing custom rules that match your organization's specific security policies. Here's a rule that catches dangerous use of Runtime.exec() with user-controlled input — a classic OS command injection pattern in Spring Boot applications:

# semgrep-rules/java-security.yaml
rules:
  - id: java-os-command-injection
    patterns:
      - pattern: Runtime.getRuntime().exec($CMD)
      - pattern-not: Runtime.getRuntime().exec("...")  # static string is safe
    message: |
      OS command injection: $CMD is potentially user-controlled.
      Use ProcessBuilder with argument lists instead of shell string concatenation.
    languages: [java]
    severity: ERROR
    metadata:
      cwe: CWE-78
      owasp: A03:2021 - Injection
      fix: "Use ProcessBuilder(List.of(...)) with explicit arguments"

  - id: java-sql-injection-concatenation
    pattern: |
      $STMT.executeQuery("..." + $USER_INPUT + "...")
    message: "Potential SQL injection via string concatenation. Use PreparedStatement."
    languages: [java]
    severity: ERROR
    metadata:
      cwe: CWE-89

  - id: java-insecure-deserialization
    pattern: |
      new ObjectInputStream($STREAM).readObject()
    message: |
      Insecure deserialization detected. Validate class whitelist before deserializing
      or use a safe serialization format (JSON, Protobuf).
    languages: [java]
    severity: WARNING
    metadata:
      cwe: CWE-502

Managing SAST False Positives

The biggest practical challenge with SAST tools is false positives. A pipeline that generates hundreds of false alarms trains developers to ignore warnings — the opposite of the desired effect. Effective false positive management strategies include:

4. DAST: Dynamic Application Security Testing

Dynamic Application Security Testing (DAST) tests the running application by sending malicious HTTP requests and analyzing responses. Unlike SAST, DAST doesn't need source code — it treats the application as a black box, making it excellent for catching authentication bypasses, session management flaws, CSRF, and injection vulnerabilities that only manifest at runtime.

OWASP ZAP Automation in CI/CD

OWASP ZAP (Zed Attack Proxy) is the industry standard open-source DAST tool. In CI/CD, you deploy the application to a staging environment and run ZAP's Automation Framework against it:

# .github/workflows/dast-zap.yml (excerpt)
- name: Run OWASP ZAP API Scan
  uses: zaproxy/action-api-scan@v0.9.0
  with:
    target: 'https://staging.myapp.example.com/v1/openapi.json'
    format: openapi
    fail_action: true
    rules_file_name: '.zap/rules.tsv'
    cmd_options: '-a'   # include alpha passive rules

- name: ZAP Baseline Scan (passive only)
  uses: zaproxy/action-baseline@v0.12.0
  with:
    target: 'https://staging.myapp.example.com'
    fail_action: warn   # warn on baseline; block on full scan

# rules.tsv — tune alert thresholds per rule ID
# 10096  IGNORE  (Web Browser XSS Protection Not Enabled — handled by WAF)
# 10038  WARN    (Content Security Policy)
# 90022  FAIL    (Application Error Disclosure)

Burp Suite Enterprise for Authenticated Scanning

For applications requiring authentication or complex session handling, Burp Suite Enterprise provides REST API-driven scanning that integrates with CI/CD. Key features for pipeline integration include: recorded login sequences to maintain authenticated sessions, out-of-band (OAST) detection for blind injection vulnerabilities, and OpenAPI/Swagger-based crawling to ensure full endpoint coverage.

API Security Testing with DAST

Modern applications are predominantly API-driven. Traditional web crawling DAST misses 60–80% of an API surface. Use OpenAPI/Swagger specifications to seed DAST scanners with the complete endpoint inventory, including request bodies, parameter types, and authentication schemes. Key API-specific checks include: broken object level authorization (BOLA/IDOR), missing rate limiting, JWT algorithm confusion, and mass assignment vulnerabilities.

5. SCA: Software Composition Analysis & Dependency Scanning

Software Composition Analysis (SCA) identifies known vulnerabilities in your open-source dependencies by comparing them against vulnerability databases (NVD, GitHub Advisory, OSV). Given that 70–90% of modern application code is open-source libraries, SCA has become one of the highest-ROI security controls in any pipeline.

SAST vs. DAST vs. SCA: The Complete Comparison

Dimension SAST DAST SCA
What it tests Source code, bytecode Running application 3rd-party dependencies
When to run Pre-commit, CI build Staging / pre-deploy CI build + continuous
False positive rate High (10–30%) Low (2–8%) Very low (CVE-based)
Key tools CodeQL, Semgrep, SonarQube OWASP ZAP, Burp Suite Snyk, Dependabot, OWASP DC
Needs running app No Yes No
Catches 0-days No (rule-based) Sometimes No (CVE-based)

Snyk and OWASP Dependency Check in Practice

Snyk provides the most developer-friendly SCA experience: inline PR comments, fix PRs that auto-upgrade vulnerable dependencies, and a license compliance engine. It integrates natively with GitHub, GitLab, Jenkins, and all major CI systems. OWASP Dependency Check is the gold standard open-source alternative — it scans Maven, Gradle, npm, NuGet, and pip manifests against the NVD database and generates detailed HTML/XML reports suitable for compliance evidence.

GitHub Dependabot provides always-on dependency monitoring that auto-creates PRs when new CVEs are published for your dependencies. Configure it with severity thresholds to auto-merge patch-level security updates in low-risk projects, reducing the operational overhead of dependency maintenance.

6. Container Image Scanning: From Build to Runtime

Containers have become the primary deployment artifact for cloud-native applications, and they bring a unique attack surface: the base image, installed OS packages, language runtime, and application dependencies all contribute to the vulnerability surface. Container scanning must happen at multiple points: during image build, before registry push, at deployment admission, and continuously at runtime.

Trivy: The Industry Standard Container Scanner

Trivy (by Aqua Security) scans container images, filesystems, Git repositories, and Kubernetes clusters for OS vulnerabilities, language dependencies, IaC misconfigurations, and exposed secrets. Here's how to integrate it with a critical-severity build gate:

# Trivy container scan with critical/high threshold
- name: Build Docker image
  run: docker build -t myapp:${{ github.sha }} .

- name: Run Trivy vulnerability scanner
  uses: aquasecurity/trivy-action@master
  with:
    image-ref: 'myapp:${{ github.sha }}'
    format: 'sarif'
    output: 'trivy-results.sarif'
    severity: 'CRITICAL,HIGH'
    exit-code: '1'           # fail build on CRITICAL or HIGH findings
    ignore-unfixed: true     # skip vulnerabilities with no fix yet
    vuln-type: 'os,library'
    timeout: '10m0s'

- name: Upload Trivy results to GitHub Security tab
  uses: github/codeql-action/upload-sarif@v3
  if: always()   # upload even if scan fails
  with:
    sarif_file: 'trivy-results.sarif'
    category: 'container-scanning'

- name: Trivy config scan (IaC / Dockerfile misconfigs)
  uses: aquasecurity/trivy-action@master
  with:
    scan-type: 'config'
    scan-ref: '.'
    severity: 'HIGH,CRITICAL'
    exit-code: '1'

Distroless and Minimal Base Images

The most effective container hardening strategy is reducing the attack surface of the base image itself. Distroless images (gcr.io/distroless/java21-debian12) contain only the application runtime — no shell, no package manager, no coreutils. This eliminates entire categories of vulnerabilities and makes post-exploitation significantly harder since an attacker who achieves RCE has no tools to work with.

For Spring Boot applications, the recommended pattern is multi-stage builds: compile in a full JDK image, then copy only the JAR into a distroless JRE image. Pair this with Docker Scout for continuous registry monitoring — it watches your production images and alerts when new CVEs are published for packages in already-deployed images.

7. Secret Detection & Prevention: Pre-Commit & CI

Exposed secrets — API keys, database credentials, private certificates, OAuth tokens — are the single most common cause of cloud security breaches. According to GitGuardian's 2025 State of Secrets Sprawl report, 1 in 10 public GitHub repositories has a live secret exposure. The average time to detect and remediate an exposed secret is 27 days, during which it may have already been exploited.

Gitleaks: Pre-Commit and CI Integration

Gitleaks scans Git history, staged changes, and repository contents for secrets using a high-signal ruleset covering 150+ secret patterns (AWS keys, Stripe tokens, GitHub PATs, private keys, JWT secrets, and more). Configure it as a pre-commit hook to catch secrets before they ever reach a remote:

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.18.4
    hooks:
      - id: gitleaks

# Custom .gitleaks.toml for organization-specific patterns
[extend]
useDefault = true

[[rules]]
  id = "internal-api-token"
  description = "Internal MYCO API token"
  regex = '''MYCO_[A-Z0-9]{32}'''
  tags = ["key", "internal"]

[[allowlist]]
  description = "Test fixtures"
  paths = ['''test/fixtures/.*''']
  regexes = ['''FAKE_SECRET_FOR_TESTING''']

# In GitHub Actions CI
- name: Scan for secrets with Gitleaks
  uses: gitleaks/gitleaks-action@v2
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}  # required for orgs

GitHub Native Secret Scanning & Push Protection

GitHub Advanced Security provides built-in secret scanning with push protection — a server-side control that rejects pushes containing detected secrets before they land in the repository. This is complementary to pre-commit hooks (which can be bypassed with --no-verify) and provides a mandatory network-level control. GitHub's secret scanning covers 200+ partner token types with validity checking — it can confirm in real time whether a detected AWS key is still active.

HashiCorp Vault Integration: Secrets at Runtime

The best secret is one that never exists in your codebase at all. Replace static credentials with dynamic secrets from HashiCorp Vault or AWS Secrets Manager. In CI/CD, use the Vault GitHub Actions integration to inject short-lived credentials at pipeline runtime — the credentials expire automatically, so even if leaked from a log, they're worthless within minutes. In Kubernetes, use the Vault Agent Injector or External Secrets Operator to inject secrets as environment variables or mounted files without persisting them in etcd.

8. SBOM: Software Bill of Materials for Compliance

A Software Bill of Materials (SBOM) is a machine-readable inventory of all components, libraries, and dependencies in a software artifact — the digital equivalent of an ingredient list. Following Executive Order 14028 (US) and EU Cyber Resilience Act requirements, SBOMs have moved from "best practice" to a compliance mandate for software sold to government and many enterprise customers in 2026.

SBOM Formats: CycloneDX vs. SPDX

CycloneDX (OWASP) is optimized for security use cases — it supports vulnerability disclosure, license compliance, and hardware/firmware BOMs. It's the preferred format for DevSecOps pipelines. SPDX (Linux Foundation) is the ISO standard (ISO 5962:2021) and better suited for license compliance workflows. Most enterprise tooling supports both.

Generating and Attesting SBOMs with Syft and Cosign

# SBOM generation with Syft + attestation with Cosign
# Step 1: Generate SBOM in both formats
- name: Generate SBOM (CycloneDX)
  uses: anchore/sbom-action@v0.15.8
  with:
    image: myapp:${{ github.sha }}
    format: cyclonedx-json
    output-file: sbom.cdx.json

- name: Generate SBOM (SPDX)
  run: |
    syft myapp:${{ github.sha }} \
      --output spdx-json=sbom.spdx.json \
      --output cyclonedx-json=sbom.cyclonedx.json

# Step 2: Sign the container image with Cosign (keyless OIDC)
- name: Install Cosign
  uses: sigstore/cosign-installer@v3.4.0

- name: Sign container image
  run: |
    cosign sign --yes \
      --rekor-url https://rekor.sigstore.dev \
      registry.example.com/myapp:${{ github.sha }}

# Step 3: Attach SBOM as signed attestation
- name: Attest SBOM
  run: |
    cosign attest --yes \
      --type cyclonedx \
      --predicate sbom.cdx.json \
      registry.example.com/myapp:${{ github.sha }}

# Step 4: Verify at deployment time (in CD pipeline or admission webhook)
- name: Verify image signature and SBOM
  run: |
    cosign verify \
      --certificate-identity-regexp="https://github.com/myorg/myrepo" \
      --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \
      registry.example.com/myapp:${{ github.sha }}

9. Supply Chain Security: SLSA, Sigstore/Cosign & in-toto Attestations

The SolarWinds and Log4Shell incidents demonstrated that even perfectly written application code can be compromised through its build toolchain or dependencies. Supply chain security addresses the entire provenance chain: from source commit to deployed artifact, every transformation must be verifiable.

SLSA Framework: Supply chain Levels for Software Artifacts

SLSA (pronounced "salsa") is a framework developed by Google and the OpenSSF that defines four security levels for build provenance. Each level adds stronger guarantees about the integrity of the build process:

For most production pipelines, targeting SLSA Level 2–3 provides excellent protection against the most common supply chain attacks. Use the slsa-github-generator to automatically generate SLSA provenance in GitHub Actions with no changes to your existing build scripts.

Sigstore Ecosystem: Keyless Signing for the Masses

Sigstore provides a free, open, non-profit infrastructure for signing software artifacts using short-lived OIDC-based identities — no key management required. The ecosystem consists of: Cosign (sign and verify container images and artifacts), Fulcio (certificate authority that issues short-lived signing certificates), and Rekor (transparency log that provides tamper-evident audit trail of all signatures). All npm packages published to the npm registry since late 2023 include Sigstore provenance by default.

in-toto Attestations: Linking the Entire Chain

in-toto attestations allow you to assert facts about your build process in a tamper-evident way: "this build was performed on commit X", "these tests passed", "this SBOM was generated by Syft". Consumers of your artifacts can verify the complete attestation chain — from source commit, through build steps, to the final image — without trusting any single party.

10. Infrastructure as Code Security Scanning

Infrastructure as Code (IaC) has moved security misconfigurations upstream — a misconfigured S3 bucket or an overly permissive security group is now a code defect that can be caught in a PR review before it ever reaches a cloud account. IaC security scanning is SAST applied to Terraform, CloudFormation, Kubernetes manifests, Helm charts, and Dockerfiles.

Checkov, tfsec, and KICS in CI/CD

Checkov (Bridgecrew) is the most comprehensive IaC scanner — it supports Terraform, CloudFormation, ARM, Kubernetes, Helm, Dockerfile, and GitHub Actions with 2,000+ built-in policies. tfsec is faster and more Terraform-focused. KICS (Checkmarx) covers the widest range of IaC formats. Here's a Checkov integration for a Terraform + Kubernetes repo:

# Checkov IaC scan in CI pipeline
- name: Run Checkov IaC Security Scan
  uses: bridgecrewio/checkov-action@v12
  with:
    directory: .
    framework: terraform,kubernetes,helm,dockerfile
    output_format: sarif
    output_file_path: checkov-results.sarif
    soft_fail: false          # fail build on policy violations
    skip_check: CKV_AWS_18,CKV_AWS_21   # suppress accepted risks
    check: CKV_AWS_*,CKV_K8S_*

- name: Upload Checkov results
  uses: github/codeql-action/upload-sarif@v3
  if: always()
  with:
    sarif_file: checkov-results.sarif
    category: 'iac-scanning'

# Example Terraform: detect public S3 bucket
# resource "aws_s3_bucket_public_access_block" must be present
# and all four block_public_* flags must be true
# Checkov rule CKV2_AWS_6 catches missing public access block
# Checkov rule CKV_AWS_18 catches missing access logging

Kubernetes-Specific Security Policies

For Kubernetes workloads, complement static IaC scanning with admission-time policy enforcement using OPA/Gatekeeper or Kyverno. Common production policies include: no privileged containers, no containers running as root, all images must come from approved registries, all images must have a valid Cosign signature, and resource limits must be specified. These policies provide a runtime safety net that catches misconfigurations even when IaC scanning is bypassed.

11. Building the Complete DevSecOps Pipeline in GitHub Actions

Here is a production-grade GitHub Actions workflow that orchestrates all the security controls discussed in this guide. It runs SAST, container scanning, secret detection, SCA, IaC scanning, SBOM generation, and image signing in a single coordinated pipeline with appropriate parallelism and fail-fast gates.

# .github/workflows/devsecops-pipeline.yml
name: DevSecOps Security Pipeline

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

permissions:
  contents: read
  security-events: write   # upload SARIF to GitHub Security tab
  id-token: write          # required for Cosign keyless signing
  packages: write          # push signed images to GHCR

env:
  IMAGE_NAME: ghcr.io/${{ github.repository }}:${{ github.sha }}

jobs:
  # ─────────────── SAST: Static Code Analysis ───────────────
  sast-codeql:
    name: SAST — CodeQL
    runs-on: ubuntu-latest
    strategy:
      matrix:
        language: [java, javascript]
    steps:
      - uses: actions/checkout@v4
      - uses: github/codeql-action/init@v3
        with:
          languages: ${{ matrix.language }}
          queries: security-and-quality
      - uses: github/codeql-action/autobuild@v3
      - uses: github/codeql-action/analyze@v3
        with:
          category: "codeql-${{ matrix.language }}"

  sast-semgrep:
    name: SAST — Semgrep
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: semgrep/semgrep-action@v1
        with:
          config: >-
            p/java
            p/spring
            p/owasp-top-ten
            p/secrets
          publishToken: ${{ secrets.SEMGREP_APP_TOKEN }}
          generateSarif: "1"
      - uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: semgrep.sarif

  # ─────────────── Secrets Detection ───────────────
  secrets-scan:
    name: Secrets — Gitleaks
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0   # full history for git log scanning
      - uses: gitleaks/gitleaks-action@v2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  # ─────────────── SCA: Dependency Scanning ───────────────
  sca-snyk:
    name: SCA — Snyk Dependencies
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: snyk/actions/maven@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        with:
          args: --severity-threshold=high --sarif-file-output=snyk.sarif
      - uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: snyk.sarif

  # ─────────────── Container Build & Scan ───────────────
  container-security:
    name: Container — Build & Trivy Scan
    runs-on: ubuntu-latest
    needs: [sast-codeql, secrets-scan]
    outputs:
      image-digest: ${{ steps.build.outputs.digest }}
    steps:
      - uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Build container image
        id: build
        uses: docker/build-push-action@v5
        with:
          context: .
          push: false
          tags: ${{ env.IMAGE_NAME }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
          load: true

      - name: Trivy image scan (CRITICAL/HIGH gate)
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: ${{ env.IMAGE_NAME }}
          format: sarif
          output: trivy-image.sarif
          severity: CRITICAL,HIGH
          exit-code: '1'
          ignore-unfixed: true

      - uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: trivy-image.sarif
          category: container-scan

      - name: Trivy config/Dockerfile scan
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: config
          scan-ref: .
          severity: HIGH,CRITICAL
          exit-code: '1'

  # ─────────────── IaC Security Scanning ───────────────
  iac-scan:
    name: IaC — Checkov
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: bridgecrewio/checkov-action@v12
        with:
          directory: infrastructure/
          framework: terraform,kubernetes
          output_format: sarif
          output_file_path: checkov.sarif
          soft_fail: false
      - uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: checkov.sarif
          category: iac-scan

  # ─────────────── SBOM + Image Signing ───────────────
  sbom-and-sign:
    name: SBOM + Cosign Signing
    runs-on: ubuntu-latest
    needs: [container-security]
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4

      - name: Log in to GHCR
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Push signed image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ env.IMAGE_NAME }}

      - name: Install Cosign
        uses: sigstore/cosign-installer@v3.4.0

      - name: Generate SBOM
        uses: anchore/sbom-action@v0.15.8
        with:
          image: ${{ env.IMAGE_NAME }}
          format: cyclonedx-json
          output-file: sbom.cdx.json

      - name: Sign image (keyless)
        run: cosign sign --yes ${{ env.IMAGE_NAME }}

      - name: Attest SBOM
        run: |
          cosign attest --yes \
            --type cyclonedx \
            --predicate sbom.cdx.json \
            ${{ env.IMAGE_NAME }}

      - name: Upload SBOM artifact
        uses: actions/upload-artifact@v4
        with:
          name: sbom-cyclonedx
          path: sbom.cdx.json

Pipeline Design Principles

  • Parallelize aggressively: SAST, secrets scan, SCA, and IaC scan can all run in parallel — total pipeline time should be < 8 minutes for most repos
  • Gate on severity, not volume: Block builds only on CRITICAL/HIGH; report lower severities as warnings to avoid alert fatigue
  • Upload SARIF to GitHub Security tab: Centralizes all findings in one place — developers don't need to dig through CI logs
  • Sign only on main branch merges: Signing every PR commit wastes Rekor transparency log space and creates noise
  • Cache tool databases: NVD databases and container layer caches dramatically reduce CI time on repeated runs

12. Conclusion & DevSecOps Maturity Checklist

Building a mature DevSecOps pipeline is not a one-sprint project — it is an incremental journey that pays compounding returns as each control reduces your exposure and the cognitive load of your security team. The teams that do it best follow a pragmatic progression: start with the highest-ROI, lowest-friction controls first (secrets scanning, dependency scanning, CodeQL), then add deeper controls as the team's security maturity grows.

The key cultural insight is that security must be a developer experience problem, not just a compliance problem. If your SAST tool generates 500 false positives per PR and takes 20 minutes to run, developers will find ways to bypass it. If it generates 2 high-confidence findings, runs in under 60 seconds, and provides a one-click fix suggestion in the PR, developers will engage with it as a useful tool. Optimize for developer experience first; coverage second.

DevSecOps Maturity Checklist

Level 1 — Foundation (Days 1–30)

  • ☐ Gitleaks pre-commit hook installed across all repos
  • ☐ GitHub Dependabot enabled with weekly security update PRs
  • ☐ CodeQL enabled via GitHub Advanced Security (free for public repos)
  • ☐ Trivy container scan added to CI build, warning on CRITICAL
  • ☐ GitHub Push Protection enabled to block secret commits

Level 2 — Intermediate (Days 30–90)

  • ☐ Semgrep with custom rules tuned to your tech stack
  • ☐ OWASP ZAP baseline scan in staging pipeline
  • ☐ Checkov / tfsec integrated for all Terraform & Kubernetes IaC
  • ☐ Trivy blocking builds on CRITICAL container findings
  • ☐ Snyk SCA integrated with auto-fix PR capability
  • ☐ Secrets rotated to HashiCorp Vault or AWS Secrets Manager

Level 3 — Advanced (Days 90–180)

  • ☐ SBOM generated for every release in CycloneDX format
  • ☐ Container images signed with Cosign (keyless, OIDC-based)
  • ☐ SLSA Level 2 provenance via slsa-github-generator
  • ☐ OPA/Gatekeeper admission policies enforcing image signing
  • ☐ Falco deployed in Kubernetes for runtime threat detection
  • ☐ Security champions program with metrics-driven security reviews
  • ☐ DAST full scan (not just baseline) with authenticated session coverage

The organizations that treat DevSecOps as a product — with an owner, a roadmap, and developer NPS as a success metric — consistently outperform those that treat it as a compliance checkbox. Security tooling that developers love to use is security that actually gets used. Start with the foundation, ship incrementally, and measure everything.

Leave a Comment

Related Posts

Md Sanwar Hossain - Software Engineer
Md Sanwar Hossain

Software Engineer · Java · Spring Boot · Kubernetes · DevSecOps & Cloud Security

All Posts
Last updated: April 10, 2026