REMAP · BUILD FORENSICS

Put the
sources
back.

A source map usually carries the original, unminified files. This turns them back into a directory tree — and refuses any entry that tries to leave it.

EDIT THE SOURCES ARRAY. WATCH THE GUARD. safeJoin, running here

These are the ordinary entries a bundler writes. The interesting question is what the tool does with an array it did not write.
THE PART THAT MATTERS

The filenames come from
a file you did not write.

{
  "version": 3,
  "sources": [
    "src/app.js",
    "../../../../.ssh/authorized_keys"
  ],
  "sourcesContent": ["…", "…"]
}

A source map is something you fetched from a server you do not control, and its sources array is used to build the filenames it writes. That makes path traversal the entire security surface of a tool like this — and it is not theoretical. Before the guard existed, a Vite/Rollup-style map with plain relative sources wrote a file to its grandparent directory. Webpack's webpack:/// prefix normalises away, which is exactly why casual testing missed it.

$ npx @mrkt_frwd/remap main.js.map --out ./src   REMAP  main.js.map  ────────────────────────────────────  ok   src/components/Header.jsx  ok   src/utils/math.js  --   ../../PWNED.txt         refused — path escapes the output directory   2 file(s) written to ./src, 1 refused

Stripping the .. and writing it somewhere inside the root would also be safe, and it would be a guess about what the map meant. A map that climbs out of its own output directory is either malformed or hostile; deciding which is the caller's business, not this tool's. So it says which entry, and why, and carries on with the rest.

WHAT IT REFUSES

Six ways out, all closed.

the entrywhat a naive join doesremap
../../PWNED.txtwrites to the grandparent of your output directoryrefused and named
webpack:///../../x.jsprefix normalises away, then escapes — the form that hid the bugrefused and named
webpack://../../x.jsthe two-slash variant, which also escapedrefused and named
/etc/passwdleading slash makes join treat it as absolutemade relative to the root
C:\Windows\x.jsdrive letter is absolute on some platformsdrive letter stripped, kept inside
referenced, not embeddedsilently produces nothing, so a partial tree looks completereported as skipped, with the reason

The last row is not a security case and it matters just as much. A reconstruction that quietly omits what the map did not carry is a tree you will trust more than you should.

THE VERB

unpack, and the guard underneath it.

unpackBundleMap plus an output directory. Returns what was written and what was refused, each with a reason.
safeJoinExported on purpose. The boundary is the product, so it is testable on its own.
const { unpackBundle } = require('@mrkt_frwd/remap');

const { written, skipped } = await unpackBundle('main.js.map', './src');
// written: string[]                     paths, relative to ./src
// skipped: { source, reason }[]         refusals and missing content, never silent

A refusal is information, not a crash — the command exits non-zero only when nothing came out at all. You get the files that were safe to write and a list of what was not, which is the shape that lets an agent carry on and report honestly.

SCOPE

Use it on things you have the right to read.

Your own builds, your own bundles, and code you are permitted to inspect. A source map being reachable is not by itself permission to redistribute what is inside it. This is stated on the package, in the README and here, because a tool that reconstructs someone's source should say it once in every place someone meets it.

DOCS

Read it before you run it.

Packagenpm · MIT Agent readinessagents.md All seven enginesthe register