Vulnerabilities
composer.json vs package.json on Production Origins
July 24, 2022
composer.json vs package.json on production origins is two language manifests that must not sit next to index.html. /package.json donates npm names and versions. /composer.json (and composer.lock) donates PHP packages and constraints — the same inventory class for WordPress plugins, Laravel, and Symfony shops. Neither is a pentest. Both shrink reconnaissance and feed advisory lookups. A private Packagist or npm registry is the public catalog. The marketing CDN is not. RoastMyOpsec probes /package.json with a bounded GET. It does not currently score /composer.json, does not parse lockfiles on the URL scan, and does not install packages.
The practical difference
package.json is Node. composer.json is PHP. Dockerfile is how the image is built. composer.lock and package-lock.json pin versions more tightly than the manifest — still do not serve them from the brochure origin. OSV deepen on a repo you own is a different channel than a GET to the CDN.
A 200 on /composer.json is an information leak even when every version is already on Packagist.
How to choose what to publish
CI: fail if composer.json, composer.lock, package.json, or lockfiles appear in a static marketing artifact. WordPress: the app may live on the same host — still deny those filenames at the CDN for paths that are not the app's private deploy. Keep manifests in the private repo. Staging hostnames that are guessable get the same deny list.
| File | Typical leak | Roast today | Takeaway |
|---|---|---|---|
| /package.json | JS names and versions | Bounded GET | See public package.json vs OSV |
| /composer.json | PHP names and versions | Not scored; deny anyway | Same class |
| /composer.lock | Pinned PHP tree | Not scored | Deny the name |
| /Dockerfile | Image build inventory | Not scored | See Dockerfile vs package.json |
When composer.json still wins (off the public origin)
It wins in a private repo and in CI. Packagist already lists public packages. The brand origin does not need to mirror the file. Pair with phpinfo vs verbose errors if the same host also dumps the runtime.
What the roast can prove
A reachable /package.json can surface. composer.json is not a scored path. Confirm a 404 yourself. Pair with OSV lockfile deepen on a GitHub repo you own.
Common mistakes
The first mistake is deploying the whole Laravel tree to the document root including composer.json.
The second mistake is treating Packagist visibility as a reason the CDN should serve the file.
The third mistake is Disallow: /composer.json and calling it access control.
Free audit the URL you own
RoastMyOpsec is a defensive public-surface roast: headers, cookies, sensitive paths, and more — no exploit payloads. Start with the free audit, then open the vault if the blurred findings look expensive.
Free audit nowFAQ
- Is a public composer.json a vulnerability?
- It is an information leak. Remove it from the public origin. It is not a credential dump by itself.
- Does RoastMyOpsec fetch composer.json?
- Not as a scored path today. It does probe /package.json. Deny Composer filenames at the edge anyway.
- Does composer.lock belong on the CDN?
- No. Keep lockfiles in the private repo. Use OSV on a repo you own, not a marketing URL.
Sources
Related guides
Vulnerabilities
Public package.json vs Lockfile Advisories on ProductionVulnerabilities
Gemfile vs package.json on Production OriginsVulnerabilities
requirements.txt vs package.json on Production Origins