Dev

SAST vs DAST vs IAST vs SCA: Which Security Test Finds What

A security scanner only finds what it can see, and the four families of application security testing each look at a different artifact. That is the whole of the SAST vs DAST vs IAST vs SCA question. Static analysis (SAST) reads your source code, software composition analysis (SCA) reads your dependency list, dynamic testing (DAST) attacks the running app over HTTP, and interactive testing (IAST) watches from inside the runtime while something else sends the requests. Pick one and you inherit its blind spots.

Original content from computingforgeeks.com - post 180856

To see how far apart they land, we pointed one open-source tool of each kind at the same deliberately vulnerable app, OWASP Juice Shop: Semgrep for SAST, Trivy and OSV-Scanner for SCA, and OWASP ZAP for DAST. IAST has no maintained open-source tool we could run, so its section explains the mechanism without a lab result. Every count and output below comes from those scans, run against Juice Shop v20.3.0 on an Ubuntu 26.04 VM in October 2026, and every outside number links to its report in the Sources section.

SAST vs DAST vs IAST vs SCA: what each test reads

Each test’s input decides its output. A tool that parses source code can point at the exact line where user input reaches a SQL query. It has no idea which version of a library the build pulled in, or how the server answers a malformed request. A tool that sends HTTP requests sees the response and nothing behind it. The diagram below puts each test next to the artifact it reads and the moment in the pipeline where that artifact exists.

Diagram of SAST, SCA, DAST and IAST showing which artifact each application security test reads: source code, dependencies, or the running app

Two of the tests run at commit time and need nothing deployed. The other two need a running build, which is why they usually live in a staging environment and run later in the pipeline:

  • SAST: parses the code into a syntax tree and follows data from a source (a request field) to a sink (a query, a file read, an eval), without executing anything.
  • SCA: builds an inventory of third-party packages and their versions, then matches each one against advisory databases such as the CVE list and GitHub advisories.
  • DAST: crawls the running app and sends crafted requests, then judges each response for signs of injection, leaks or missing protections.
  • IAST: loads an agent into the app’s runtime, so when tests or a DAST crawler exercise a route, it sees both the incoming request and the dangerous call it reaches.

This matters because the categories of the OWASP Top 10:2025 sort cleanly onto those inputs. Software Supply Chain Failures (A03) is SCA territory, Security Misconfiguration (A02) shows up mostly in responses that DAST reads, and Injection (A05) can be caught from either side, as the lab shows. No single input covers all three.

How SAST finds a flaw without running the code

SAST works on the text of your program, so it catches a flaw the moment the vulnerable line is written, long before anything is deployed. We cloned the Juice Shop source at its release tag and ran Semgrep’s default ruleset over it in a container. The scan took 50 seconds on 4 vCPUs, ran 408 rules over 1,191 files and needed no running app at all:

docker run --rm -v ~/lab/juice-shop:/src -v ~/lab:/out semgrep/semgrep semgrep scan --config p/default --metrics=off --json -o /out/semgrep.json /src

Of the 68 findings, the one worth reading first is the login query. Semgrep traced req.body.email straight into a raw Sequelize query and printed the line with its rule ID, which is the kind of answer only SAST gives: a file, a line number and the fix pattern:

    /src/routes/login.ts
   ❯❯❱ javascript.sequelize.security.audit.sequelize-injection-express.express-sequelize-injection
          ❰❰ Blocking ❱❱
          Detected a sequelize statement that is tainted by user-input. This could lead to SQL injection if
          the variable is user-controlled and is not properly sanitized. In order to prevent SQL injection, it
          is recommended to use parameterized queries or prepared statements.
          Details: https://sg.run/gjoe

           34┆ models.sequelize.query(`SELECT * FROM Users WHERE email = '${req.body.email || ''}' AND
               password = '${security.hash(req.body.password || '')}' AND deletedAt IS NULL`, { model:
               UserModel, plain: true }) // vuln-code-snippet vuln-line loginAdminChallenge
               loginBenderChallenge loginJimChallenge

An identical rule fired on the product search route, and other rules caught the open redirect in routes/redirect.ts and the res.sendFile calls behind the file-serving routes. That is four of the seven flaws we tracked. It only partly caught the hardcoded keys: two HMAC secrets and a JWT secret in lib/insecurity.ts were flagged, while the RSA private key pasted inline at line 21 of the same file was not.

Misses teach as much as hits. Semgrep’s free ruleset said nothing about vm.runInContext in the B2B order route, which is the app’s remote code execution flaw. It was also silent on the nine bypassSecurityTrust calls in the Angular frontend that make its XSS challenges work. Pattern rules only know the sinks someone wrote a rule for. Noise was the other cost: 15 findings sat in data/static/codefixes, Juice Shop’s library of intentionally broken snippets. Another 23 came from GitHub Actions workflows, Terraform files and .npmrc, which left 30 in the application code and its tests. Triage time on that ratio is where teams give up on SAST, and our piece on keeping SAST scans from feeding alert fatigue covers the tuning side.

