Our last two reports were about the flood of CVEs you can see, and why most of them do not matter. This one is about what a CVE-matching program cannot see at all: software that never runs, and software that is running and malicious but was never assigned a CVE. In 2026, with AI writing more of the code, filing more of the CVEs, and shipping more of the malware, the bill of materials is the only lens that catches either.

Strip a vulnerability program down and it is a matching engine. It inventories what you run, matches each item against a database of known-vulnerable versions, and emits a finding on every hit. Everything downstream, the severity, the ticket, the SLA, hangs off that match. The engine is only as complete as the two lists it joins.
Our prior two reports were about the left-hand list being too long: how a single OpenSSL advisory becomes a fleet-wide noise event, and how a kernel that files thousands of CVEs a year is now cheap for AI to exploit. That work stands, and this report does not repeat it. This report is about the right-hand list, the database of what counts as a vulnerability. Code that is present but never runs is on it and should not be. Code that is running and harmful but was never assigned a CVE is not on it at all, and in 2026 that blind half is the one growing.
Layered configuration: why most of what you are told never runs
A scanner reports vulnerabilities you do not have not because it is wrong about what is installed, but because installation and execution are different events, separated by layers. A base image contributes a full distribution of packages, application layers add dependencies, and runtime configuration decides which of them ever load. A container serving one API exercises a handful and leaves the rest on disk, contributing CVEs to the scanner and nothing to the attack surface. On hosts where we can observe it, about 38% of installed packages never run.

Some of the loudest “criticals” of 2026 were defused entirely by one configuration layer. CVE-2026-47291, an unauthenticated kernel-mode RCE in Windows’ HTTP.sys, carried a 9.8, but the Zero Day Initiative showed a default Windows Server is not exploitable: the overflow only triggers if an admin has raised MaxRequestBytes from its 16,384-byte default to at least 262,144. CVE-2026-22778, a 9.8 RCE in the model server vLLM, is unreachable unless the deployment serves a video model (advisory). The AI gateway LiteLLM shipped a cluster of flaws each gated on a non-default setting. The contrast proves the point: LiteLLM’s ungated SQL injection, CVE-2026-42208, needed no configuration and was exploited within a day and added to CISA KEV. Same product, same severity band, opposite real-world risk, and the only difference is a configuration layer CVSS cannot see.
Defense in depth, read from the vulnerability’s side
Read the layers a team builds, WAF, segmentation, EDR, reachability, from the vulnerability’s side, and most “criticals” turn out to sit behind several already. The CVSS specification concedes this itself: the Base Score excludes compensating controls, and almost nobody computes the Environmental score that would put them back.

The 2026 data is stark. JFrog found only 11.9% of 248 high-profile CVEs genuinely exploitable in context. OX Security cut a backlog across 900 organizations by 99.5% with reachability analysis. Jerry Gamblin found only 0.24% of the first half of 2026’s CVEs reached CISA’s exploited catalog. We see the same shape in our customers’ data: a large share of internet-reachable findings sit behind a WAF with no known exploit and low EPSS, and we set findings aside only when two orthogonal tools, static analysis in the repository and runtime analysis in the running container, independently return a negative exploit verdict for the same CVE. The honest caveat, from Picus: defense in depth is strong at the perimeter (69% blocked) and weaker once inside (37%). Compensating layers buy risk reduction on the way in; they do not let you ignore what is already running.
The blind half: what has no CVE

Turn the engine around and the pipeline goes quiet, because it only ever records “found vulnerable,” never “scanned and clean.” The sharpest version of the blind half is not a measurement gap at all. It is a class of software that has no CVE by design, and in 2026 there is a great deal of it. This is not a forecast; it is the documented record of the last twelve months.
What the attackers are actually doing
A CVE is what you get when someone reports a defect made in good faith. A growing share of supply-chain risk is not that. The code is malicious on purpose, introduced upstream, and it ships as an ordinary release with a legitimate version and often a valid signature. Nobody files a defect on their own backdoor, so there is no CVE and nothing for a matching engine to match.

