Skip to main content
BlogDevOps

DevSecOps: Integrating Security into CI/CD

Shift security left with DevSecOps: embed SAST, DAST, SCA, container image scanning, and secrets detection directly into your GitHub Actions CI/CD pipeline with real workflow examples.

Matthews Wong

Software Engineer

November 202410 min read
DevSecOps: Integrating Security into CI/CD

Photo by Unsplash

DevSecOps integrates security testing directly into the software delivery lifecycle rather than treating it as a separate phase after development is complete. The 'shift-left' approach means every pull request is automatically scanned for vulnerabilities, exposed secrets, and insecure dependencies — catching issues when they're cheapest to fix: before they reach production. This article walks through building a practical DevSecOps pipeline using GitHub Actions, Semgrep, Trivy, Gitleaks, and OWASP ZAP.

Shifting Security Left: The DevSecOps Pipeline

A mature DevSecOps CI/CD pipeline runs multiple security gates in parallel: Static Application Security Testing (SAST) analyzes source code for vulnerabilities, Software Composition Analysis (SCA) scans dependencies for known CVEs, secrets detection scans for accidentally committed credentials, and container image scanning ensures your Docker images don't carry known vulnerabilities. Each gate has a clear pass/fail criterion and blocks deployment on critical findings.

SAST and Dependency Scanning with Semgrep and Trivy

Semgrep is an open-source SAST tool with hundreds of OWASP-aligned rules. It scans source code for SQL injection, XSS, hardcoded secrets, and insecure API usage. Trivy is a versatile scanner that handles both filesystem dependency scanning (npm, pip, go.mod) and container image scanning. Running them together on every pull request gives you immediate feedback on security regressions introduced by a change.

# .github/workflows/devsecops.yml — Security gates in CI/CD
name: DevSecOps Pipeline
on: [push, pull_request]

jobs:
  sast:
    name: Static Analysis (SAST)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run Semgrep SAST
        uses: semgrep/semgrep-action@v1
        with:
          config: p/owasp-top-ten

  dependency-scan:
    name: Dependency Vulnerability Scan
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run Trivy on dependencies
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: fs
          scan-ref: .
          severity: CRITICAL,HIGH
          exit-code: 1   # Fail build on critical/high vulns

  secrets-detection:
    name: Secrets Detection
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: gitleaks/gitleaks-action@v2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Container Image Scanning in the Pipeline

Container images are a common attack surface — base images like ubuntu:latest or node:18 frequently have known CVEs in their installed packages. Trivy image scanning checks the image manifest against the CVE databases (NVD, GitHub Advisory Database) and reports vulnerabilities by severity. Integrating SARIF output with GitHub's security tab gives you a permanent record of all findings across your repositories.

Building Docker Images and Scanning Before Push

The security gate runs after the Docker build step but before pushing to the registry. If Trivy finds critical or high-severity vulnerabilities, the GitHub Actions step exits with code 1, blocking the push. The SARIF upload step uses if: always() to ensure findings are recorded even when the build fails — giving security teams visibility into what was blocked and why.

# Container image scanning with Trivy
  container-scan:
    name: Container Image Scan
    runs-on: ubuntu-latest
    needs: [sast]
    steps:
      - uses: actions/checkout@v4

      - name: Build Docker image
        run: docker build -t myapp:${{ github.sha }} .

      - name: Scan image with Trivy
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: myapp:${{ github.sha }}
          format: sarif
          output: trivy-results.sarif
          severity: CRITICAL,HIGH
          exit-code: 1

      - name: Upload SARIF to GitHub Security
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: trivy-results.sarif

Use policy-as-code for vulnerability exceptions. Rather than hardcoding severity thresholds, maintain a .security/policy.yml file that specifies which CVE IDs are accepted with expiry dates and justifications. This creates an auditable trail of accepted risk decisions and auto-expires waivers so they get reviewed regularly.

Dynamic Application Security Testing (DAST)

DAST tests your running application by sending crafted HTTP requests to find security vulnerabilities that only appear at runtime — things like broken authentication, insecure direct object references, and misconfigured CORS headers that SAST can't detect by looking at code alone. OWASP ZAP is the most widely-used open-source DAST tool, and its GitHub Action makes integration straightforward.

