← All guides

SCA: Software Composition Analysis

Code & dependencies
On this page

In plain terms

Modern software is mostly other people's code. A typical application is 80–90% open-source dependencies, meaning libraries you pulled in with npm install, go get, pip install, and so on, each of which pulled in its dependencies. Software Composition Analysis (SCA) is the control that inventories all of that and checks it against databases of known vulnerabilities.

Think of it as checking every ingredient in a recipe against a recall list, and not because you made a mistake, but because someone upstream might have.

Why it matters

You didn't write the vulnerable code, but you shipped it. Most real-world breaches that trace back to "a library" are known, already-patched vulnerabilities that were never upgraded, the Log4Shell/log4j incident is the famous example, but it happens quietly all the time. SCA is often the highest-value control to adopt first, because the fix is usually just "upgrade to the patched version," and the vulnerabilities are publicly cataloged as CVEs.

Where it fits

  • Artifact: your dependency manifests and lockfiles (package-lock.json, go.sum, poetry.lock, pom.xml, …).
  • Stage: early and often. It runs on a pull request with no deployment needed, and again continuously (a dependency that was clean yesterday can have a new CVE today).

How it works

An SCA tool resolves your full dependency tree (direct and transitive), identifies each package and version, and matches them against vulnerability databases, chiefly the OSV (Open Source Vulnerabilities) database and the National Vulnerability Database (NVD). Good tools tell you not just what's vulnerable but whether a fixed version exists and how far you'd have to upgrade. Many also flag license risk from the same dependency tree, since a copyleft license that is incompatible with how you distribute. That's a different kind of problem with a different owner, so it's worth treating separately. See software licenses.

ToolNotes
TrivyOSS, fast, scans filesystems/images/repos; very common default
OSV-ScannerOSS, from Google; built directly on the OSV database
GrypeOSS, from Anchore; pairs with the Syft SBOM tool
retire.jsOSS; fingerprints JavaScript files themselves, so it sees what no lockfile lists
DependabotBuilt into GitHub; opens upgrade PRs automatically
govulncheckOSS, from the Go team; call-graph reachability for Go modules
Snyk, Mend, SonatypeCommercial platforms with reachability across more languages, plus policy. Draugr can run Mend for this control if you already have it

Common pitfalls

  • Transitive noise. Most findings are in dependencies-of-dependencies you can't directly upgrade, because you're waiting on a direct dependency to bump its version.
  • Not all vulnerabilities are reachable. A CVE in a code path you never call is real but low-priority. Reachability analysis separates the two by building a call graph from what is in the build. Coverage varies sharply by language, and the claim is worth only as much as the evidence behind it, which reachability covers.
  • Severity ≠ priority. A wall of "high" CVEs is not a plan. See prioritization.
  • The lockfile is not the whole inventory. Front-end code routinely ships JavaScript no package manager installed, such as a library pulled from a CDN, a vendored file under static/, bundled output shipped without its manifest. Lockfile-based SCA answers for what was installed, so a repository serving a five-year-old jQuery scans clean. The control runs, reports, and passes. The gap matters because of its shape rather than its size, since nothing in that output suggests there is anywhere left to look.
A lockfile answers for what was installed Two ways a library reaches production. Installed through a package manager, it appears in the lockfile, and a dependency scanner resolves it and reports its known vulnerabilities. Committed by hand into the repository, or shipped as bundled output with no manifest beside it, it appears in no lockfile, and the same scanner reports nothing. The second case produces a clean report rather than an error. HOW THE LIBRARY GOT THERE A package manager installed it package-lock.json lodash@4.17.20 The scanner resolves the tree, finds the version, and reports the CVE with the release that fixes it. Somebody committed it static/vendor/jquery-1.9.1.min.js dist/app.bundle.js No manifest names either one. The scanner runs, reports, and passes. A CLEAN REPORT AND AN UNREAD FILE LOOK THE SAME retire.js identifies a library from a fingerprint of the file itself, so a file no manifest lists is still reported with a name, a version, and the version that fixes it.
A lockfile scanner answers for what was installed, precisely and completely. The question is whether that is the same set as what you ship.

How Draugr fits. Draugr's sca control runs Trivy in filesystem mode over each of your component's repositories, normalized to SARIF alongside every other control. Because Draugr knows each component's exposure and criticality, it ranks those CVEs into a short ordered list instead of a flat wall. See the quickstart.

A dependency file no scanner took packages from, such as a pyproject.toml with no lockfile, a requirements file of version ranges or a lockfile in a format no scanner reads, is listed under Caveats as unread, with the reason, because the packages it declares were not checked. With failOnCaveats: [unread-files] in the descriptor's gate, such a file fails the run.

Four more scanners serve the same control, each answering a question Trivy alone does not, and all of them land in that same ranked report. Turning one on is a line in the descriptor, described under running two scanners on one control.

The scannerWhat it adds
Grypea second opinion on the same tree. Where it and Trivy disagree, the difference is a statement about coverage that neither makes alone
retire.jsthe library no manifest lists, identified from a fingerprint of the file itself, so the vendored jQuery above is reported with a name, a version and the version that fixes it
govulncheckon Go modules, which of the known vulnerabilities this code can actually reach, with the call path as evidence. Its verdicts fold onto the findings already there, and a finding nothing can reach is ranked down rather than removed. See reachability
Mendyour existing commercial coverage, if you already pay for it. Opt-in and gated on consent, because a Mend scan uploads your dependency inventory and creates a project in your account, which outlives the scan

Keep learning

  • SAST, the same idea for code you write
  • SBOM, the inventory SCA depends on
  • CVE & CVSS, where vulnerabilities are cataloged and scored