Skip to main content
Back to Blog
DevSecOpsSecuritySASTDASTSBOMCI/CDPipelineAppSecDevOpsSoftware Security

DevSecOps in Action: Shifting Security Left with SAST, DAST, and SBOMs in Your Pipeline

Learn how to integrate DevSecOps practices into your CI/CD pipeline by shifting security left with SAST, DAST, and SBOMs. A practical guide for developers looking to build secure software from day one.

April 2, 202611 min readNiraj Kumar

Security has long been the afterthought bolted on at the end of the software development lifecycle — the dreaded "security review" that happens right before production and inevitably delays releases. That era is over.

In 2026, DevSecOps isn't a buzzword. It's the baseline expectation. Organizations that treat security as a shared responsibility — embedded directly into development workflows — ship faster, safer, and with far fewer costly breaches.

This guide walks you through three pillars of a modern secure pipeline: SAST, DAST, and SBOMs. Whether you're just getting started or looking to harden an existing pipeline, you'll leave with actionable patterns, real-world examples, and working configurations.


What Is "Shifting Security Left"?

"Shifting left" means moving security checks earlier in the software development lifecycle — ideally, as early as a developer's local machine or the first CI pipeline stage.

The logic is simple: the earlier you find a vulnerability, the cheaper it is to fix.

A bug caught during code review costs a few minutes. The same bug caught post-deployment can cost millions in incident response, regulatory fines, and lost user trust.

Traditional:  Code → Build → Test → Deploy → [Security Audit] → Prod
Shift Left:   [Security] → Code → [Security] → Build → [Security] → Deploy → Prod

Security becomes a continuous activity, not a checkpoint.


The Three Pillars: SAST, DAST, and SBOMs

🔍 SAST — Static Application Security Testing

SAST tools analyze your source code, bytecode, or binaries without executing the application. Think of it as a security-aware linter that understands vulnerability patterns like SQL injection, hardcoded secrets, insecure deserialization, and more.

When it runs: During development and on every pull request or commit.

What it catches:

  • Injection vulnerabilities (SQL, command, LDAP)
  • Hardcoded credentials and API keys
  • Insecure use of cryptographic functions
  • Path traversal and unsafe file operations
  • Cross-site scripting (XSS) in server-rendered templates

Popular SAST tools:

ToolLanguage SupportNotes
Semgrep30+ languagesFast, highly customizable rules
CodeQL10+ languagesDeep semantic analysis by GitHub
SonarQube30+ languagesFull platform with dashboards
BanditPythonLightweight, fast
CheckmarxEnterpriseComprehensive with IDE plugins

SAST in a GitHub Actions Pipeline

Here's a practical example using Semgrep in a GitHub Actions workflow:

# .github/workflows/sast.yml
name: SAST - Semgrep Scan

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

jobs:
  semgrep:
    name: Static Security Scan
    runs-on: ubuntu-latest
    container:
      image: semgrep/semgrep

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Run Semgrep
        run: |
          semgrep ci \
            --config=auto \
            --sarif \
            --output=semgrep-results.sarif
        env:
          SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}

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

This uploads findings directly to GitHub's Security tab in SARIF format, so your team sees vulnerabilities inline with pull requests.


🌐 DAST — Dynamic Application Security Testing

DAST tools test your running application by sending real HTTP requests and analyzing responses. Unlike SAST, DAST doesn't care about your source code — it attacks the application the same way a malicious user would.

When it runs: Against a staging or ephemeral environment after deployment, but before production.

What it catches:

  • Authentication and session management flaws
  • Broken access control
  • Server misconfigurations
  • Runtime injection vulnerabilities
  • Security headers missing or misconfigured

Popular DAST tools:

ToolTypeNotes
OWASP ZAPOpen sourceIndustry standard, CI-friendly
Burp SuiteCommercialBest-in-class for manual + automated
NucleiOpen sourceTemplate-based, blazing fast
StackHawkSaaSDeveloper-friendly, CI-native

DAST with OWASP ZAP in CI/CD

# .github/workflows/dast.yml
name: DAST - OWASP ZAP Scan

on:
  push:
    branches: [main]

jobs:
  zap-scan:
    name: Dynamic Security Scan
    runs-on: ubuntu-latest

    services:
      app:
        image: your-org/your-app:latest
        ports:
          - 3000:3000

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Wait for app to be ready
        run: |
          timeout 60 bash -c 'until curl -s http://localhost:3000/health; do sleep 2; done'

      - name: Run ZAP Baseline Scan
        uses: zaproxy/action-baseline@v0.12.0
        with:
          target: "http://localhost:3000"
          rules_file_name: ".zap/rules.tsv"
          cmd_options: "-a"
          fail_action: true

      - name: Upload ZAP Report
        uses: actions/upload-artifact@v4
        with:
          name: zap-report
          path: report_html.html
        if: always()

Tip: Use a .zap/rules.tsv file to tune which alerts fail the build versus warn. Not every finding should be a pipeline blocker — context matters.


📦 SBOMs — Software Bill of Materials

An SBOM is a formal, machine-readable inventory of every component in your software: open-source libraries, their versions, licenses, and known vulnerabilities.

