On March 29, 2024, PostgreSQL developer Andres Freund noticed SSH logins on a Debian sid box taking roughly 500 milliseconds instead of the usual 100 — a latency anomaly he chased into a multi-year backdoor planted in xz-utils 5.6.0 and 5.6.1 by a contributor using the handle "Jia Tan." The payload sat in liblzma, a compression library linked transitively into sshd through libsystemd, and it drew a CVSS score of 10.0. Two years earlier, on December 9, 2021, Apache disclosed Log4Shell (CVE-2021-44228) — also CVSS 10.0 — after Alibaba Cloud's Chen Zhaojun privately reported it two weeks prior. In both cases, the hardest part of incident response wasn't writing the patch. It was answering a question most organizations couldn't answer in hours: which of our services actually ship this library, how deep is it buried, and which of those exposures matters most right now? A Software Bill of Materials lists what's inside a single artifact. It takes a second structure — a service and deployment dependency graph — joined against that inventory to compute blast radius and turn a flat CVE alert into a ranked patch order.
What does an SBOM actually tell you, and what does it leave out?
An SBOM, in SPDX or CycloneDX format, enumerates every component in a build artifact — name, version, supplier, and the declared relationships between them — following the baseline the NTIA published on July 12, 2021 as the "Minimum Elements for a Software Bill of Materials" under Executive Order 14028. That's genuinely useful: given a new CVE, you can grep an SBOM and get a yes/no answer for one artifact in seconds instead of days. What it doesn't tell you is what happens next. An SBOM has no concept of which services are internet-facing, which process handles payment data, which build artifact is deployed to production versus a dev sandbox, or whether the vulnerable function is even called. Two services can share the exact same vulnerable transitive dependency and carry wildly different real-world risk — one sitting behind three layers of internal auth, the other terminating public HTTP traffic directly. SBOMs are inventory. They are not risk.
Why does the dependency graph matter more than the CVSS score?
CVSS scores a vulnerability in isolation, assuming worst-case conditions for an unspecified deployment. Blast radius asks a narrower, more useful question: given your actual topology, what does exploiting this specific vulnerable component let an attacker reach next? That requires a service dependency graph — which services call which, which sit behind a load balancer versus an internal mesh, which share a database or a secrets store — overlaid on the component graph from your SBOMs. In the xz-utils case, the component itself (a compression library) had no obvious network-facing role; its danger came entirely from graph position, sitting transitively behind OpenSSH on affected distributions. A vulnerability scanner that only reports "liblzma 5.6.1 present, CVSS 10.0" produces the same alert for a box running public SSH and a box that never opens a network socket. Graph position is what tells you which one to fix tonight.
How do you compute fan-out when one library sits behind dozens of services?
Fan-out is the count of independently deployed services that resolve to the same vulnerable component, whether as a direct or transitive dependency, and it's usually the single biggest driver of total organizational exposure for widely used libraries. Log4Shell demonstrated this at extreme scale: log4j-core wasn't a niche dependency, it was quietly present — often several dependency levels deep — inside application servers, admin consoles, and embedded appliances across effectively every Java shop running anything built in the prior decade, which is why remediation stretched into 2022 for many organizations despite the patch shipping within days. Computing fan-out requires querying SBOMs across your whole fleet, not one artifact, and grouping by resolved package + version, since a fix for log4j-core:2.14.1 doesn't help a service still resolving 2.15.0-rc1 through a different lockfile. The output is a ranked list: not "here's a vulnerability," but "here are the 47 services that share it, sorted by how many of them are exposed."
Does reachability change the priority order, not just the noise level?
Yes — reachability re-sorts the list, it doesn't just shrink it. A component being present and a component's vulnerable code path being invoked at runtime are different facts, and multiple vendor studies from 2023–2024 found that the large majority of CVEs flagged by standard software composition analysis in typical applications sit on code paths never actually executed. For blast radius specifically, reachability matters because fan-out and reachability move independently: a library might be present in 40 services but only reachable from user input in 6 of them, and those 6 — not the raw count of 40 — are where an attacker gets a foothold first. Building a call graph from each service's entry points down through its resolved dependency tree, and checking whether execution reaches the vulnerable function, converts "47 services affected" into "6 services affected and exploitable, 41 affected but dormant." That's the difference between an all-hands incident and a scheduled maintenance window.
What should actually drive patch order when everything can't go first?
Patch order should weight internet exposure, data sensitivity, reachability, and fan-out together — not CVSS alone, and not any single one of those factors alone either. A service that's internet-facing, handles authentication tokens, has the vulnerable path confirmed reachable, and shares the dependency with a dozen other services sits at the top regardless of whether the CVE itself scores 7.5 or 10.0; an internal batch job with the same CVE, no external exposure, and an unreachable code path can wait for the next release train. This is the practical lesson from both Log4Shell and xz-utils: responders who had a queryable graph joining components to services to exposure could produce a ranked list within hours, while those without one spent that time just discovering where the library lived. The SBOM answers "what." The dependency graph answers "where it matters." Neither alone produces a patch order — the join does.
How Safeguard helps
Safeguard generates CycloneDX SBOMs automatically on every build and ingests SBOMs from vendors and open-source dependencies alike, giving you one queryable inventory instead of a folder of static reports. Deep dependency scanning walks the resolved graph to depth 100 — well past the roughly 50-60 level ceiling common in other SCA tools — specifically because incidents like SolarWinds SUNBURST, the xz-utils backdoor, and recurring npm malware campaigns have shown the dangerous edge sitting several transitive levels down, not at the top. Reachability analysis then traces each finding through the actual call graph, so a shared transitive dependency only ranks urgent where your code can execute the vulnerable path, and Griffin AI explains the reachable path in plain language so a security team can go from "new CVE dropped" to a ranked, fan-out-aware patch order without manually reconstructing the graph by hand.