Back to Blog
Security16 min readJun 2026

DevSecOps: SAST, DAST, SCA, and Shifting Security Left

Security scanners are easy to bolt onto a pipeline and easy to make everyone hate. Here is what SAST, DAST, SCA, IAST, and secret scanning each catch, where they belong in CI/CD, and how to gate a PR without burying your team in false positives.

DevSecOpsSASTDASTSCA
SB

Sri Balaji

Founder

On this page

The pen-test-at-the-end problem

Here is how security used to work: you build for six months, then a security team runs a scan the week before launch and hands you a 200-page PDF. Half the findings are real, half are noise, and all of them are now urgent. You ship late, or you ship with known holes. Shifting security left means moving those checks earlier, onto the commit, onto the pull request, so problems surface when they are cheap to fix instead of when they are a release blocker.

Who this is for

You write code, you have a CI/CD pipeline (probably GitHub Actions), and someone said 'we need to do DevSecOps' without telling you what to actually install. You want to know which scanners do what, where each one runs, and how to turn them on without your team revolting over false positives.

The trap most teams fall into is treating 'security scanning' as one thing. It is not. There are five distinct tools here, each looking at your software through a different lens, each blind to what the others catch. Turn them all on blindly and you drown. Understand what each sees and you build a layered net with very few gaps.

Layers, like airport security

DevSecOps is not a tool you buy. It is the practice of making security a property of your pipeline instead of a gate at the end of it.

No single airport check catches everything, and nobody pretends one does. Each layer looks for a different class of threat, and the security comes from stacking them. Application security scanning works the same way.

ID check at the door, are you even allowed to be here?Secret scanning: did a credential leak into the code that grants access it should not?
Bag X-ray, looking at the contents without opening themSAST: reads your source code statically, without running it, for dangerous patterns.
Checking where each bag was packed and by whomSCA: traces every third-party dependency and its known CVEs.
Behavioral screening, watching how a person actually actsDAST: pokes the running application from the outside, like a real attacker.
An air marshal on the plane, watching during the flightIAST: instruments the app from inside while it runs and tests exercise it.
Each scanner is one checkpoint. The coverage comes from layering them, not from any single one being perfect.

Where each scanner runs in the pipeline

Timing is the whole game. A scanner that needs running code cannot run on a raw commit, and a scanner that only reads source cannot tell you how the deployed app behaves. Map each tool to the moment it has the information it needs.

scan diffopen PRpass gateemitpush imageno crit CVEprobepromote
Developer

git commit

Secret scan

gitleaks (pre-commit)

PR scan

SAST + SCA

Build

compile + package

SBOM

syft

Image scan

trivy / grype

Deploy

staging

DAST

ZAP (runtime)

Production

promote

A secure CI/CD pipeline. The happy path runs left to right; each scanner hooks in at the stage where it has the data it needs. Dashed lines are artifacts and async signals.

  1. 1

    Commit, secret scan

    gitleaks runs on the diff (ideally a pre-commit hook plus a CI check). Catches an AWS key or token before it ever lands in history, where rotation becomes the only fix.

  2. 2

    Pull request, SAST + SCA

    On every PR, run static analysis on the changed code and a dependency scan on the lockfile. This is the cheapest place to block a problem because the author is right there with context.

  3. 3

    Build, generate the SBOM

    Once dependencies resolve, emit a software bill of materials. It is the manifest you will query later when the next Log4Shell drops.

  4. 4

    Image, scan the artifact

    Scan the built container for OS-package CVEs and bad config. Your code can be clean while the base image ships a vulnerable libc.

  5. 5

    Deploy to staging, DAST

    With a running instance, fire dynamic probes at it: injection, auth bypass, misconfigured headers. This is the only layer that sees the app the way an attacker does.

  6. 6

    Promote, gated on results

    Production only gets what cleared every prior gate at the severity threshold you set.

SAST vs DAST vs SCA vs IAST

The four big scanner families differ on one axis above all: do they read your code, or do they watch it run? That single distinction decides what they can possibly find and how noisy they tend to be.

ScannerWhen it runsWhat it findsBlind to / false positives
SASTCommit / PR (no run needed)Code-level flaws: SQLi patterns, hardcoded crypto, unsafe deserialization, taint flowsHigh false positives; cannot see runtime config or whether a path is reachable
SCAPR / build (reads lockfile)Known CVEs in your dependencies and their transitive tree; license riskOnly finds published CVEs; a flagged dep may be a function you never call
DASTAfter deploy (needs running app)Real runtime bugs: injection, broken auth, missing headers, exposed endpointsNo source context, so it cannot point at the line; misses unreached code paths
IASTDuring tests on a running appRuntime flaws with a code location, best of both, low false positivesNeeds an agent in the runtime and good test coverage; language support is limited
The four families. Secret scanning is a fifth, narrower tool, covered below.

The mental shortcut

SAST and SCA are build-time and read code/manifests, fast, early, noisier. DAST and IAST are runtime and watch behavior, later, slower, more accurate but blind to code you never exercise. You want both halves; neither replaces the other.

The build-time vs runtime trade-off

Build-time scanners (SAST, SCA, secret scanning) are cheap, run in seconds to minutes, and fire on every PR, so you fix things while the code is fresh. Their weakness is that they reason about code in the abstract. SAST will flag a SQL concatenation it cannot prove is reachable; SCA will flag a CVE in a library function you never import. That gap between 'theoretically dangerous' and 'actually exploitable' is where false positives live.

Runtime scanners (DAST, IAST) have the opposite profile. Because they exercise a real instance, a finding is almost always real, there is much less to argue about. But they are slow, they need a deployed environment, and they are blind to any path your tests or crawler never hit. A DAST scan that never logs in will never test your authenticated endpoints.

  • Want fast feedback and broad coverage? Lean on build-time SAST/SCA at the PR gate.
  • Want high-confidence, low-noise findings? Lean on runtime DAST/IAST in staging.
  • Want both without the wait? Run build-time on every PR (blocking), runtime nightly or pre-release (reporting, then blocking once tuned).

A real GitHub Actions pipeline

Here is a working workflow that wires in four layers: Semgrep for SAST, Trivy for SCA and image scanning, gitleaks for secrets, and Syft for the SBOM. It runs on every pull request. Note the GitHub Actions expression syntax and how each job is scoped to one concern.

.github/workflows/security.yml
yaml
name: security

on:
  pull_request:
  push:
    branches: [main]

permissions:
  contents: read
  security-events: write   # lets us upload SARIF to the Security tab

jobs:
  secrets:
    name: Secret scan (gitleaks)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0     # gitleaks needs full history to scan the diff
      - uses: gitleaks/gitleaks-action@v2
        env:
          GITLEAKS_VERSION: latest

  sast:
    name: SAST (semgrep)
    runs-on: ubuntu-latest
    container: semgrep/semgrep
    steps:
      - uses: actions/checkout@v4
      - name: Run semgrep
        run: |
          semgrep ci \
            --config=p/default \
            --config=p/owasp-top-ten \
            --sarif --output=semgrep.sarif
        env:
          SEMGREP_RULES: p/ci
      - name: Upload findings
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: semgrep.sarif

  sca:
    name: SCA (trivy fs)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Scan dependencies
        uses: aquasecurity/trivy-action@0.24.0
        with:
          scan-type: fs
          scanners: vuln
          severity: HIGH,CRITICAL     # only these fail the build
          exit-code: '1'
          ignore-unfixed: true        # skip CVEs with no patch yet

  image:
    name: Build + image scan + SBOM
    runs-on: ubuntu-latest
    needs: [sast, sca, secrets]
    steps:
      - uses: actions/checkout@v4
      - name: Build image
        run: docker build -t app:${{ github.sha }} .
      - name: Scan image (trivy)
        uses: aquasecurity/trivy-action@0.24.0
        with:
          image-ref: app:${{ github.sha }}
          severity: HIGH,CRITICAL
          exit-code: '1'
          ignore-unfixed: true
      - name: Generate SBOM (syft)
        uses: anchore/sbom-action@v0
        with:
          image: app:${{ github.sha }}
          format: cyclonedx-json
          output-file: sbom.cdx.json
      - name: Publish SBOM artifact
        uses: actions/upload-artifact@v4
        with:
          name: sbom
          path: sbom.cdx.json

The image job declares needs: [sast, sca, secrets], so the build only happens after the cheap source-level checks pass, fail fast, fail cheap. Findings upload as SARIF so they show up inline on the PR and in the repo Security tab, not just buried in log output.

Fail only on high and critical

Notice `severity: HIGH,CRITICAL` and `ignore-unfixed: true` on every scanner. This is the single most important tuning decision you will make. Block the build on HIGH/CRITICAL with an available fix; report everything else without failing. If you fail on every LOW and MEDIUM, developers learn to ignore the red X within a week, and then the gate is worthless even for the criticals.

SBOMs: knowing what you shipped

A software bill of materials is an inventory of every component in your build, direct and transitive dependencies, versions, licenses, hashes. The syft step above emits one in CycloneDX format. It feels like bureaucracy until the next zero-day lands.

When Log4Shell hit, the teams that survived the night were the ones who could answer 'are we affected, and where?' in minutes by grepping their SBOMs, instead of spending days manually auditing every service. The SBOM is what turns 'a critical CVE was just published in libfoo' from a fire drill into a query.

query the sbom
bash
# Generate an SBOM from a local image
syft app:latest -o cyclonedx-json > sbom.cdx.json

# Later: is log4j-core anywhere in what we shipped?
grep -i 'log4j-core' sbom.cdx.json

# Re-scan a stored SBOM against today's CVE feed, 
# no rebuild needed, just the manifest
grype sbom:./sbom.cdx.json --fail-on critical

Store SBOMs as release artifacts

Attach the SBOM to every release and image. Then you can answer 'were we vulnerable in v2.3.1?' for a version that shipped months ago, without rebuilding it. Scanning a stored SBOM against a fresh CVE feed is how you find out you are exposed before an attacker does.

Secret scanning: the cheapest, highest-ROI check

Of the five, secret scanning has the best effort-to-payoff ratio. A single leaked AWS key in git history can cost you a five-figure crypto-mining bill overnight. gitleaks and trufflehog scan diffs (and full history) for the regex and entropy signatures of credentials.

Run it in two places: a pre-commit hook so the secret never leaves the laptop, and a CI check as a backstop for anyone who skips the hook. The critical thing to understand, once a secret is committed, deleting it in a later commit does not help. It is in history forever. The only real fix is to rotate the credential. Catching it before the push is the whole point.

For the deeper playbook on managing credentials inside pipelines themselves, see secrets in CI/CD pipelines.

Common mistakes that cost hours

  1. Failing the build on every severity. Block on HIGH/CRITICAL; report the rest. Day one of failing on LOW is the day developers stop reading the output.
  2. Not setting `ignore-unfixed`. Failing on CVEs with no available patch gives developers a red X they literally cannot fix. That is pure alert fatigue.
  3. Treating SAST as proof of safety. SAST cannot see runtime config, secrets injection, or whether a flagged path is even reachable. Clean SAST is not a clean app.
  4. Skipping image scanning because the code is clean. Your base image ships an OS with its own CVEs. Scan the artifact, not just the source.
  5. Running DAST without authenticating. An unauthenticated crawl never reaches your logged-in endpoints, the highest-value attack surface goes completely untested.
  6. Bolting on scanners with no triage owner. A scanner with nobody assigned to its findings is theater. Route results to the PR author and define who decides what gets waived.
  7. Ignoring the SBOM until a zero-day hits. Generate and store it on every build. The night Log4Shell drops is not the night to start.

Takeaways

The whole article in seven lines

  • Shift left: run checks on commit/PR where fixes are cheap, not in a pen-test at the end.
  • Five layers, five blind spots: secret scan, SAST, SCA (build-time) + DAST, IAST (runtime).
  • Build-time = fast, early, noisier. Runtime = slow, accurate, blind to unexercised paths. Use both.
  • Map each scanner to the stage where it has the data it needs (see the pipeline diagram).
  • Gate the build only on HIGH/CRITICAL with a fix available, or developers learn to ignore the red X.
  • Emit and store an SBOM on every build so the next zero-day is a query, not a fire drill.
  • Secret scanning is the cheapest, highest-ROI check; rotate, do not just delete, leaked credentials.

Where to go next

You have the layered model and a working pipeline. The next step is hands-on: tune the gates against a real project, then go deeper on the two layers that scanners only partially cover, your dependencies and your container images.

Want to go deeper?

This article covers concepts taught hands-on in the Cloud Engineer and DevOps career paths, with real terminal labs, production scenarios, and structured lessons.