Think of it like a nutritional label for your software. You need to know what's in the package — especially when a new CVE drops and you have 10 minutes to answer "are we affected?"

In 2025, SBOMs became a regulatory requirement for software sold to the U.S. federal government (via Executive Order 14028) and are increasingly mandated in the EU under the Cyber Resilience Act.

Common SBOM formats:

  • SPDX — Linux Foundation standard, widely adopted
  • CycloneDX — OWASP-led, richer security metadata, gaining ground fast

Popular SBOM tools:

ToolFormatNotes
SyftSPDX, CycloneDXMulti-ecosystem, fast
cdxgenCycloneDXDeep language support
TrivySPDX, CycloneDXVulnerability scanning + SBOM
grypeVuln scanning from Syft SBOMs

Generating and Scanning an SBOM in CI

# .github/workflows/sbom.yml
name: SBOM Generation and Vulnerability Scan

on:
  push:
    branches: [main]
  release:
    types: [published]

jobs:
  sbom:
    name: Generate SBOM & Scan
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install Syft
        uses: anchore/sbom-action/download-syft@v0

      - name: Generate SBOM
        uses: anchore/sbom-action@v0
        with:
          format: cyclonedx-json
          output-file: sbom.cyclonedx.json
          artifact-name: sbom-cyclonedx

      - name: Scan SBOM for Vulnerabilities with Grype
        uses: anchore/scan-action@v3
        with:
          sbom: sbom.cyclonedx.json
          fail-build: true
          severity-cutoff: high

      - name: Attach SBOM to Release
        uses: softprops/action-gh-release@v2
        if: github.event_name == 'release'
        with:
          files: sbom.cyclonedx.json

This workflow generates a CycloneDX SBOM, scans it for known CVEs using Grype, fails the build on HIGH or CRITICAL findings, and attaches the SBOM to GitHub releases automatically.


Putting It All Together: A Complete DevSecOps Pipeline

Here's how all three layers fit into a unified pipeline:

┌─────────────────────────────────────────────────────────────────┐
│                     CI/CD SECURE PIPELINE                       │
├────────────┬──────────────┬─────────────────┬───────────────────┤
│  PRE-COMMIT│    BUILD     │   STAGING/TEST  │    RELEASE        │
│            │              │                 │                   │
│ • Secrets  │ • SAST scan  │ • DAST scan     │ • SBOM attached   │
│   scanning │   (Semgrep,  │   (ZAP, Nuclei) │   to artifact     │
│   (gitleaks│   CodeQL)    │                 │                   │
│   trufflehg│ • Dependency │ • API security  │ • Sign artifact   │
│   )        │   audit      │   testing       │   (Sigstore/      │
│            │ • SBOM gen   │                 │   cosign)         │
│ • Lint &   │   (Syft)     │ • Container     │                   │
│   SAST     │              │   scanning      │ • Publish SBOM    │
│   (fast)   │ • License    │                 │   to registry     │
│            │   check      │                 │                   │
└────────────┴──────────────┴─────────────────┴───────────────────┘

Secrets Scanning: The Pre-Commit Layer

Before SAST even runs, stop secrets from entering your codebase with gitleaks or trufflehog:

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.21.1
    hooks:
      - id: gitleaks
        name: Detect hardcoded secrets

Install it locally with:

pip install pre-commit
pre-commit install

Now every git commit runs the secret scan before it even reaches your remote.


Real-World Example: Securing a Node.js API Pipeline

Let's say you have a Node.js REST API. Here's a complete, annotated pipeline covering all layers:

# .github/workflows/secure-pipeline.yml
name: Secure CI Pipeline

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

env:
  NODE_VERSION: "22"
  IMAGE_NAME: my-api

jobs:
  # ── Layer 1: SAST ────────────────────────────────
  sast:
    name: Static Analysis
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: npm

      - name: Install dependencies
        run: npm ci

      - name: npm audit (dependency vulnerabilities)
        run: npm audit --audit-level=high

      - name: Semgrep SAST scan
        uses: semgrep/semgrep-action@v1
        with:
          config: >-
            p/nodejs
            p/secrets
            p/owasp-top-ten
        env:
          SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}

  # ── Layer 2: Build & SBOM ────────────────────────
  build:
    name: Build & Generate SBOM
    runs-on: ubuntu-latest
    needs: sast
    outputs:
      image-digest: ${{ steps.build.outputs.digest }}
    steps:
      - uses: actions/checkout@v4

      - name: Build Docker image
        id: build
        uses: docker/build-push-action@v6
        with:
          context: .
          push: false
          tags: ${{ env.IMAGE_NAME }}:${{ github.sha }}
          outputs: type=docker,dest=/tmp/image.tar

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

      - name: Scan SBOM for CVEs
        uses: anchore/scan-action@v3
        with:
          sbom: sbom.json
          fail-build: true
          severity-cutoff: high

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

  # ── Layer 3: DAST ────────────────────────────────
  dast:
    name: Dynamic Analysis
    runs-on: ubuntu-latest
    needs: build
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4

      - name: Deploy to ephemeral staging
        run: |
          # Pull and run the built image (or deploy to staging env)
          docker load < /tmp/image.tar
          docker run -d -p 3000:3000 \
            --name staging-app \
            ${{ env.IMAGE_NAME }}:${{ github.sha }}
          sleep 5  # Wait for startup

      - name: Run ZAP API scan
        uses: zaproxy/action-api-scan@v0.7.0
        with:
          target: "http://localhost:3000"
          format: openapi
          api_scan_file: openapi.yaml
          fail_action: true
          cmd_options: "-l WARN"

      - name: Cleanup
        run: docker stop staging-app && docker rm staging-app
        if: always()

