← All checks
SecurityTypical severity: Low

Dependency Manifest Scanner

Detects accessible package.json or composer.json files that reveal your exact dependencies and versions.

What it checks

This scanner checks whether dependency manifest files like package.json or composer.json are publicly accessible on your site. It detects when they are served and reveal the list of libraries you use along with their exact versions.

Why it matters

Knowing your exact dependencies and versions makes it easy for an attacker to look up public vulnerabilities that affect precisely what you use. It is reconnaissance work you save them, and it narrows down where to try attacking your site.

How to fix it

Make sure these manifest files aren't served from the public folder; they normally belong in the project root, not the web directory. Block their access in your server or CDN configuration if your framework exposes them by default.

How this check works

Scanaris requests two files at the root of your domain: /package.json, the manifest of a Node project, and /composer.json, the manifest of a PHP project. For each, it only confirms the finding if it responds 200, the body is not HTML, it is valid JSON and it contains the keys specific to that manifest: dependencies or devDependencies in the first, require or require-dev in the second. That way it does not flag a single-page app that returns its HTML for any path, nor some random JSON that happens to live there. Each file found opens a low-severity finding.

Why it matters

These files contain no secrets, but they contain something almost as useful to an attacker: the exact list of libraries you use and their versions. With that, there is nothing to guess: they cross your list with the database of known vulnerabilities and know which exploit to try and against what. They also reveal the project's internal name, the build and deploy scripts, sometimes the private repository URL and the names of your organisation's internal packages, which are the starting point of dependency confusion attacks. It is low severity because it is information, not access; but it is information you give away for no reason.

Why it happens

Because the web server serves the whole project folder instead of the output folder. A properly deployed Vite, Nuxt or Next.js only publishes dist/, .output/public/ or .next/static/, and there is no package.json there. It shows up when the full repository is uploaded over FTP to public_html, when a static host's root directory points to the project root, or in PHP projects where composer.json sits in the same directory as index.php, which is common in WordPress with Composer and in hand-made installs.

How to fix it

  • Serve only the public folder. On Vercel and Netlify, check the project's "output directory" or "publish directory"; it should be the build folder, not .. In nginx and Apache, root or DocumentRoot points to public/ or dist/.
  • In PHP, move composer.json, composer.lock and vendor/ one level above the public directory if your framework allows it (Laravel and Symfony already do). If you cannot move them, block them:
location ~* /(package|composer)(-lock)?\.json$ { return 404; }
location ~* /(yarn|pnpm-lock)\.(lock|yaml)$ { return 404; }
  • In Apache, a <FilesMatch "^(package|composer)(-lock)?\.json$"> Require all denied </FilesMatch> in the configuration or in .htaccess.
  • Check what else was uploaded. Where there is a public package.json there is usually a .env, a .git/ or a composer.lock next to it; the sensitive files check looks for the serious ones.

Check it is fixed

for p in package.json composer.json package-lock.json composer.lock yarn.lock; do
  printf "%-20s " "$p"; curl -s -o /dev/null -w "%{http_code} %{content_type}\n" "https://yourdomain.com/$p"
done

Expected: 404 or 403. A 200 with text/html is a single-page app's generic HTML and is not a problem; a 200 with application/json is the real file.

Frequently asked questions

My repository is public on GitHub anyway.

Then the manifest is already public and this finding adds no new exposure; you can mark it as accepted. Even so, fixing the server root prevents the next file that lands in the project folder from being published by accident.

What about lockfiles?

This check looks at the two manifests, but package-lock.json, yarn.lock and composer.lock are worse: they list every transitive dependency with its exact version. If the manifest is exposed, the lockfile almost certainly is too; the blocking rule above covers them.

If I do not serve the manifest, can nobody tell which versions I use?

They can infer it by other, slower means: headers, bundle paths, behaviour. What outdated libraries detects in your JavaScript, for instance, anyone can see. Hiding the manifest removes the shortcut; updating the dependencies removes the problem.

Check it on your site

Scan your site free and see if this and 44 other checks pass, with a fix prompt for every issue.

Scan my site free

Related checks