Skip to content

Repository files navigation

unmapped

Either your source maps don't work, or they're publishing your source code. Most projects don't know which.

CI npm license zero dependencies


The problem

Source maps fail in two opposite directions, and both are completely silent.

They don't work. A .map that never made it into the deploy. A mappings string that is empty. A reference the CDN stripped. Your error tracker shows a minified stack trace, and you find out during an incident — which is the worst possible moment to discover a debugging tool is broken.

If URLs or release names don't match, symbolication fails silently — Sentry shows the minified stack with no warning.Sentry's own troubleshooting guide

They work too well. A .map with sourcesContent embedded, served from your CDN, is your entire original source — comments, TODOs, internal naming — downloadable by anyone who opens the network tab. This is not a bug in your build. It is exactly what sourcemap: true does, which is why nothing warns about it.

Nothing in a build, a deploy or an error tracker reports either one.

unmapped reads the directory you actually deploy and tells you which of the two you are in.

What it looks like

$ npm run build && npx unmapped dist

4 minified bundles · 0 will symbolicate
                                   0%

  no original source embedded in any map
  2 map file(s) · treated as publicly served

error   empty-mappings vendor.js.map
  1 source map has an empty `mappings` string.
  so: The file exists, uploads succeed, and every dashboard reports the map as
      present — while symbolication resolves nothing. This is the most
      misleading state of the three, because everything says it is working.

error   missing-source-map app.js
  1 bundle points at a source map that is not in this output.
  so: The browser and your error tracker both request that file and get a 404.

And the other direction:

$ npx unmapped dist

1 minified bundle · 1 will symbolicate
  ██████████████████████████████ 100%

  249 B of original source embedded in 1 map(s)

error   source-published app.js.map
  249 B of original source is embedded in 1 published source map.
  so: Anyone who opens the network tab can download your unminified source,
      including comments and any secret that was ever written into a client
      file. Search engines index .map files; so do scanners.

Note the second one symbolicates perfectly. That is the problem.

Install

npx unmapped dist

Or as a dev dependency:

npm install --save-dev unmapped

Requires Node 20.10 or newer. Zero dependencies.

Use it

Run it on the directory you actually deploy, after the build:

npm run build && npx unmapped dist
unmapped dist --private     # not served publicly; embedded source is expected
unmapped dist --verbose     # list every bundle and map
unmapped dist --json        # machine-readable
unmapped dist --fail-on warning

In CI:

- run: npm ci
- run: npm run build
- run: npx unmapped dist

Exit codes: 0 clean, 1 findings at or above the threshold, 2 bad usage.

The one thing it asks you

Is this directory served publicly?

unmapped cannot tell by looking. A dist bundled into a server image and a dist uploaded to a CDN are the same files. So --private is an explicit switch, and it flips the disclosure rules from findings into information.

Default is public, because that is the common case for a directory someone points this at — and because the safe default for a disclosure check is to report rather than stay quiet.

The rules

Rule Severity Meaning
missing-source-map error a bundle points at a .map that is not in the output — a 404
empty-mappings error the map exists and maps nothing
unreadable-source-map error the .map is not valid source-map JSON
source-published error* original source is embedded in a publicly served map
no-source-map warning a minified bundle with no reference at all
build-path-leak warning* sources contain absolute paths from the build machine
inline-source-map warning* the map is embedded in the bundle every visitor downloads
orphan-source-map info a .map nothing points at, still deployed
no-embedded-source info private build with no sourcesContent to show

* downgraded or dropped with --private.

Details and worked examples: docs/rules.md.

Try it

Three fixtures, one for each state a build can be in:

git clone https://github.com/hamodywe/unmapped && cd unmapped
npm install
node src/cli.ts examples/broken-build   # four ways to fail
node src/cli.ts examples/leaky-build    # works perfectly, leaks everything
node src/cli.ts examples/good-build     # silence

Design notes

It reads output, not configuration. A bundler config that says sourcemap: true proves nothing — the map that reaches the deploy can still be empty, absent, or stripped by a CDN's auto-minify. Only the emitted files settle it.

The report never reproduces the source it is warning about. A finding that pasted your leaked code into a build log would be a strange way to warn you about leaked code. There is a test for it.

A denominator of zero is not 100%. A build with nothing minified has no symbolication question to answer, and the report says so rather than drawing a full green bar.

Zero dependencies, offline, deterministic. No network, no error-tracker API, no token. Two runs over the same directory produce identical output.

Limitations

  • It does not verify mappings resolve to real positions. An empty-mappings map is caught; a map whose segments point at the wrong lines is not. Checking that would need the original sources, which for a public build are exactly what should not be there.
  • It does not check your error tracker. Whether a map was uploaded to Sentry under the right release is a question only Sentry can answer. unmapped checks the half you control locally, which is where most of the failures are.
  • Minification detection is a heuristic based on line length. An unminified bundle is not reported as needing a map, because its stack traces are readable anyway.
  • Remote map references are not fetched. A sourceMappingURL pointing at another host is reported as remote and left alone; following it would mean network access.
  • It cannot know your deploy strips .map files. If your CDN or server config excludes them, pass --private.

FAQ

We upload maps to Sentry and delete them from the deploy. Will this complain? It will report no-source-map at warning level for the minified bundles, since from the directory's point of view they have nothing to symbolicate against. That is the correct reading of what was deployed. Use --fail-on error — the default — and it will not fail your build.

Is publishing source maps actually bad? Not always. Plenty of projects do it deliberately, and open-source front ends have nothing to hide. The point is that it should be a decision. --private records that you made one.

Does it modify anything? No. It only reads.

Contributing

The most useful issue is a wrong verdict — a build reported as broken that symbolicates fine, or a clean report on a build that does not. See CONTRIBUTING.md.

Licence

MIT © hamodywe

References

About

Checks that the source maps you shipped actually symbolicate — and that they are not quietly publishing your source code.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages