All Articles

Build a DevSecOps Pipeline from Scratch (2026 Project Walkthrough)

A complete end-to-end DevSecOps pipeline with SAST, container scanning, secrets detection, DAST, and supply chain security using open-source tools.

DevOpsBoysApr 6, 20263 min read
Share:Tweet

DevSecOps gets thrown around a lot. This guide shows you how to actually build it — a real pipeline with security checks at every stage, using open-source tools on GitHub Actions.


What You Will Build

Code Push → Secret Scan → SAST → Dependency Scan
                                         ↓
                              Docker Build + Container Scan
                                         ↓
                              Image Signing → Deploy Staging
                                         ↓
                              DAST → Deploy Production

Why Security in the Pipeline?

The earlier you catch vulnerabilities, the cheaper they are to fix:

  • Found in development: $10 to fix
  • Found in staging: $100 to fix
  • Found in production: $10,000+ (incident, customer impact, compliance)

Stage 1: Secret Scanning with Gitleaks

Catches hardcoded API keys, passwords, and tokens before they reach your repo history.

yaml
- name: Detect secrets with Gitleaks
  uses: gitleaks/gitleaks-action@v2
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Run this before any other stage. A secret in code invalidates everything downstream.


Stage 2: SAST with Semgrep

Static Application Security Testing analyzes source code without running it. Finds SQL injection, XSS, hardcoded credentials, insecure patterns.

yaml
- name: Run Semgrep SAST
  uses: semgrep/semgrep-action@v1
  with:
    config: >-
      p/security-audit
      p/owasp-top-ten
      p/nodejs

Free for open-source. 3000+ rules out of the box.


Stage 3: Dependency Scanning

Your code might be clean, but your npm/pip/maven packages might have known CVEs.

yaml
- name: OWASP Dependency Check
  uses: dependency-check/Dependency-Check_Action@main
  with:
    project: myapp
    path: .
    format: SARIF
    args: --failOnCVSS 7

--failOnCVSS 7 fails the build on HIGH and CRITICAL vulnerabilities.


Stage 4: Container Image Scanning with Trivy

After building the Docker image, scan it before pushing.

yaml
- name: Build Docker 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
    exit-code: 1
 
- name: Push image (only if scan passes)
  run: docker push ghcr.io/myorg/myapp:${{ github.sha }}

Trivy scans OS packages, language packages, Dockerfile misconfigs, and secrets baked into image layers.


Stage 5: Image Signing with cosign

Proves the image was built by your pipeline and not tampered with.

yaml
- name: Install cosign
  uses: sigstore/cosign-installer@v3
 
- name: Sign image (keyless via Sigstore)
  run: |
    cosign sign --yes ghcr.io/myorg/myapp:${{ github.sha }}

Before deploying to production, verify:

bash
cosign verify \
  --certificate-identity-regexp="https://github.com/myorg/myapp" \
  --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \
  ghcr.io/myorg/myapp:abc123

If verification fails — image was tampered with or not built by your pipeline.


Stage 6: DAST with OWASP ZAP

Dynamic Application Security Testing runs against the deployed staging environment. Finds auth bypass, injection flaws, and misconfigurations that only appear at runtime.

yaml
- name: OWASP ZAP Baseline Scan
  uses: zaproxy/action-baseline@v0.12.0
  with:
    target: https://staging.myapp.com
    cmd_options: -a

Only runs after staging deploy. ZAP needs a live target.


Complete Job Structure

yaml
name: DevSecOps Pipeline
on:
  push:
    branches: [main]
 
jobs:
  secret-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: gitleaks/gitleaks-action@v2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 
  sast:
    needs: secret-scan
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: semgrep/semgrep-action@v1
        with:
          config: p/security-audit p/owasp-top-ten
 
  build-scan:
    needs: sast
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: docker build -t myapp:${{ github.sha }} .
      - uses: aquasecurity/trivy-action@master
        with:
          image-ref: myapp:${{ github.sha }}
          severity: CRITICAL,HIGH
          exit-code: 1
      - run: docker push ghcr.io/myorg/myapp:${{ github.sha }}
 
  deploy-staging:
    needs: build-scan
    environment: staging
    runs-on: ubuntu-latest
    steps:
      - run: echo "Deploy to staging"
 
  dast:
    needs: deploy-staging
    runs-on: ubuntu-latest
    steps:
      - uses: zaproxy/action-baseline@v0.12.0
        with:
          target: https://staging.myapp.com
 
  deploy-production:
    needs: dast
    environment: production
    runs-on: ubuntu-latest
    steps:
      - run: echo "Deploy to production"

What Each Stage Catches

StageToolCatches
Secret scanGitleaksHardcoded API keys, tokens
SASTSemgrepSQLi, XSS, insecure patterns
Dependency scanOWASP DCKnown CVEs in packages
Container scanTrivyOS CVEs, Dockerfile misconfigs
Image signingcosignSupply chain tampering
DASTOWASP ZAPRuntime vulnerabilities

What to Add Next

  • Kyverno/OPA — enforce K8s admission policies
  • SBOM — generate Software Bill of Materials with syft
  • Falco — runtime threat detection in containers
  • Checkov — Terraform and Helm security scanning

Resources

Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Comments