SAST vs SCA comes down to one question: whose code are you checking? SAST (static application security testing) analyzes the code your team writes. SCA (software composition analysis) analyzes the open-source code you import. Both are static, both run early in the pipeline and both show up in the same dashboards, which is why people confuse them. But they find completely different classes of problems, and neither can do the other's job.
This guide compares software composition analysis vs SAST in practice: what each one finds, where they overlap, why reachability and lockfiles decide how useful SCA is, what license risk means, and how to run both without drowning your team in alerts.
What SAST does
SAST reads your source code and builds a model of how data moves through it. It traces untrusted input (request parameters, headers, message payloads) to dangerous operations (SQL queries, shell commands, template rendering, file paths) and reports the vulnerable path with a file and line. Modern AI-based SAST also reasons about logic that rules cannot express, like a handler that forgets to check who owns a record. See our SAST tools guide for how rule-based and AI SAST approaches differ.
Typical SAST findings: SQL injection, command injection, XSS sinks, path traversal, unsafe deserialization, SSRF, hardcoded credentials, weak cryptography and broken access control in your own handlers.
What SCA does
SCA builds an inventory of your dependencies from manifests and lockfiles (package-lock.json, Gemfile.lock, poetry.lock, go.sum and so on), including transitive dependencies you never chose directly. Then it matches each package and version against vulnerability databases such as OSV, the GitHub Advisory Database and the NVD, and tells you which versions fix the issue. Many tools also report licenses. The same inventory can be exported as an SBOM.
Typical SCA findings: a known CVE in a library version you ship, an abandoned package, a dependency with a license that conflicts with how you distribute your product.
SAST vs SCA comparison table
| Dimension | SAST | SCA |
|---|---|---|
| What it analyzes | Code your team wrote | Third-party packages you depend on |
| Input | Source files | Manifests and lockfiles |
| What it finds | New, unknown vulnerabilities in your logic | Known, published vulnerabilities (CVEs) and license issues |
| Knowledge source | Rules, data-flow models or AI reasoning over your code | Vulnerability databases (OSV, GitHub Advisories, NVD) |
| Typical fix | Change your code | Upgrade to a fixed version, or replace the package |
| Location | File and line | Package, version and dependency path |
| Main noise source | Findings that are not actually exploitable | CVEs in code paths you never call |
| When new findings appear | When code changes | When code changes, and also when a new CVE is published for a package you already ship |
That last row matters more than it looks. SAST results only change when your code changes. SCA results can change overnight with no commit at all, because someone published an advisory for a library you have shipped for two years. SCA has to run continuously, not only on pull requests.
What each one misses
A bug SCA will never see
// invoices.js: your code, no vulnerable dependency involved
app.get('/api/invoices/:id', requireLogin, async (req, res) => {
const invoice = await Invoice.findById(req.params.id);
res.json(invoice); // missing: invoice.ownerId === req.user.id
});Every package here can be fully patched. The vulnerability (an IDOR, part of broken access control) lives in your logic. Only analysis of your own code, whether SAST, review or testing, can find it.
A bug SAST usually will not flag
// package.json
"dependencies": {
"lodash": "4.17.20"
}lodash versions before 4.17.21 are affected by CVE-2021-23337, a command injection through the template function, rated high severity. Your code may look perfectly normal; the problem is the version you pinned. SAST tools generally do not analyze the internals of node_modules against advisories. SCA matches [email protected] to the advisory and tells you to upgrade to 4.17.21.
Where SAST and SCA overlap
The boundary is not perfectly clean, and the gray areas are where incidents come from:
- Unsafe use of a safe library. PyYAML is not vulnerable, but
yaml.load(data, Loader=yaml.Loader)on user input is. SCA sees a healthy package; SAST sees the dangerous call. This is how many remote code execution bugs happen. - Vendored and copied code. A library copied into
vendor/or pasted from a snippet does not appear in any lockfile, so SCA may miss it. SAST scans it as if it were yours, which it now is. - Reachability. Deciding whether a CVE matters needs both: SCA says which function is vulnerable, code analysis says whether you call it.
- Configuration and supply chain. CI workflows, Dockerfiles, base images and Terraform are neither "your app logic" nor "a package", but they are part of what you ship. Good programs cover them too, alongside a secret scanner for keys in git history.
Reachability: the difference between 400 alerts and 12
A typical JavaScript app pulls in hundreds of transitive packages, and SCA against them often returns a long list. Many of those CVEs sit in functions your code never calls, or in dev-only tooling that never runs in production. Treating them all as urgent is how teams learn to ignore SCA.
Reachability analysis asks: does any path from your code reach the vulnerable function? If the lodash advisory is about _.template and you only use _.get, the finding is real but low priority. If your request handler passes user input into _.template, it goes to the top of the list. Not every tool does reachability, and none is perfect (dynamic imports and reflection make call graphs incomplete), but even a rough version, combined with "is this a production dependency?", cuts noise dramatically.
Lockfiles: SCA is only as good as its input
If your repository only has "express": "^4.18.0" in a manifest, an SCA tool has to guess which version you actually run. Lockfiles remove the guesswork by recording the exact resolved version of every direct and transitive dependency.
- Commit your lockfiles (
package-lock.json,yarn.lock,pnpm-lock.yaml,Gemfile.lock,poetry.lock,Cargo.lock,go.sum). - Install from them in CI (
npm ci,bundle install --frozenand equivalents) so what you scan is what you build. - Watch for multiple lockfiles in monorepos; each service may resolve different versions.
License risk
Security is not the only thing SCA can find. Every dependency comes with a license, and some licenses impose obligations. Permissive licenses like MIT, BSD or Apache 2.0 mostly ask for attribution. Copyleft licenses like GPL or AGPL can require you to release source code under certain conditions, and AGPL extends that to software offered over a network. Whether an obligation applies depends on how you use and distribute the code, so treat license findings as input for a legal review, not as a verdict. What matters for engineering is having the inventory, so the question can be answered at all.
Why you need both
Think of your application as two halves. SAST covers the half you wrote, where new and unknown bugs are born. SCA covers the half you imported, where known and published bugs accumulate over time. Skipping SAST leaves your business logic unreviewed. Skipping SCA leaves you blind to the next Log4Shell. A practical setup:
- On every pull request: SAST on changed code and SCA on changed manifests and lockfiles, with findings ranked by exploitability.
- Continuously: re-check existing dependencies against new advisories, because new CVEs appear without new commits.
- On a schedule: a full scan of the repository, including secrets in history and infrastructure configuration.
- Periodically: dynamic testing and pentests for what static analysis cannot see. Our SAST vs DAST guide covers that layer.
This is the practical core of DevSecOps: cheap, precise checks early, expensive checks where only they can help.
How Nurbak does both
Nurbak runs SAST and SCA in the same scan. You connect GitHub and pick a repository. Its own self-hosted AI model analyzes your code (the analysis does not send your code to OpenAI or Anthropic) and reports exploitable vulnerabilities with the file and line. In the same pass it checks your dependencies for known CVEs via OSV and tells you the fixed version to upgrade to, and it flags GitHub Actions, Docker, Terraform and Kubernetes misconfigurations and secrets in git history. Results come as a 0 to 100 score with plain-language explanations, and for a finding you choose to fix it can open a Pull Request with the fix plus a security regression test (the fix uses Claude, with your explicit consent). The free scan shows the three most important findings in full; plans start at USD 79/month. Start from the software composition analysis page.
How to choose tools
- Coverage of your stack: languages for SAST, package ecosystems and lockfile formats for SCA.
- Fixed versions, not just CVE IDs: an SCA finding without an upgrade path is homework, not help.
- Prioritization: exploitability, reachability, production vs dev dependency.
- Developer workflow: results in pull requests, clear explanations, suggested fixes.
- Data handling: where your code is processed, especially with AI-based analysis.
Bottom line
SAST vs SCA is not a choice. SAST finds the bugs you write; SCA finds the bugs you import. They overlap at the edges (how you call libraries, reachability, vendored code), and that overlap is exactly why running them together beats running either alone. If you already have one, add the other. A good first step is checking your repository with software composition analysis and seeing how many known CVEs are sitting in your lockfile today.
