Supply Chain Security

Your Package Manifests Are a Supply-Chain Gift to Attackers

2MNY Admin October 13, 2026 5 min read 3 views

Dependency scanners usually run where the code is built. But when the manifests themselves ship to the web root, anyone can download your exact dependency inventory — name, version, architecture — and match it against public vulnerability databases at their leisure. Here's what leaks per ecosystem, and how a free scan turns the same data into your defense.

Modern applications are mostly someone else's code. A typical web project pins between fifty and three hundred third-party packages, and every one of them carries a version number that public vulnerability databases — OSV, NVD, GitHub Advisories — already index. Your build pipeline knows those versions because it reads manifests. The question this post asks is simpler: are those manifests sitting in your web root, downloadable by anyone?

They often are. Framework scaffolding drops package.json into the project root, Python deploy guides say scp the whole directory, and Go projects ship go.mod and go.sum next to the binary because "the binary reads them at startup" — except it usually doesn't, they're just there. Each readable manifest is a precise, self-describing inventory of your attack surface, and attackers read them before you do.

What Each Manifest Hands Over

  • requirements.txt / Pipfile / pyproject.toml (Python) — name==version pins. Python's ecosystem makes this especially valuable to attackers: a large fraction of public CVEs with working public exploits are in pip packages, and exact pins remove all guesswork from exploit selection.
  • go.mod / go.sum (Go) — module names, versions, and cryptographic hashes. go.sum even includes transitive dependencies, so the inventory is more complete than what your developers consciously chose.
  • Gemfile / Gemfile.lock (Ruby) — the lockfile's GEM specs block lists every gem with an exact version. Ruby's history of deserialization and file-handling gems makes exact versions directly weaponizable.
  • pom.xml / build.gradle (Java) — groupId:artifactId:version coordinates for every dependency, including dependencyManagement entries. Java's ecosystem is the richest target of all: deserialization gadget chains and template injection exploits are frequently version-gated, and the pom tells the attacker exactly which chain applies.
  • Cargo.toml (Rust) and packages.config (NuGet/.NET) — same story, different registry.

With a manifest in hand, an attacker doesn't scan your site for vulnerabilities — they look up which ones you have, choose the quietest, and act. It converts blind probing into a targeted operation, and it never touches your server until the exploit itself fires.

The Two Findings That Matter

An exposed manifest produces two distinct problems, and conflating them leads to bad prioritization:

1. The exposure itself (information disclosure). The file shouldn't be downloadable. This is a misconfiguration class finding — the fix is the same as for .git exposure: don't ship build metadata to the web root, or deny the patterns at the web server.

2. The vulnerable dependencies it reveals. Whether or not the manifest is exposed, the dependencies it describes may have known CVEs. The manifest merely publishes the problem. This is where software composition analysis comes in — and where our sca_manifest_deep scanner earns its name.

How the Free Scan Works

When you run a free security scan on 2MNY, the SCA scanner fetches each manifest from your site root through the same Tor-routed, real-browser-fingerprint transport an external attacker would use — then parses eleven manifest formats across nine ecosystems: pip (three variants), RubyGems (two), Go (two), Maven (two), Cargo, and NuGet. Pins are extracted exactly (property placeholders and non-literal versions are skipped rather than guessed), deduplicated, and batch-queried against OSV.dev — the open, free vulnerability database backed by Google and fed by GitHub Advisories, PyPA, RustSec, and the Go team.

Every match becomes a finding with the CVE list, severity, and fixed versions — so the report doesn't say "you may have outdated packages," it says "you are running X 1.2.3; versions before 1.4.0 are affected by CVE-2025-XXXX (fixed in 1.4.0)." Active-exploitation and KEV-listed vulnerabilities surface as high severity; the rest rank by real impact. Because the same manifest data feeds both sides, the finding you receive is literally the finding an attacker would construct — minus the exploit, plus the fix.

One design note: the scanner deliberately reports only confirmed vulnerable dependencies, not the mere readability of the file — exposed-file detection is owned by our sensitive-path scanners, so a single issue never appears twice in a report with different titles. If you see an SCA finding, it's a dependency problem. If you see a disclosure finding, it's a file-exposure problem. Different problems, different fixes.

Close the Gift Shop

Three steps, in order of how much grief they save you:

  1. Stop shipping manifests. Deploy built artifacts (vendored vendor/ with autoloader, compiled binaries, dist folders) — not the project directory with its package.json, go.sum, and pom.xml intact.
  2. Deny manifest patterns at the web server as a backstop: package.json, package-lock.json, yarn.lock, composer.lock, requirements*.txt, Pipfile*, pyproject.toml, Gemfile*, go.*, pom.xml, build.gradle, Cargo.*, packages.config.
  3. Scan from the outside so the evidence matches what the internet sees. The 2MNY scanner checks every manifest type above, cross-references OSV live, and hands you a version-pinned remediation list — the same list an attacker would write, pointed the other way.

Your dependency list is inevitable — the vulnerability database already knows it. The only choice you get is who reads it first.

Is your website affected?

Find out in minutes — free scan, professional report, no credit card.

Start Free Scan