OWASP ZAP Baseline Scan Against Staging

The ZAP baseline scan is a passive scan that doesn't actively attack the application — it crawls the site, observes responses, and flags security issues without sending malicious payloads. It's safe to run against a staging environment in every CI/CD run. For more thorough testing, the ZAP full scan includes active attack rules but should be run less frequently and in an isolated environment.

# DAST with OWASP ZAP against a staging environment
  dast:
    name: Dynamic Analysis (DAST)
    runs-on: ubuntu-latest
    needs: [container-scan]
    steps:
      - name: ZAP Baseline Scan
        uses: zaproxy/action-baseline@v0.12.0
        with:
          target: https://staging.myapp.com
          rules_file_name: .zap/rules.tsv
          cmd_options: >
            -I            # ignore warnings (only fail on alerts)
            -j            # output JSON report
            -a            # include alpha active scan rules
False Positives Will Kill Adoption

A DevSecOps pipeline that blocks every build with false positives will quickly be bypassed or disabled by frustrated developers. Start with high-confidence rules only, tune thresholds conservatively (CRITICAL and HIGH only, not MEDIUM), and establish a clear process for accepting false positives with justification. Security tooling should help developers, not antagonize them.

Secrets Detection and Supply Chain Security

Gitleaks scans the full Git history for accidentally committed credentials — AWS keys, GitHub tokens, database passwords, JWT secrets. Running it with fetch-depth: 0 scans every commit in the repository, not just the latest. For supply chain security, generate and publish a Software Bill of Materials (SBOM) using Syft or Trivy's SBOM generation mode — this documents every dependency in your container image for compliance and incident response.

DevSecOps Key Concepts

Essential DevSecOps terminology you'll encounter in the field: SAST, DAST, SCA, CVE, shift-left security, and SBOM.

DevSecOps is a cultural shift as much as a technical one. Start small: add Gitleaks to prevent secrets from entering the repository, then add Trivy dependency scanning, then container scanning. Celebrate wins when the pipeline catches real vulnerabilities. Invest in developer security training so the team understands why the gates exist — teams that understand security context write more secure code proactively.

FAQ

Frequently Asked Questions

Short answers to what readers ask most about this topic.

What tools does the DevSecOps pipeline in this post use, and what does each one do?

The pipeline uses Semgrep for SAST (scanning source code for vulnerabilities like SQL injection and XSS), Trivy for both dependency scanning (npm, pip, go.mod) and container image scanning against CVE databases, Gitleaks for secrets detection across the full Git history, and OWASP ZAP for DAST against a staging environment. Each tool targets a different attack surface, so running them together gives broad security coverage on every pull request.

What is the difference between SAST and DAST, and why does this pipeline use both?

SAST (Static Application Security Testing) analyzes source code without running the application, catching issues like hardcoded secrets and insecure API usage. DAST (Dynamic Application Security Testing) sends crafted HTTP requests to a running application to find vulnerabilities like broken authentication, insecure direct object references, and misconfigured CORS headers that SAST cannot detect from code alone. Using both ensures coverage at compile time and at runtime.

How does the pipeline handle a security finding — does it block deployment automatically?

Yes. If Trivy finds critical or high-severity vulnerabilities during the container image scan, the GitHub Actions step exits with code 1, which blocks the push to the registry. The SARIF upload step uses if: always() so that findings are recorded even when the build fails, giving security teams a permanent audit trail of what was blocked and why.

What is the recommended approach for managing false positives without killing developer productivity?

The post recommends starting with high-confidence rules only and tuning thresholds conservatively — flagging only CRITICAL and HIGH findings, not MEDIUM. It also suggests establishing a clear process for accepting false positives with written justification. Using a policy-as-code file (.security/policy.yml) to track accepted CVE IDs with expiry dates and justifications creates an auditable trail and forces periodic review of waivers.

How does the pipeline address supply chain security beyond scanning application code?

Gitleaks scans the full Git history (using fetch-depth: 0) to catch accidentally committed credentials such as AWS keys, GitHub tokens, and JWT secrets. Additionally, the post recommends generating and publishing a Software Bill of Materials (SBOM) using Syft or Trivy's SBOM generation mode, which documents every dependency in the container image for compliance auditing and incident response.