How SCA matches your dependencies against advisories

SCA never looks at your logic. It answers one question: which packages and versions are in this build, and which of them have a published advisory. That makes it the cheapest test to run and the one with the highest hit rate on a typical codebase, because most of the code you ship was written by someone else. We scanned the published Juice Shop image with Trivy, which inventories the node_modules tree inside the container layers:

trivy image --scanners vuln --pkg-types library --format json -o trivy.json bkimminich/juice-shop:v20.3.0

Trivy finished in 22 seconds, about 15 of them spent downloading its vulnerability database, and reported 82 findings (75 unique advisory IDs) across 31 package versions: 11 critical, 32 high, 36 medium and 3 low. The deliberately old packages Juice Shop pins for its challenges all showed up, including jsonwebtoken 0.1.0 and 0.4.0 with CVE-2015-9235, express-jwt 0.1.3 and sanitize-html 1.4.2. Neither Semgrep nor ZAP flagged any of them, because a vulnerable library version is not a pattern in your code or a symptom in a response.

OSV-Scanner reads lockfiles rather than images. Juice Shop’s repository ships without one, so we generated package-lock.json from package.json first, inside a Node 22 container. It took five minutes, and --legacy-peer-deps was needed for npm to resolve the tree:

docker run --rm -v $PWD:/app -w /app node:22 npm install --package-lock-only --ignore-scripts --legacy-peer-deps

With the lockfile in place, run the scanner against it:

osv-scanner scan source -L juice-shop/package-lock.json --format json --output-file osv.json

The scan itself took 3.9 seconds, and its summary counts packages rather than findings:

Scanned /home/gedion/lab/juice-shop/package-lock.json file and found 1181 packages
Total 28 packages affected by 70 known vulnerabilities (6 Critical, 26 High, 30 Medium, 2 Low, 6 Unknown) from 1 ecosystem.
63 vulnerabilities can be fixed.

Both SCA tools agreed on 25 vulnerable package versions. Six more appeared only in Trivy’s image scan and three only in OSV-Scanner’s lockfile scan, and that gap is not a quality verdict. A lockfile regenerated today resolves different transitive versions than the ones baked into the image, so the image is the truer record of what runs. When you compare SCA tools, compare them on the same artifact, and if you run a monorepo, the SCA tools that handle multi-language repositories differ mostly in how they find those artifacts.

Industry data tells the same story across hundreds of codebases. Black Duck’s 2026 OSSRA audit of 947 commercial codebases found open source in 98% of them and at least one open-source vulnerability in 87%. Veracode’s 2026 State of Software Security puts open-source vulnerabilities in 62% of the applications it scanned:

Bar chart of open source vulnerability share from Black Duck OSSRA 2026 and Veracode State of Software Security 2026 reports

Read the last two bars together. Veracode finds flaws in first-party code (63%) about as often as open-source vulnerabilities (62%), so SCA covers roughly half the problem, not all of it. One OSSRA number also limits what any SCA scanner can do: 16% of the open-source components its auditors found entered the codebase as copied snippets, vendored code or binaries, which manifest-based scanners like the two we ran cannot see.

How DAST attacks the running app from outside

DAST treats the app as a black box. It crawls the pages and API routes it can discover, sends payloads into every parameter, and decides from the response whether something broke, which means it needs a deployed build and finds problems only on routes it can reach. ZAP ships two packaged scans, and the difference between them was the biggest surprise of the lab. The baseline scan is passive: it crawled 116 URLs in about a minute and raised 9 alert types (2 Medium, 4 Low, 3 informational), mostly header and CORS issues, with no High alert at all.

The full scan adds the AJAX spider and the active attack phase. We gave it a five-minute spider budget and let it run from a directory the container’s zap user can write to, since the reports land in the mounted /zap/wrk:

docker run --rm --network host -v $(pwd):/zap/wrk:rw -t zaproxy/zap-stable zap-full-scan.py -t http://localhost:3000 -j -m 5 -T 45 -J zap-full.json -r zap-full.html -I

It ran for 55 minutes 48 seconds over 573 URLs and raised 22 alert types against Juice Shop: 3 High, 5 Medium, 7 Low and 7 informational. The High alerts are the ones that prove exploitability, because ZAP sent a quote character and watched the server fail. The report section for the SQL injection alert shows how a DAST finding reads:

OWASP ZAP full scan report showing a High risk SQL Injection alert on the Juice Shop search and login endpoints
  1. 1High risk SQL Injection alert
  2. 2Search endpoint
  3. 3500 error as evidence
  4. 4Login endpoint
  1. High risk SQL Injection alert: the alert name and the risk ZAP assigned after the active scan confirmed it.
  2. Search endpoint: the product search URL with the payload '( injected into the q parameter.
  3. 500 error as evidence: the server answered with HTTP 500, which is the symptom ZAP judged the injection by.
  4. Login endpoint: the second instance, a single quote in the email field of the login POST.

Compare that with the Semgrep output. ZAP proved the login injection is reachable and exploitable from the internet, but it cannot tell you that line 34 of routes/login.ts is the culprit. Semgrep named the line but could not prove anyone can reach it. The full scan also found what no other tool in the lab could: 31 backup files exposed under /ftp, a missing Content Security Policy, a missing anti-clickjacking header and the socket.io session ID in URLs. Semgrep did flag the serveIndex directory listing on /ftp in server.ts, but only ZAP showed which files that listing actually hands out.

ZAP missed the rest. It reported no XSS and no path traversal, even though Juice Shop has both. The DOM XSS sinks need a specific navigation the spider never performed, and the file routes need payloads it did not try. Two practical costs showed up too. The AJAX spider followed links out to github.com and scanned seven GitHub hosts we never asked about. Then the Juice Shop container exited with a segfault (exit code 139) 11 seconds before the scan finished, a fair warning about running an active scan anywhere near production. The trade-offs between an automated platform and a human tester are covered in how DAST platforms complement manual security testing.

Where IAST fits, and why we could not lab it

IAST closes the gap between the last two sections. An agent loaded into the runtime (a Java agent, a .NET profiler, a Node.js module) wraps the functions that matter, so when a request arrives it tags the input and follows it through the code. If that tagged value reaches a query builder or a file call unchanged, the agent reports the vulnerability with the request that triggered it and the stack trace down to the line. That is the ZAP evidence and the Semgrep line number in one finding, and it is why IAST vendors claim fewer false positives than SAST: the agent only reports paths that actually executed. Runtime detection does not stop at the app’s own process either, and once an exploit lands, our test of what an EDR agent sees on a Linux server shows the host-side evidence (processes, files, connections) that none of these four tests can reach.

IAST also has requirements the other tests do not:

  • A supported runtime: the agent has to understand the language and framework, so coverage follows the vendor’s support matrix.
  • Traffic: functional tests, QA sessions or a DAST crawl must exercise the code while the agent watches.
  • Permission to instrument: someone has to accept an extra agent inside the app process, with its memory and latency cost.

Code paths that no test touches stay invisible, just as they do for DAST. We found no actively maintained open-source IAST tool to put in the lab: Baidu’s OpenRASP has had no commit since October 2025 and is mainly a runtime protection project, and Contrast Security’s free tier is now a Java-only runtime agent in observation mode. So the lab chart below has no IAST bar, and we would rather leave it empty than draw one from a vendor brochure.

What the lab results say side by side

We tracked seven flaws that Juice Shop documents in its source, plus every vulnerable dependency, and recorded which tool caught each one. The chart puts the two halves next to each other, and the split is the point of the whole exercise:

Bar chart of SAST vs DAST vs SCA lab results on OWASP Juice Shop: known flaws caught by Semgrep and ZAP, vulnerable packages flagged by Trivy and OSV-Scanner

The table has the evidence behind each cell, with the rule or alert that fired:

Known flawSemgrep (SAST)ZAP full scan (DAST)Trivy, OSV-Scanner (SCA)
SQL injection, loginFound, routes/login.ts:34Found, POST /rest/user/login, emailNot in scope
SQL injection, searchFound, routes/search.ts:23Found, GET /rest/products/search, qNot in scope
Hardcoded keysPartly: HMAC and JWT secrets, not the inline RSA keyNot foundNot in scope
vm.runInContext code executionNot foundNot foundNot in scope
DOM XSS sinksNot foundNot foundNot in scope
Open redirectFound, routes/redirect.ts:18Found, /redirect?to=Not in scope
File-serving path traversalFound as res.sendFile in 4 routesNot foundNot in scope
Vulnerable npm packages0031 (Trivy), 28 (OSV-Scanner)
Exposed backup files, missing headersPartly: flagged the /ftp directory listing at server.ts:290, not the files or headersFound, 31 files under /ftpNot in scope
Time to run50 s55 min 48 s22 s and 3.9 s

Only three flaws were caught twice, all by Semgrep and ZAP together, and two were caught by nobody. Every vulnerable package came from SCA alone, and the exposed backup files and missing headers were confirmed only by DAST. If we had run only an SCA tool, we would have shipped two SQL injections; if we had run only ZAP’s baseline, the scan most teams wire into CI first, we would have shipped all seven code flaws.

