SAST, DAST & SCA
No single scanner sees the whole application. Static analysis reads your code without running it, dynamic analysis attacks your app while it's running, and composition analysis checks the code you didn't write yourself. This page defines each category precisely, places each one at the pipeline stage where it actually earns its keep, and covers the triage problem that determines whether any of them survive contact with a real team.
Think of shipping a car. SAST is the inspector who reads the engineering blueprints before anything is built — they can spot a wrong-sized bolt or a missing weld on paper, fast and cheap, but they can't tell you how the engine actually sounds under load. DAST is the test driver who takes the finished, assembled car out on a track and tries to break it — brakes, steering, crash behavior — things you only find by actually driving it. SCA is the parts inspector who checks every component you bought from a supplier instead of machined yourself, because a recalled brake pad from a vendor is just as dangerous as a defect you built by hand — and in a modern car, most of the parts came from vendors.
SAST: reading the code without running it
Static Application Security Testing analyzes source code, bytecode, or binaries without executing them. A SAST engine builds an abstract syntax tree or control-flow graph from your code and pattern-matches against known-dangerous constructs: string concatenation flowing into a SQL query, unsanitized input reaching an eval() call, a hardcoded cryptographic key, a path that lets user input reach a shell command. Tools like Semgrep, CodeQL, SonarQube, and Checkmarx all work this way, differing mainly in how deep their data-flow analysis goes and how much of a real interprocedural taint trace they can follow versus a shallow single-file pattern match.
The defining advantage is timing. Because SAST needs nothing more than the source tree, it can run in an editor as you type, as a pre-commit hook, or as a required check on every pull request in CI/CD — the same shift-left principle covered on what DevSecOps actually changes. A vulnerability caught at PR time costs a few minutes of the author's attention; the same class of bug caught in production after an incident costs an on-call rotation and a postmortem. IBM's Systems Sciences Institute research on defect cost, cited across the industry for two decades, put the ratio at roughly 6x more expensive to fix a defect found in testing than in design, and orders of magnitude more once it's in production — SAST is the tool built specifically to exploit that curve by catching the bug before it's ever committed.
The tradeoff is exactly what you'd expect from analyzing code in isolation: SAST cannot see how your application behaves once it's deployed behind a real reverse proxy, talking to a real database with real permissions, under a real authentication flow. It also cannot reason about business logic — a SAST tool will not flag "this endpoint lets a normal user cancel someone else's order" because nothing about that pattern looks syntactically wrong. That gap is exactly what dynamic testing is for.
DAST: attacking a running instance from the outside
Dynamic Application Security Testing takes the opposite approach: it treats the application as a black box and attacks it the way an external adversary would, with no access to source code. A DAST scanner — OWASP ZAP and Burp Suite's automated scanner are the two you'll see most — crawls the running application's routes, then throws malformed and malicious input at every form field, header, and parameter it finds: SQL injection payloads, XSS strings, path traversal sequences, malformed authentication tokens. It watches the actual HTTP responses, status codes, and rendered output for evidence the attack worked.
Because it needs a live, deployed target, DAST typically runs later in the pipeline than SAST — against a staging or pre-production environment after a build has already passed unit tests and SAST gates, not on every commit. This is also where DAST earns its keep: it catches an entire category of runtime and configuration issues that are invisible to source-code analysis, because they don't exist in the source code at all. Missing security headers (no Content-Security-Policy, no X-Frame-Options), a misconfigured TLS setup, a session cookie missing the Secure or HttpOnly flag, an authentication bypass that only shows up when two middleware layers interact at runtime — none of these are visible by reading any single file, but all of them are visible the moment you actually attack the running system.
The cost of that realism is speed and precision. A DAST scan against a nontrivial application can take anywhere from twenty minutes to several hours, versus SAST's seconds-to-minutes on a diff, and a DAST finding tells you a symptom ("this endpoint returned a 500 with a stack trace for this payload") rather than a source location — someone still has to trace the response back to the vulnerable line of code.
SCA: the code you didn't write is still your code
Software Composition Analysis scans your project's third-party and open-source dependencies — everything pulled in via package.json, requirements.txt, go.mod, a container base image, and similar manifests — against databases of known vulnerabilities, principally the CVE database and the broader National Vulnerability Database, cross-referenced through machine-readable feeds like OSV. Tools in this category include Snyk, Dependabot, OWASP Dependency-Check, and Trivy (which extends into container and IaC scanning covered on container and supply chain security).
SCA earns a category of its own, distinct from SAST, because of a simple fact about how modern software is actually built: a typical production codebase is majority third-party code. Industry composition studies (Synopsys's annual OSSRA report is the most-cited) have consistently found that 70-90%+ of the code in a typical commercial application by line count comes from open-source dependencies, not code the team wrote. A SAST scanner analyzing your own source files is, by construction, only looking at a minority of what actually ships. SCA closes that gap by asking a narrower but different question than "is this code written well" — it asks "is this exact version of this exact library on a known-vulnerable list," and increasingly also builds and checks a Software Bill of Materials (SBOM) so the org can answer "are we affected by CVE-2024-XXXXX" in minutes rather than days when the next Log4Shell-scale disclosure lands.
SCA's practical failure mode is different from SAST's or DAST's: a CVE match doesn't tell you whether the vulnerable code path in that dependency is actually reachable from your application, so a naive SCA setup can report "critical" on a library whose vulnerable function you never call. More mature tools attempt reachability analysis to filter for exactly this, but plenty of SCA output in the wild is still severity-ranked by the dependency's CVSS score alone, with no attempt to correlate that against your own call graph.
# .ci/pipeline.yml — three scan categories at three different stages
stages:
- build
- test
- scan
- deploy-staging
- dast
- deploy-prod
sast-scan:
stage: scan
# runs on every commit/PR — needs only the source tree, no running app
script:
- semgrep ci --config=auto --error
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
dependency-scan:
stage: scan
# runs alongside SAST — needs only the manifest/lockfile, no running app
script:
- trivy fs --scanners vuln --exit-code 1 --severity CRITICAL,HIGH .
artifacts:
reports:
dependency_scanning: sbom.json
dast-scan:
stage: dast
# runs after deploy-staging — needs a live, reachable target
needs: ["deploy-staging"]
script:
- zap-baseline.py -t https://staging.internal.example.com -r zap-report.html
artifacts:
paths: [zap-report.html]All three categories share the same failure mode if you don't manage it deliberately: alert fatigue from false positives. A SAST rule set tuned for "flag anything that could theoretically be an issue" buries the two real SQL injection findings under two hundred style-pattern matches nobody reads. A DAST scan against an app with verbose error pages generates dozens of "possible information disclosure" findings that are really just stack traces already scrubbed in production. An SCA scan without reachability analysis flags every CVE in every transitive dependency regardless of whether your code path ever calls the vulnerable function. Teams that don't actively tune severity thresholds, suppress confirmed false positives with a documented reason, and periodically re-baseline their rule sets end up with engineers reflexively clicking past security gates — which is a worse security posture than having no scanner at all, because it trains people to ignore the tool right before the one finding that mattered.
Putting the three together in one pipeline
The three categories are complementary, not redundant, precisely because each one's blind spot is a different one's strength. SAST is fast and early but code-only and blind to runtime behavior. DAST is realistic and catches configuration and runtime issues but is slow and gives you a symptom, not a source line. SCA covers the majority-share of your codebase that SAST never touches — the dependencies — but needs reachability analysis to avoid drowning teams in irrelevant CVEs. A mature pipeline runs SAST and SCA together at PR time, gated as required checks before merge, and runs DAST later against a deployed staging environment, gated before promotion to production — matching each tool's cost and realism to how early or late it can practically run, which is also why the ordering in the YAML above puts sast-scan and dependency-scan in the same early stage and dast-scan after a staging deploy exists to attack. None of the three replaces threat modeling — they find implementation and dependency flaws in things you built or pulled in, not design-level gaps in what you decided to build in the first place.
SAST, DAST, and SCA answer three different questions — "is my code written safely," "does my running app behave safely," and "are my dependencies known-vulnerable" — and a pipeline missing any one of them has a permanent blind spot no amount of tuning the other two will close.
1. Why does SAST typically run earlier in the pipeline than DAST? 2. Name one class of vulnerability DAST can catch that SAST structurally cannot, and explain why. 3. Why does SCA matter as much as it does for a typical modern codebase? 4. What's the risk of an SCA scanner that reports CVE matches without reachability analysis?
Check your answers
- SAST only needs the source tree, so it can run on every commit or pull request before anything is deployed; DAST needs a live, running instance of the application, so it can only run after a build has been deployed somewhere (typically staging), which naturally places it later.
- Runtime/configuration issues such as missing security headers, a misconfigured TLS setup, or a session cookie missing the Secure/HttpOnly flag — none of these exist as a pattern in the source code itself, so a tool that only reads code has nothing to match against; they only become visible when the application is actually running and you inspect its real behavior.
- Because a typical production codebase is majority third-party/open-source code by line count (commonly cited at 70-90%+), so a scanner that only analyzes code the team wrote is, by construction, missing most of what actually ships.
- It generates a high volume of findings for CVEs in code paths the application never actually calls, which buries the genuinely exploitable findings under noise and trains engineers to stop reading — the same false-positive fatigue problem that undermines SAST and DAST when severity isn't tuned.