The Shai-Hulud worm
In September 2025 a self-replicating npm worm named Shai-Hulud began with a single compromised maintainer. A malicious postinstall script ran the secret scanner TruffleHog across each machine, wrote what it found to a public GitHub repository named “Shai-Hulud,” and used any valid npm token to trojanize and republish every package that maintainer controlled. The first wave took @ctrl/tinycolor (over two million weekly downloads), reaching hundreds of packages within days (Wiz).
The technique matured wave over wave. November 2025’s “Shai-Hulud 2.0” backdoored roughly 796 packages, moved its trigger to preinstall, and pulled the Bun runtime to run an obfuscated multi-megabyte second stage that could hijack self-hosted GitHub Actions runners; researchers counted more than 25,000 malicious dump repositories and recovered hundreds of live cloud credentials (on the order of 775 GitHub, 373 AWS, 300 GCP, 115 Azure tokens), with named victims including PostHog, Zapier, Postman, and ENS Domains (Datadog). By May 2026 a cross-ecosystem wave hit npm and PyPI together, including the entire @tanstack family, and did something genuinely new: it poisoned a GitHub Actions build cache so a legitimate maintainer’s own release workflow published the trojan, lifting the short-lived OIDC publish token straight out of CI runner memory. Some of those releases still carried valid SLSA Build Level 3 provenance attestations, the first real-world defeat of that guarantee (Akamai). Then, on 4 August 2026, the largest wave took the keyv and cacheable families, roughly 444 packages summing to close to two billion monthly downloads, buried so deep in ordinary chains (eslint → file-entry-cache → flat-cache → keyv) that most teams never knowingly installed them (Socket). That wave compromised the maintainer’s GitHub account directly, so the poisoned releases were signed by a legitimate pipeline, and it planted autostart hooks inside developers’ AI coding-assistant configuration directories. CISA’s original alert on the campaign is here. Not one wave arrived as a CVE.
North Korea’s package flood
The Contagious Interview activity (MITRE G1052, also DeceptiveDevelopment) has published more than 1,700 malicious packages across npm, PyPI, Go, crates.io, and Packagist since the start of 2025 (Socket). A fake recruiter approaches a developer, a “coding test” asks the victim to run a repository, and the install step drops the BeaverTail infostealer and the InvisibleFerret backdoor. Some 2026 variants read their command-and-control address out of a public blockchain transaction, so there is no fixed indicator to block. None of it is a defect anyone will file a CVE against.
When the scanner is the vector
In March 2026 an actor tracked as TeamPCP used credentials stolen weeks earlier through a misconfigured GitHub Actions workflow to publish a malicious build of the Trivy vulnerability scanner and force-push 76 of 77 version tags of aquasecurity/trivy-action to credential-stealing code, dropping a backdoor that polled a blockchain canister every fifty minutes and a worm that hit 47 npm packages, 28 of them inside a minute (Aqua). Days later LiteLLM, a Python gateway pulling around 3.6 million PyPI downloads a day, ran that compromised scanner in its own CI; the attacker lifted LiteLLM’s PyPI token and shipped two backdoored releases. LiteLLM’s own security posture never mattered, because the malicious code lived three trust-hops away, in the tool meant to find vulnerabilities.
xz-utils: why provenance beats the version string

CVE-2024-3094 earned its CVE only because a Microsoft engineer noticed his SSH logins were half a second slow. The backdoor was the product of a roughly two-and-a-half-year social-engineering campaign by a persona called “Jia Tan,” who built trust on the single-maintainer xz project and even submitted a patch to Google’s fuzzing project that disabled the exact code path the backdoor would later use. The payload never appeared as source: it was staged as disguised binary content inside fake test-fixture files, and a modified build-to-host.m4 present only in the release tarball injected it at build time, but only on x86-64 Linux builds packaged as Debian or RPM. The injected object used a GNU IFUNC resolver to hook RSA_public_decrypt in sshd; because many distributions link sshd against libsystemd, which depends on liblzma, xz sat squarely in the SSH authentication path. The hook tested incoming material against an attacker-held Ed448 key whose public half was hidden with x86 instruction-level steganography: a match granted pre-authentication remote code execution as root (Red Hat).
Anyone who pinned a dependency to “xz 5.6.1” and reviewed the GitHub tag would have seen clean commits, because the tampering happened procedurally, at build time. Rebuilding the same declared version from clean source produces a different binary than the maintainer’s tarball. Provenance decides. The version string does not.
# build-to-host.m4 -- present only in the release tarball, absent from git:if host == x86_64-linux and building (.deb OR .rpm) and toolchain == gcc + GNU-ld: extract object from tests/files/bad-3-corrupt_lzma2.xz splice into liblzma -> IFUNC hook on RSA_public_decrypt in sshdelse: build a clean, ordinary liblzma # what every code reviewer saw
None of these campaigns arrived with a CVE, and their reach was not marginal:
Campaign (2026)ReachDeliveryWhat it tookShai-Hulud → keyv (Aug)~444 packages, ~2B installs/mopreinstall + Bun stage; maintainer-account compromisecloud credentials; AI-tool persistenceShai-Hulud 2.0 (Nov 2025)~796 packagesself-replicating CI-runner hijack25,000 dump repos; hundreds of cloud tokensContagious Interview / Lazarus1,700+ packages, 5 ecosystemsfake recruiter, then BeaverTail + InvisibleFerretdeveloper credentials; crypto walletsTrivy → LiteLLM (Mar)the scanner, plus downstreamforce-pushed action tags; CI token exfiltrationPyPI publish token; blockchain C2chalk / debug takeover (Sep 2025)~18 packages, ~2.6B dl/weekphished maintainer via npmjs.helpbrowser crypto-clipper
Our own inventory, swept against all of it
So we checked, not against a hand-picked dozen but against the full OpenSSF/OSV malicious-package corpus, roughly 235,000 known-bad builds with the Shai-Hulud, keyv, and Lazarus campaigns inside it.

