Your Package Manifests Are a Supply-Chain Gift to Attackers
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==versionpins. 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.sumeven includes transitive dependencies, so the inventory is more complete than what your developers consciously chose.Gemfile/Gemfile.lock(Ruby) — the lockfile'sGEMspecs 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:versioncoordinates for every dependency, includingdependencyManagemententries. 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) andpackages.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:
- Stop shipping manifests. Deploy built artifacts (vendored
vendor/with autoloader, compiled binaries,distfolders) — not the project directory with itspackage.json,go.sum, andpom.xmlintact. - 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. - 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.