Best Practices

✅ Do These

  • Fail fast, fail loud. Block PRs on CRITICAL and HIGH findings. Warn on MEDIUM. Log LOW.
  • Tune your tools. A scanner that generates 500 false positives will be ignored. Invest time in baseline rules.
  • Treat security findings like bugs. Create tickets, assign owners, set SLAs. Don't let findings rot.
  • Sign your artifacts. Use Sigstore/cosign to cryptographically sign container images and SBOMs.
  • Store SBOMs with every release. They're useless if you generate them but don't keep them.
  • Use ephemeral environments for DAST. Never run active scanners against production.
  • Rotate secrets regularly and use a secrets manager (Vault, AWS Secrets Manager, 1Password Secrets Automation) — never environment variables in code.

❌ Common Mistakes

  • Running DAST against production. Scanners generate real traffic. They can trigger alerts, corrupt data, or — in worst cases — cause outages. Always target a staging environment.
  • Ignoring license compliance. SBOMs reveal GPL-licensed dependencies you may not be allowed to ship commercially. Check licenses automatically with tools like FOSSA or license-checker.
  • Only scanning on main. By the time code reaches main, it's already been reviewed. Scan on every PR and every commit.
  • Not updating scanner rule sets. SAST tools are only as good as their rules. Keep them updated — new vulnerability patterns emerge constantly.
  • Treating DevSecOps as a tooling problem. Tools are 30% of the solution. The other 70% is culture: developers owning security, security teams enabling developers, and leadership measuring security outcomes.
  • Skipping developer education. A scanner that flags a SQL injection is useless if the developer doesn't understand why it's dangerous or how to fix it properly.

🚀 Pro Tips

💡 Use policy-as-code for consistent enforcement. Tools like Open Policy Agent (OPA) or Kyverno let you define security policies declaratively and enforce them consistently across pipelines and Kubernetes clusters.

💡 Correlate SAST + DAST findings. A vulnerability flagged by both SAST and DAST is almost certainly real. Build a process to prioritize correlated findings — they're your highest-confidence, highest-priority items.

💡 Implement a "break glass" process. Sometimes you need to deploy despite a finding (zero-day patch, critical business need). Implement a documented override process with required approvals and automatic ticket creation — so bypasses are visible, not invisible.

💡 Generate SBOMs at build time, not post-hoc. Generating SBOMs from source is more accurate than scanning a deployed artifact. Use tools like cdxgen with your build system for language-native dependency resolution.

💡 Leverage GitHub Advanced Security or GitLab Ultimate. Both platforms have native SAST, secret scanning, and dependency review built in. If you're already on these platforms, enable them first before adding external tools.

💡 Track your Mean Time to Remediate (MTTR) for security findings. Security is measurable. Tracking MTTR across severity levels tells you whether your DevSecOps program is actually improving outcomes — or just generating reports.


📌 Key Takeaways

  • Shifting left means embedding security at every stage of the SDLC, starting from the developer's machine — not just before release.

  • SAST analyzes your source code without running it, catching issues like injection flaws, insecure crypto, and hardcoded secrets early in development.

  • DAST tests your running application the way an attacker would, finding runtime vulnerabilities that static analysis can't see.

  • SBOMs give you a complete, auditable inventory of your software's components — essential for vulnerability response, license compliance, and regulatory requirements.

  • All three are complementary. SAST + DAST + SBOM together provide defense-in-depth across the pipeline. No single tool catches everything.

  • Culture beats tooling. DevSecOps succeeds when security becomes a shared responsibility, not a gated function owned by a separate team.

  • Start small, iterate fast. Don't try to implement everything at once. Add SAST to PRs first, then SBOMs to builds, then DAST to staging. Incrementally raise the bar.


Conclusion

The shift from "security at the end" to "security everywhere" isn't just a technical change — it's a cultural one. But the technical foundation matters enormously. SAST gives you early feedback in the IDE and on every commit. DAST gives you real-world attack simulation against running code. SBOMs give you transparency and traceability across your entire dependency graph.

Together, they form a layered security posture that catches different classes of vulnerabilities at different stages — while keeping your pipeline fast enough that developers actually embrace it rather than route around it.

The best security program is one developers trust, understand, and contribute to. Start there.

All Articles
DevSecOpsSecuritySASTDASTSBOMCI/CDPipelineAppSecDevOpsSoftware Security

Written by

Niraj Kumar

Software Developer — building scalable systems for businesses.