← All guides
Critical Reported as: Sensitive file exposure / source map accessible

Exposed .env files and source maps

Two ways a deploy publishes things it never meant to: config files served as static assets, and source maps shipped next to production bundles.

Both of these are deployment accidents rather than code defects, which is why they survive code review and show up in scans.

Sensitive file exposure

SecScan probes roughly 75 paths for files that should never be publicly readable — .env and its variants, .git/config, docker-compose.yml, .htpasswd, Spring Boot actuator endpoints, /WEB-INF/ internals, IDE configs, admin panels and server status pages.

A .env hit is graded Critical because of what the file is: database URLs, API keys, signing secrets, SMTP credentials. There is no partial version of this finding — if the file is served, everything in it is public and must be rotated.

.git/config is the same class of problem one step removed. A readable .git directory frequently means the whole repository is reconstructible, including every secret ever committed and later removed. Deleted-from-HEAD is not deleted from history.

Why the results are trustworthy

The obvious way to write this check produces constant false positives. Any site whose router answers every path with the app shell — SPAs, multi-tenant platforms like GitHub or npm — returns 200 for /.env and everything else you ask for.

SecScan fingerprints that behaviour first. It requests a random path that cannot exist, records the response, and suppresses any probe hit whose body matches that same shell. Findings that survive are files whose content is actually different from the catch-all.

A few paths get extra confirmation on top:

  • /phpmyadmin and /adminer require a real login-form marker such as a pma_username field, not just the tool’s name appearing somewhere on the page.
  • /swagger.json, /openapi.json and /graphql are handled by dedicated modules that require a parseable schema or a real introspection response, rather than matching on a keyword.

Fixing it

Serving your application directory as static files is the root cause. Deny dotfiles explicitly and stop deploying what you do not serve:

location ~ /\.(?!well-known/) {
  deny all;
  return 404;
}

Then confirm the file is not in the image at all — add .env, .git and docker-compose.yml to .dockerignore. A deny rule that a future config change removes is one layer; an artifact that never contained the file is the other.

Source map exposure

SecScan checks each JavaScript bundle (up to eight files) two ways: for a .map file directly alongside it, and for a sourceMappingURL comment pointing at a reachable map. Either one is graded High.

A source map reverses minification completely. An attacker gets your original module structure, variable names, comments, commented-out code, and — routinely — internal API routes and admin endpoints that the minified bundle only hinted at.

This is not the same as saying your front-end code is secret. It is not: anyone can read the bundle. The point is that a source map turns hours of reading minified code into a git-like browse of your actual source, and that difference in effort is most of what deters casual probing.

Fixing it

Turn maps off for production builds, or generate them and upload them to your error tracker instead of the CDN. Both Sentry and Rollbar accept maps directly, which gets you readable stack traces without publishing anything.

// vite.config.js
export default { build: { sourcemap: false } };

If your build already uploads maps to a monitoring service, make sure the upload step also deletes them from dist/ before the deploy step copies it.

Rotate first, fix second. If a .env was reachable, the credentials in it should be treated as compromised regardless of whether you see evidence of access. Access logs will not show you what a crawler cached months ago.

Last reviewed 2026-09-07