PostCSS Path Traversal via sourceMappingURL Leads to Arbitrary .map File Disclosure
PostCSS blindly follows attacker-controlled sourceMappingURL paths in CSS comments, allowing any .map file on the server's filesystem to be read and leaked through the generated source map output.

The problem
PostCSS auto-loads a previous source map whenever it finds a /*# sourceMappingURL=... */ comment in CSS it parses. This happens by default on every postcss.parse() or postcss().process() call unless the caller explicitly passes map: false.
The path in that comment is attacker-controlled. loadMap() builds the final path with join(dirname(opts.from), annotation), and path.join() normalizes but does not sandbox .. segments. Any .map file reachable via traversal (or any absolute path when opts.from is unset) can be read.
Its contents then surface in result.map, which most pipelines write to disk or serve publicly.
Proof of concept
A working proof-of-concept for this issue in postcss, with the exact payload below.
/* malicious.css submitted by attacker to a service that runs postcss().process(userCss, { from: '/app/uploads/user123/input.css' }) */
/*# sourceMappingURL=../../../../etc/app/dist/bundle.js.map */The root cause is that loadFile() in lib/previous-map.js only checked that the resolved path ended in .map (the partial fix from 8.5.12), but never verified the path stayed inside the CSS file's own directory. path.join(dirname(file), annotation) collapses .. segments without any boundary enforcement, so a crafted annotation walks freely up the tree.
The patch in 8.5.18 (commit 95663d3) adds a resolve()-based sandbox: it resolves both the CSS file's directory and the candidate map path to absolute strings, then rejects the load if the resolved path does not equal the root directory or start with root + sep.
This is a classic directory-escape fix (CWE-22). When opts.from is unset, the patch also returns early rather than reading an absolute path verbatim.
The fix
Upgrade postcss to 8.5.18 or later. If upgrading immediately is not possible, pass map: false in every postcss().process() call that handles untrusted CSS. Do not rely on the .map extension check alone as a security boundary.
Related research
- high · 7.5CVE-2026-45623CVE-2026-45623: PostCSS Arbitrary File Read via sourceMappingURL Path Traversal
- highCVE-2026-55607CVE-2026-55607: @anthropic-ai/claude-code Sandbox Escape via Git Worktree Path Confusion
- high · 7.5CVE-2026-15074CVE-2026-15074: @fastify/static Route Guard Bypass via Dot-Dot Path Traversal
- highn8n Git Node fetch/pull/pushTags Operations Bypass Sandbox Path Restriction