How to choose the order to adopt them

Start with whatever covers the most risk for the least effort, then add the tests that need more of your pipeline. NIST’s Secure Software Development Framework draws the same line between reviewing code (practice PW.7) and testing executable code (PW.8), so a programme needs at least one of each. The lab timings and coverage suggest this order:

  1. SCA on every build: seconds to run, almost no false positives on version matching, and it covers the dependency half that the 2026 reports put in most codebases.
  2. SAST on every pull request: it names the file and line while the author still remembers the change, as long as you tune out test fixtures and infrastructure rules.
  3. DAST nightly against staging: the full active scan, not just the baseline, because the baseline found none of the High alerts and the full scan took close to an hour.
  4. IAST when you have test traffic to spare: it pays off when a QA suite already exercises most routes in a supported runtime, and adds little where tests are thin.

Your threat model can change that order. A public API that gets probed every day earns DAST earlier, since the 2025 Verizon DBIR found exploitation of vulnerabilities was the initial access vector in 20% of breaches, second only to credential abuse. A team shipping a library rather than a service may never need DAST at all. The DevSecOps books we recommend go further into wiring these gates into a pipeline without stalling releases.

Tools in this space

Each testing type has open-source and commercial options, and several vendors now sell two or more types on one platform. These are current products in each category, listed by type rather than ranked, since we only measured the open-source tools above:

  • Semgrep (SAST): a rule-based static analysis engine whose Community Edition is open source; the commercial platform adds cross-file dataflow analysis, supply chain scanning and secrets detection.
  • SonarQube (SAST): static analysis for code quality and security, self-hosted or as a cloud service; our list of SonarQube alternatives compares the options around it.
  • CodeQL (SAST): GitHub’s semantic analysis engine, which treats code as a queryable database and powers GitHub code scanning.
  • Snyk (SCA and SAST): a developer security platform covering open-source dependencies, first-party code, container images and infrastructure as code.
  • OWASP Dependency-Track (SCA): an open-source platform that ingests CycloneDX SBOMs and keeps tracking component risk after the build, as new advisories land.
  • ZAP (DAST): the open-source scanner used in this lab, packaged as Docker images with baseline, full and API scan scripts for CI.
  • Burp Suite DAST (DAST): PortSwigger’s scheduled and CI-driven scanner, formerly Burp Suite Enterprise Edition, built on the same scanning engine as Burp Suite Professional.
  • Contrast Assess (IAST): an instrumentation agent that runs inside the application and reports vulnerabilities as tests and users exercise the code.

For container images specifically, Trivy and OSV-Scanner from the lab are both free and fast, and our older walkthrough on scanning Docker images with Trivy shows the CLI in more depth.

Where the four-way split breaks down

The neat split by input hides three misconceptions that cost teams money. The first is that finding counts are comparable. Semgrep’s 68, Trivy’s 82 and ZAP’s 22 count different things (code matches, advisories per package, alert types), so a vendor comparison that adds them up across tools measures nothing useful.

The second is that SAST plus DAST makes SCA redundant. In our run they found zero of the 31 vulnerable package versions between them, because neither reads a version number. The third is that the four letters cover application security. Secrets in Git history, misconfigured Terraform and container base images each have their own scanners, and the 23 CI and infrastructure findings Semgrep raised along the way are a hint that those belong in a separate, deliberate step. Which is why most programmes end up with at least two of these tests rather than one, and with a way to merge their findings into a single queue.

Sources

Lab numbers come from our own scans of Juice Shop. Every other figure on this page traces to one of these:

Raw scanner output (Semgrep, Trivy and OSV-Scanner JSON plus both ZAP reports) was kept from the lab run, so any number above can be traced to the scan that produced it.

Keep reading

UFW Firewall Commands with Examples on Ubuntu 26.04 / 24.04 Security UFW Firewall Commands with Examples on Ubuntu 26.04 / 24.04 PHP2Shell (CVE-2026-109618, CVE-2026-109619): Check and Patch PHP Php PHP2Shell (CVE-2026-109618, CVE-2026-109619): Check and Patch PHP PHP2Shell on End-of-Life PHP 8.1, 8.0 and 7.4 (WHMCS, ionCube) Php PHP2Shell on End-of-Life PHP 8.1, 8.0 and 7.4 (WHMCS, ionCube) Best Pentesting Hardware for Kali Linux in 2026 Security Best Pentesting Hardware for Kali Linux in 2026 APT CVE-2026-109518: Check and Patch Debian and Ubuntu Debian APT CVE-2026-109518: Check and Patch Debian and Ubuntu Install and Use Metasploit Framework on Kali Linux Security Install and Use Metasploit Framework on Kali Linux

Leave a Comment

Press ESC to close