chore(deps): update dependency postcss to v8.5.23 [security] #38
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "renovate/npm-postcss-vulnerability"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
This PR contains the following updates:
8.5.12→8.5.23PostCSS: Path Traversal in Previous Source Map Auto-Loading (sourceMappingURL) leads to Arbitrary .map File Disclosure
GHSA-r28c-9q8g-f849
More information
Details
Vulnerability Details
File:
lib/previous-map.jsLine: 87-98 (
loadFile), 129-144 (loadMap)Root Cause
PostCSS auto-detects a
/*# sourceMappingURL=... */comment inside the CSS text it is asked to parse and, unless the caller explicitly passesmap: false, attempts to load that path from disk as a "previous source map." This happens on everypostcss.parse()/postcss().process()call by default (opt-out, not opt-in).loadMap()builds the candidate path viajoin(dirname(opts.from), annotation), whereannotationis the raw, attacker-controlled string from the CSS comment.path.join()normalizes but does not sandbox..segments, so a../../../prefix walks the resolved path outside the intended directory. Ifopts.fromis not set at all, the annotation is used completely unmodified — an absolute path in the CSS comment is read verbatim.8.5.12 already fixed a strictly worse variant of this (any file, any extension, could be read) by requiring the resolved path to end in
.map(loadFile()). That fix did not address the traversal itself, only the target extension. Since thejoin(dirname(file), map)logic has existed unchanged since PostCSS 8.0.0 (Feb 2020), any file ending in.mapremains readable through this path in the current release (8.5.16).Once loaded,
MapGenerator.isMap()treats the mere presence of a loaded "previous map" as an implicit request to generateresult.map, even when the caller never set themapoption. If the loaded map has asourcesContentfield (common for maps emitted by bundlers/transpilers), that content is merged intoresult.mapand returned to the caller — disclosing the traversed-to file's content to whoever supplied the CSS.Attack Scenario
postcss().process(userCss, { from: '/app/uploads/user123/input.css', to: '/app/uploads/user123/output.css' })— idiomatic usage;mapoption untouched./*# sourceMappingURL=../../../../some/other/app/dist/bundle.js.map */(or an absolute path iffromis unset)..mapfile and folds itssourcesContentintoresult.map.result.map— writes it next to the CSS output or returns it via API (source maps are meant to be consumed by browser devtools, so this is commonly public/served).Impact
Disclosure of the contents of arbitrary
.mapfiles reachable via path traversal (or absolute path whenfromis unset) from the process's filesystem. Affects any application processing CSS it does not fully trust without explicitly passingmap: false. No authentication or user interaction beyond submitting CSS text is required.Vulnerable Code
Recommended Fix
Constrain the resolved path to remain inside the CSS file's own directory instead of relying solely on a filename-extension check:
I've implemented, tested (full existing test suite — 660/660 passing, plus new PoC-based regression checks for both the traversal and legitimate same-directory cases), and can share this fix on request or via a private fork if invited.
Verification
Dynamically confirmed on v8.5.16 (current npm release / repo HEAD) via a standalone Node.js harness against
lib/postcss.js: a "secret".mapfile placed two directories outside a simulated project directory was read via a craftedsourceMappingURLcomment in otherwise-innocuous CSS, with itssourcesContentappearing verbatim inresult.map.toString()— with nomapoption set by the caller. A second harness confirmed the simpler no-fromcase reads an absolute path directly. A third harness confirmedmap: falseis the only current workaround. The attached fix branch closes both vectors while keeping all 660 existing unit tests green.Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:NReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
PostCSS: incomplete fix of GHSA-6g55-p6wh-862q — attacker-controlled sourceMappingURL reads arbitrary .map files when
fromis unsetCVE-2026-69153 / GHSA-fxqj-rqcc-2cmp
More information
Details
Summary
The fix for GHSA-6g55-p6wh-862q added a guard in
lib/previous-map.jsPreviousMap.loadFile()that restricts an attacker-controlledsourceMappingURL(from a CSS comment) to a.mapextension and, for untrusted maps, rejects..traversal and absolute paths. The traversal/absolute rejection is nested insideif (cssFile) { ... }. When PostCSS is invoked without thefromoption,cssFileis falsy and that branch is skipped, leaving only the.mapextension check.PreviousMapis constructed bylib/input.jswheneverpathAvailable && sourceMapAvailable(under Node with source-map available), independent ofopts.from/opts.map(the constructor returns early only foropts.map === false). Sopostcss([]).process(css)on attacker CSS reachesloadFilewithcssFileundefined, and an attacker/*# sourceMappingURL=/abs/path/x.map */(or../-traversing path) is read viareadFileSync. When the file is valid JSON, itssources(filesystem paths) andsourcesContent(source contents) are disclosed in the generated source map.Affected code (v8.5.22 — the release carrying the GHSA-6g55 fix)
Proof of concept (verified on postcss 8.5.22)
Observed output on postcss 8.5.22:
../traversal (nofrom) also succeeds; non-.maptargets (.txt,?x=.map,#.map) are blocked by the.mapcheck. The tested build contains the GHSA-6g55 fix (this.json = JSON.parse(...)inloadMap,consumer()usesthis.json || this.text), so this is a residual of that fix.Impact
Arbitrary
.map-file read (absolute path or../traversal) and disclosure of the target map'ssources(local filesystem paths) andsourcesContent(source) into the generated source map, for any consumer that runs PostCSS on attacker-influenced CSS without afromoption and exposesresult.map(online CSS playgrounds, minify/lint services, string-input build steps). Bounded to files ending in.mapthat parse as JSON.Suggested fix
Apply the traversal/absolute-path rejection to the untrusted map path regardless of whether
cssFileis present (resolve againstprocess.cwd()when there is nocssFile, and reject absolute paths and..escape in all untrusted cases), or refuse to load an untrusted external map when no base file is known.Severity
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:NReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
Release Notes
postcss/postcss (postcss)
v8.5.23Compare Source
v8.5.22Compare Source
v8.5.21Compare Source
v8.5.20Compare Source
v8.5.19Compare Source
v8.5.18Compare Source
v8.5.17Compare Source
v8.5.16Compare Source
v8.5.15Compare Source
v8.5.14Compare Source
v8.5.13Compare Source
Configuration
📅 Schedule: (in timezone Europe/Berlin)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR has been generated by Mend Renovate.
7bd95dcfd6to63d8af8847chore(deps): update dependency postcss to v8.5.18 [security]to chore(deps): update dependency postcss to v8.5.23 [security]View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.Merge
Merge the changes and update on Forgejo.Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.