Open-source code now makes up roughly 70-90% of the average application's codebase, according to Synopsys's 2024 Open Source Security and Risk Analysis report, which sampled over 1,000 commercial codebases. That means most of the attack surface your security team is responsible for wasn't written by anyone on your payroll. Software composition analysis (SCA) is the category of tooling built specifically to inventory that open-source code, match it against known vulnerability databases, and flag the packages that put you at risk. When Log4Shell (CVE-2021-44228) surfaced in December 2021, teams without a working SCA practice spent weeks manually grepping file systems to find every copy of Log4j buried three or four dependencies deep. Teams with SCA already deployed had an answer in hours. This post breaks down how SCA actually works, where it falls short, how Safeguard's approach compares to incumbents like JFrog, and what to look for in 2026.
What is software composition analysis (SCA)?
Software composition analysis (SCA) is the automated process of identifying every open-source and third-party component inside an application — including transitive (indirect) dependencies — and cross-referencing them against databases of known vulnerabilities, licenses, and outdated versions. The term dates back to early tools like Black Duck (founded 2002, later acquired by Synopsys in 2010 and then Sonar in 2024), which focused mainly on open-source license compliance. Vulnerability detection was bolted on later as the National Vulnerability Database (NVD) and CVE program matured through the 2010s. Today a typical mid-size Java or Node.js application pulls in 150-800 transitive dependencies for every dependency a developer directly declares — npm's own 2023 ecosystem data shows an average ratio of roughly 1:80 direct-to-transitive packages for popular frameworks. SCA exists because no human can track that graph by hand, and because roughly 96% of codebases scanned in the 2024 OSSRA report contained at least one open-source component with a known vulnerability.
How does SCA actually find vulnerable dependencies?
SCA tools find vulnerable dependencies by generating a software bill of materials (SBOM), fingerprinting each component, and matching those fingerprints against vulnerability feeds. The pipeline generally runs in four steps: (1) parse manifest and lockfiles — package-lock.json, pom.xml, go.sum, requirements.txt — plus binary and container layers to build a full dependency tree; (2) generate identifiers for each package, usually Package URLs (purl) or CPE strings, sometimes reinforced with binary hashing to catch renamed or vendored code; (3) match those identifiers against vulnerability sources such as the NVD, GitHub Security Advisories, OSV.dev, and vendor-specific research feeds; and (4) score and prioritize results, typically starting from CVSS but increasingly layered with reachability analysis — is the vulnerable function actually called by your code path? — because CVSS alone doesn't account for exploitability in context. Mature platforms also track EPSS (Exploit Prediction Scoring System) probabilities, which estimate the odds a given CVE will be exploited in the next 30 days, to help teams triage the roughly 25,000+ CVEs published in 2024 alone.
Why didn't SCA catch the xz backdoor in March 2024?
SCA didn't catch the xz backdoor (CVE-2024-3094) before release because the malicious code was deliberately hidden inside build scripts and test binaries rather than in a versioned, publicly disclosed vulnerability database — it was found by a Microsoft engineer, Andres Freund, on March 29, 2024, who noticed SSH logins taking 500 milliseconds longer than expected, not by a scanner. Traditional SCA is fundamentally reactive: it matches known-bad signatures against a database, so it can only flag a compromised package after someone has already discovered, disclosed, and cataloged the compromise. The xz incident, where a multi-year social-engineering campaign got a backdoored maintainer merged into liblzma 5.6.0 and 5.6.1, exposed the gap between "component inventory" and "supply chain integrity." It's why the industry has shifted toward pairing SCA with behavioral and provenance signals — build reproducibility checks, maintainer-activity anomaly detection, and package-registry monitoring for typosquats and unexpected postinstall scripts — none of which live inside a classic CVE-matching engine.
How is SCA different from JFrog Xray's approach?
JFrog's SCA offering, Xray, is built as an extension of the JFrog Artifactory binary repository, which means its strongest signal comes from artifacts that already flow through Artifactory as part of the build and release process. That's a reasonable model if your organization has standardized on JFrog as its artifact registry, but it creates a coverage gap for dependencies pulled directly from source registries (npm, PyPI, crates.io, Go modules) in CI environments that don't route through Artifactory, and it adds friction for teams evaluating SCA independently of a broader binary-management commitment. JFrog also weights heavily toward its own Xray vulnerability research feed layered on top of NVD data. Safeguard's model is registry-agnostic by design: it scans dependency manifests, lockfiles, and running artifacts wherever they live — CI pipeline, container registry, or production — without requiring you to first migrate binary management into a single vendor's repository, and it correlates multiple public and proprietary feeds (NVD, GHSA, OSV) rather than one primary source, which reduces both false negatives from feed gaps and vendor lock-in.
How often should SCA scans run, and does CI/CD timing matter?
SCA scans should run on every pull request and every build, not on a weekly or monthly cadence, because new CVEs are disclosed against existing packages constantly — NVD published new or updated entries for previously "clean" packages nearly every day in 2024. A once-a-month scan means a package that was safe on day one can sit in production for up to 29 days with a disclosed, exploitable CVE before anyone notices. Running SCA at the pull-request stage also matters because remediation cost compounds: fixing a flagged dependency before merge is a version bump in a PR; finding it three months later in production means coordinating a hotfix, re-testing, and possibly an incident write-up. The Equifax breach in 2017 is the reference case here — the vulnerable Apache Struts component (CVE-2017-5638) had a patch available for two months before attackers exploited it, and the eventual breach cost the company over $1.4 billion in settlements and remediation.
How Safeguard Helps
Safeguard treats SCA as one layer of a broader supply chain security posture rather than a standalone checkbox. On the detection side, Safeguard continuously scans dependency manifests, lockfiles, and container images across every registry and CI system you use — no requirement to route builds through a proprietary artifact repository first — and correlates findings across NVD, GitHub Security Advisories, and OSV.dev to reduce blind spots from any single feed. Results are prioritized using both CVSS severity and reachability analysis, so your team sees which of the (often hundreds of) flagged CVEs in a typical scan are actually exploitable in your call paths, rather than triaging every match with equal urgency.
Because incidents like the xz backdoor showed that CVE-matching alone misses supply chain compromises, Safeguard layers provenance and behavior signals on top of standard SCA: monitoring for anomalous maintainer changes, unexpected install scripts, and typosquat patterns in your dependency graph, so a compromised package can be flagged before it's formally disclosed as a CVE. Scans run automatically on every pull request and merge, with policy gates that can block a build when a newly introduced dependency carries a critical, reachable vulnerability — turning what used to be a quarterly audit finding into a same-day fix. For teams evaluating a move away from tools like JFrog Xray, Safeguard is built to plug into existing CI/CD and registries without a migration project, so you get dependency visibility on day one rather than after a multi-quarter rollout.