SAST and SCA both run in the same pipeline and both produce long lists of findings, but they answer different questions, and teams that treat them as interchangeable end up fixing the wrong things first. Static application security testing looks for flaws in the code your own developers write. Software composition analysis looks at the open source and third-party packages that code pulls in, for known vulnerabilities, licence problems and, increasingly, packages that are malicious by design. This guide compares what each finds, where each produces noise, how to rank findings from both on one scale using reachability, the CISA KEV catalogue and EPSS, and where each belongs in a delivery pipeline. It is written for application security leads, DevSecOps engineers and engineering managers deciding how to fund, tune and combine the two.
Key takeaways
- SAST finds flaws you wrote; SCA finds risk you imported. A SQL injection in your controller is a SAST finding; the same flaw inside a library you depend on is an SCA finding, reported by version rather than by line.
- You need both, because neither sees the other's territory. SAST tools do not analyse the vulnerability history of packages, and SCA tools do not read your business logic.
- The noise has different causes. SAST noise comes from data flows that are not exploitable; SCA noise comes from vulnerable versions whose affected code is never called.
- Prioritise on evidence, not severity alone. Rank SCA findings by reachability, KEV listing and EPSS probability, and SAST findings by confidence, exposure and data sensitivity, then merge them into one queue.
- Malicious packages are a different problem from vulnerable packages. They need blocking at install time, before any scan of your code would run, and the response is incident handling rather than patching.
The short answer
Run SAST to catch insecure patterns in first-party code while developers still have the change in front of them. Run SCA to know which third-party components you ship, which of them carry known vulnerabilities or unacceptable licences, and whether any are malicious. If budget forces a sequence, organisations whose applications are mostly assembled from open source packages usually get more risk reduction per hour from SCA first, because a large share of shipped code is third-party and the fix (an upgrade) is often mechanical. Large bespoke codebases gain more from SAST first. See the Application Security Guide for where both sit in a wider programme.
What each one finds
SAST (static application security testing) analyses source code, bytecode or binaries without running them, usually by tracing data from untrusted sources to dangerous sinks. OWASP notes that these tools scale well, can run in the IDE and in every build, and point to the exact file and line, but that they automatically identify only a relatively small percentage of application security flaws, produce many false positives, miss configuration that is not represented in code, and find authentication, access control and cryptography misuse hard to detect. They do well on classes such as SQL injection and buffer overflows.
SCA (software composition analysis) builds an inventory of the components an application depends on, directly and transitively, and checks each against vulnerability, licence and health data. OWASP describes component analysis as covering component inventory, age, end-of-life status, known vulnerabilities from sources beyond the NVD, repository trust (typosquatting, dependency confusion, malware transfer), provenance, licence, inherited risk from transitive dependencies and project health. For the internals of how SCA identifies components and records decisions, see the Software Composition Analysis (SCA) Guide.
| Dimension | SAST | SCA |
|---|---|---|
| Code examined | First-party source, bytecode or binaries | Third-party and open source packages, from manifests, lockfiles, build output or an SBOM |
| What a finding is | A weakness pattern (a CWE) at a file and line | A component and version matched to an advisory (CVE, GHSA, OSV ID), licence rule or malicious package report |
| Knowledge source | Rules and data flow models written by the tool vendor or your team | Vulnerability databases (NVD, OSV, ecosystem advisories), licence data, malicious package feeds |
| New findings appear when | Someone changes the code | A new advisory is published, even if the code has not changed |
| Typical fix | Change the code (parameterise, encode, validate) | Upgrade, replace or remove the component, or record that it is not affected |
| Who fixes | The developer who owns the file | The team that owns the dependency manifest, sometimes a platform team for shared base libraries |
| Main noise source | Paths that are not exploitable in practice | Vulnerable versions whose affected functions are never called; duplicate findings across many repositories |
| Blind spots | Configuration, business logic, access control, anything in dependencies | Flaws in your own code, unpublished vulnerabilities, vendored or copied code without package metadata |
| Exchange format | SARIF 2.1.0 (OASIS Standard) | SBOM formats (CycloneDX, SPDX) and OSV records, with VEX for exploitability statements |
Vulnerable packages and malicious packages are different findings
A vulnerable package is a legitimate component with a mistake in it. A malicious package was published to do harm: a typosquat, a dependency confusion upload, or a legitimate package republished by an attacker using a stolen maintainer token. OWASP's 2025 Top 10 reflects this shift: A03:2025 Software Supply Chain Failures widens the older "components with known vulnerabilities" category to cover compromise of how software is built, distributed and updated, and its prevention advice includes sourcing only from trusted locations, favouring signed packages and using staged rollouts.
The difference matters for timing. A vulnerable library usually does harm only once the application is deployed and attacked. A malicious package can do harm the moment it is installed, because install scripts run on the developer laptop or CI runner. The Shai-Hulud npm worm's November 2025 wave used a preinstall script to search for npm, GitHub and cloud credentials, and its analysts found most infections on CI runners rather than developer machines. See the Shai-Hulud npm Worm 2025 breach analysis. An SCA scan that runs after npm install is already too late.
Public malicious package data exists: the OpenSSF malicious-packages repository publishes reports of malicious packages in open source registries such as npm and PyPI in OSV format, so tools that consume OSV can act on them. Treat a malicious package match as an incident (rotate any credentials present where it ran), not as a ticket to upgrade. See the Malicious Package and Dependency Confusion Guide and the npm Install-Time Security Guide.
Noise and triage cost
| Noise source | Tool | Why it happens | What reduces it |
|---|---|---|---|
| Unexploitable data flows | SAST | The engine cannot see a sanitiser, framework protection or input constraint | Model custom sanitisers; start with a high-confidence rule set; triage with recorded reasons |
| Legacy backlog | SAST | First scan of an old codebase reports years of findings at once | Baseline, then gate only on new findings in changed code |
| Code that does not compile | SAST | Many engines need the build and its libraries to resolve types | Scan from the build job, not from a bare checkout |
| Unreachable vulnerable functions | SCA | The vulnerable symbol exists in the package but your code never calls it | Reachability analysis where your ecosystem supports it; VEX statements for the rest |
| Development and test dependencies | SCA | Packages used only in tests or tooling are reported alongside runtime ones | Scope policy: runtime dependencies gate releases, development dependencies follow a slower clock (but stay in scope for malicious package checks, because they still execute on CI) |
| Same advisory in 300 repositories | SCA | A shared library is pinned everywhere | Deduplicate by component and fix centrally in a shared base or template |
| Duplicate reporting | Both | SCA and container image scanning both report the same package | Assign each package type one owning tool. See Container Image Scanning vs SCA |
A practical rule of thumb: a SAST finding costs a developer time to read code and judge a data flow, while an SCA finding costs time to test an upgrade. SAST triage is expensive per finding; SCA triage is cheap per finding but upgrades can break builds, so the cost sits in regression testing. The SAST Triage Guide covers baselines and suppressions in depth.
Prioritising findings from both on one scale
CVSS base scores rank the theoretical severity of a flaw, not the chance it will be used against you. For SCA findings, three signals add that context.
- Reachability: whether your code actually calls the vulnerable function. Go's govulncheck is a well-documented example: the Go vulnerability database records affected packages and symbols, and the tool reports a vulnerability only when your code transitively calls an affected function. Its own documentation lists limits worth knowing for any reachability tool: calls made through reflection are invisible to static analysis, use of
unsafecan cause false negatives, function pointer and interface calls are treated conservatively, and binary scans can report unreachable code. Reachability lowers priority; it should not close a finding on its own. See the Reachability Analysis Guide. - KEV listing: CISA adds a vulnerability to the Known Exploited Vulnerabilities catalogue only with a CVE ID, reliable evidence of exploitation in the wild (proof-of-concept code does not count) and a clear remediation. BOD 22-01 binds US federal civilian agencies only, but CISA recommends that every organisation uses KEV as a priority input. See the CISA KEV Catalogue Guide.
- EPSS probability: FIRST's Exploit Prediction Scoring System estimates the probability that a published CVE will be exploited in the wild in the next 30 days, published daily as a score between 0 and 1 with a percentile. Use it to order the queue below KEV, not to dismiss findings. See the EPSS Guide.
SAST findings rarely have a CVE, so KEV and EPSS do not apply. Rank them on rule confidence, whether the code path is reachable from an exposed entry point (a public route or API rather than an internal batch job), the sensitivity of the data involved, and the CWE class. A tiered model that merges both:
| Tier | SCA criteria | SAST criteria | Target |
|---|---|---|---|
| P0 incident | Malicious package match anywhere, including development dependencies | Hard-coded live credential (hand to secret scanning and rotate) | Same day, incident process |
| P1 | KEV-listed and reachable, in an internet-facing service | High-confidence injection on an exposed route handling sensitive data | Days, gate the release |
| P2 | Reachable with high EPSS, or KEV-listed with reachability unknown | High-confidence finding on an exposed route, or medium confidence with sensitive data | Within the sprint |
| P3 | Reachable, low EPSS; or unknown reachability, critical CVSS | Medium confidence, internal entry point | Planned backlog |
| P4 | Not reachable (recorded as a VEX statement), or development-only and not malicious | Low confidence or legacy baseline | Routine upgrade cycle or periodic review |
Targets are illustrative: set your own in policy. When an SCA finding is judged not affected, record the reason in a VEX statement so the decision survives the next scan and can be shared with customers. See the VEX Guide.
Threat model, failure modes and controls by option
SAST
- Threat it addresses: attackers exploiting weaknesses introduced by your own developers, such as injection, path traversal, unsafe deserialisation and weak cryptographic calls in code.
- Architecture in words: the engine parses code into a model (syntax tree, data flow graph), applies rules that describe sources, sinks and sanitisers, and emits results, ideally as SARIF so they can be merged with other tools. Incremental scans on pull requests analyse changed files; full scans run on a schedule.
- Failure modes: rules that do not understand your framework (false negatives), suppressions without reasons, scans that silently skip languages, and gates so noisy that teams disable them.
- Controls: per-language coverage reports, custom rules for in-house frameworks, reviewed suppressions, and a measured precision rate per rule.
SCA
- Threat it addresses: exploitation of known flaws in third-party components, licence obligations you did not intend to accept, and malicious or hijacked packages entering the build.
- Architecture in words: the tool reads manifests and lockfiles (or the built artefact, or an SBOM), resolves the full dependency tree, identifies each component (often by Package URL), and matches versions against advisory data. Some tools add call graph reachability; registry proxies or install-time policies add blocking.
- Failure modes: scanning manifests without lockfiles (so resolved versions are guessed), missing vendored or copied code, running only after install so malicious install scripts have already executed, and treating "no known CVEs" as "safe".
- Controls: lockfiles committed and verified, a private registry proxy with malicious package blocking, install scripts disabled on CI by default where the ecosystem allows, and an SBOM per release.
Pipeline placement
SAST findings arrive with a code change; SCA findings also arrive when a new advisory is published against an unchanged lockfile.
| Stage | SAST | SCA |
|---|---|---|
| IDE | Fast rules on save, high confidence only | Warning when adding a package with known issues or a malicious report |
| Dependency install | Not applicable | Registry proxy or policy blocks malicious and disallowed packages before install scripts run |
| Pull request | Incremental scan; show only new findings in changed code; gate on P1 | Diff of dependency changes; gate on malicious, KEV-listed reachable or disallowed licence |
| Main branch build | Full scan; results to the central tracker as SARIF | Full resolution from the lockfile; generate the SBOM |
| Release | Confirm no open P1 findings | Attach the SBOM and VEX statements to the release record |
| After release | Scheduled full scan to pick up new rules | Continuous monitoring of the shipped SBOM against new advisories, including for code no one is changing |
The last row is where many programmes fail: dormant applications still get new SCA findings. See SBOM vs SCA for how the two artefacts relate.
Where identity fits
Both tools hold privileged non-human identities: tokens that read every repository, post pull request comments, open automated upgrade pull requests and sometimes merge them. A dependency update bot with merge rights can push a hijacked package version straight to production. Scope these tokens per organisation or repository group, prefer short-lived workload identity over stored tokens, require human review for dependency updates in sensitive repositories, and keep secrets out of the steps that install dependencies. See the CI/CD Pipeline Identity Security Guide and Securing the SAST, SCA and code scanning tool itself.
Decision criteria and worked scenario
- Lead with SCA when applications are assembled mostly from open source packages, you have many small services, or customers and regulators ask for SBOMs.
- Lead with SAST when you maintain large bespoke codebases, process sensitive data through custom logic, or have had incidents rooted in your own code.
- Small organisations: start with the dependency alerts and static analysis built into your code hosting or CI platform, plus a registry setting that blocks install scripts on CI. See the Open Source AppSec Tools Landscape Guide.
- Large organisations: run both, deduplicate in a posture platform, and set testing tiers per application by exposure and data class. See the Application Security Posture Management (ASPM) Guide.
- Either way: neither replaces secret scanning, DAST or manual review. See SAST vs DAST.
Worked scenario. A payments team runs a Node.js API and a Go settlement service. The first SCA scan reports 640 findings, the first SAST scan 410. The team baselines SAST and gates only on new high-confidence findings in changed code, which leaves 12 findings to read in the first sprint, three of them true injection paths on public routes (P1). For SCA, govulncheck on the Go service shows only four of 38 advisories reach vulnerable symbols; the rest are recorded as not affected with VEX statements. On the Node.js side, where reachability data is thinner, the team ranks by KEV and EPSS and fixes two KEV-listed packages in the API's runtime dependencies first. A dependency diff on a later pull request flags a newly added package with an OpenSSF malicious report; it is blocked at the registry proxy before install, and because it never ran, no rotation is needed.
How to implement both
- Inventory: list repositories, languages and package ecosystems, and confirm which each tool supports.
- Block malicious packages first: put a registry proxy or install-time policy in front of CI and disable install scripts where the ecosystem allows.
- Baseline and pilot: run both tools in report-only mode on two or three representative repositories, triage the results, and tune rules and scope before any gate.
- Gate narrowly: turn on pull request gates for P0 and P1 only, with a documented break-glass override that is logged and reviewed.
- Roll out in rings: extend to teams in waves, watching override rates and pipeline time. Roll back a gate to report-only if overrides exceed the threshold you set, fix the cause, then re-enable.
- Merge the queues: send SAST SARIF and SCA results to one tracker with one priority model, so developers see one list.
- Monitor what you ship: generate SBOMs per release and watch them for new advisories, including for dormant applications.
Detection and measurement
- Precision per rule (SAST): share of triaged findings confirmed as true positives; retire or tune rules below your threshold.
- Reachable share (SCA): proportion of SCA findings with reachability evidence, and how many were deprioritised as not affected.
- Time to fix KEV-listed reachable components: median days from advisory to deployed fix.
- Malicious package blocks: count of install-time blocks, and any malicious package found after install (target zero).
- Gate override rate: overrides per hundred builds, by team; a rising rate signals noise.
- Coverage: share of repositories and ecosystems scanned by each tool, and share of releases with an SBOM attached.
Common failure patterns
- Buying SCA and assuming code is covered. SCA says nothing about injection in your own controllers.
- Gating on CVSS alone. Blocking every critical CVSS finding regardless of reachability or exploitation evidence trains teams to override gates.
- Scanning after install. Malicious install scripts run before a post-install scan reports anything, as the Shai-Hulud npm Worm 2025 showed on CI runners.
- Treating reachability as proof. Reflection, dynamic loading and conservative call graphs mean "not reachable" lowers priority but should be recorded, not silently closed.
- Ignoring dormant applications. No commits means no SAST findings, but new advisories keep arriving against the shipped components.
Framework mapping
| Framework | Reference | SAST | SCA |
|---|---|---|---|
| NIST SP 800-53 Rev. 5 | SA-11, SA-11(1) | SA-11(1) Static Code Analysis | Developer testing and evaluation of included components |
| NIST SP 800-53 Rev. 5 | RA-5, SI-2 | Supporting | Vulnerability monitoring and flaw remediation for components |
| NIST SP 800-53 Rev. 5 | SR-3, SR-4 | Not applicable | Supply chain controls and provenance of components |
| NIST SP 800-218 (SSDF) | Secure software development practices | Reviewing and analysing human-readable code | Reusing well-secured software and identifying vulnerabilities in it |
| NIST CSF 2.0 | PR.PS-06, ID.RA-01 | PR.PS-06 secure development integrated in the life cycle | ID.RA-01 vulnerabilities identified, validated and recorded |
| NIST CSF 2.0 | GV.SC-05 | Not applicable | Supply chain security requirements in supplier agreements |
| MITRE ATT&CK v20 | T1195.001 | Not applicable | Compromise Software Dependencies and Development Tools |
| OWASP Top 10 2025 | A03:2025 | Indirect | Software Supply Chain Failures |
Practitioner checklist
- Confirm which languages and package ecosystems each tool actually covers, and close the gaps.
- Block malicious and disallowed packages at the registry or install step, before install scripts run.
- Baseline SAST and gate only on new high-confidence findings in changed code.
- Rank SCA findings by reachability, KEV listing and EPSS, not CVSS alone.
- Record not-affected decisions as VEX statements with reasons.
- Send SAST and SCA results into one tracker with one priority model.
- Generate an SBOM per release and monitor it for new advisories, including for dormant applications.
- Scope scanner and dependency bot tokens tightly and require review for dependency updates in sensitive repositories.
- Track precision, reachable share, time to fix KEV-listed components and gate override rates.
Standards and references
- NIST: SP 800-218 Secure Software Development Framework (SSDF) Version 1.1
- NIST: SP 800-53 Rev. 5 Security and Privacy Controls
- CISA: Known Exploited Vulnerabilities Catalog
- FIRST: Exploit Prediction Scoring System (EPSS)
- OWASP: A03:2025 Software Supply Chain Failures
- OWASP: Component Analysis
- OWASP: Source Code Analysis Tools
- OpenSSF: Malicious Packages repository
Related NHI Mgmt Group resources: Software Composition Analysis (SCA) Guide · SAST Triage Guide · Reachability Analysis Guide · AI-Assisted Code Review and AI SAST Guide · SBOM Guide