The result is a clean, honest negative. No confirmed compromised build is installed. The campaign packages we do carry, the keyv family, @ctrl/tinycolor, @cacheable/utils sitting one patch below a bad version, rc@1.2.8 (the release right before its own 2021 compromise), all sit on clean or older versions. The apparent hits collapse to cross-registry name collisions, the same version-string trap seen on the noise side. The one genuine lead is a bare xz-utils 5.6.1 present on a small number of hosts with no finding attached, and it is not even in the registry corpus, because a source-level backdoor is not a registry publish. Only reconciling inventory against provenance surfaces it. The honest limit is itself the finding: our inventory is OS-package heavy, and we barely inventory the npm and PyPI layer where every campaign above landed. The place the 2026 supply chain is attacked is the place the inventory is thinnest.
2026: AI manufactures no-CVE risk faster than the system can label it

Three curves are bending at once. AI writes a large share of new code, and close to half of it ships with a security flaw, with the pass rate stalled even as models improve (Veracode 2026). AI-assisted discovery is pushing disclosures toward 66,000 for the year (FIRST), while in April 2026 NIST conceded it will fully enrich only CVEs in CISA KEV, federal use, or EO-14028 scope, leaving most new CVEs as unenriched shells (industry estimates put the prioritized share at 15 to 20 percent). And AI is scaling the attacks that never had a CVE at all. The CVE lens is failing from three sides at once, and every one of them pushes weight onto the one lens that records what is present whether or not anyone scored it.
The inventory is the only complete lens, and it lies too
None of this makes the inventory trustworthy on its own. It over-reports. It mislabels: the reverted 5.6.1+really5.4.5 builds that read as backdoored and the cross-registry names that read as compromised are the same shape of lie. And it barely correlates, because across host-like resources the large majority are seen by exactly one tool. The inventory is where the blind half becomes visible, not where it is already solved. The discipline is reconciliation, not faith.
What to do about the blind half

- Make reachability the denominator, not a tag. A CVE on a package that never loads is a base-image hygiene item, and it is what turns a headline 9.8 into the non-event it usually is in your configuration.
- Read defense in depth from the vulnerability’s side. Give the analyst the compensating layers as first-class inputs, so a finding behind three barriers does not outrank one behind none.
- Reconcile inventory against findings. The dangerous state is a package the inventory can see and the pipeline cannot. Diff the two and treat the gap as the signal.
- Hunt the inventory for known-bad builds against the OpenSSF/OSV corpus on a schedule, and match on provenance, not version strings.
- Close the language-dependency gap and defend the install layer, because that is where every 2026 campaign landed and where reachability alone does not help.
How Zest approaches this
Zest is a vulnerability exposure platform. It ingests the scanners a customer already runs, cloud, container, host, and runtime, and normalizes them into a single model of assets, packages, and findings across environments that together hold hundreds of millions of package entries and findings. That unified model is what makes both halves of this report answerable. For the noise, Zest reads the installed / executed / loaded state per package and folds reachability, exposure, compensating controls, and real-world exploitation into prioritization. For the blind half, it reconciles inventory against findings so a package present with no finding becomes a question rather than a gap, and it sweeps the installed set directly against known-bad builds, the only path that surfaces a compromise with no CVE, including a source-level backdoor no registry feed carries.
The hard part of vulnerability management was never producing more findings. In 2026 it is knowing which of the millions you already have describe software you actually run, and finding the one that produced no finding at all.
See your own inventory-versus-findings gap →
About The Author
.jpg)




