Back to Blog
high SEVERITY8 min read

How Path Traversal happens in PostCSS Source Map Auto-Loading and how to fix it

A high-severity path traversal vulnerability (GHSA-r28c-9q8g-f849) in PostCSS versions prior to 8.5.18 allowed attackers to abuse the `sourceMappingURL` comment auto-loading mechanism to read arbitrary `.map` files outside the intended directory. The fix upgrades PostCSS from 8.5.15 to 8.5.18 in `frontend/package-lock.json` and pins the version via an `overrides` block in `frontend/package.json`. This closes a file disclosure primitive that, while not independently exploitable in all configurati

O
By Orbis AppSec
Published August 26, 2026Reviewed August 26, 2026

Answer Summary

GHSA-r28c-9q8g-f849 is a high-severity path traversal vulnerability (CWE-22) in PostCSS's previous source map auto-loading feature, where a crafted `sourceMappingURL` comment could trick PostCSS into reading `.map` files outside the intended directory. It affects PostCSS versions before 8.5.18 and is fixed by upgrading to 8.5.18 and adding an `overrides` pin in `package.json` to ensure no transitive dependency re-introduces an older version.

Vulnerability at a Glance

cweCWE-22
fixUpgrade PostCSS from 8.5.15 to 8.5.18 and pin the version with a package.json overrides block
riskArbitrary .map file disclosure from the server filesystem
languageJavaScript / Node.js
root causeInsufficient sanitization of the file path derived from `sourceMappingURL` comments before opening the referenced source map file
vulnerabilityPath Traversal in PostCSS sourceMappingURL Auto-Loading

Introduction

The frontend/package-lock.json file in this project locks down every Node.js dependency used by the frontend build pipeline — including PostCSS, the widely-used CSS transformation library. A routine dependency scan by Trivy flagged that the locked version of PostCSS, 8.5.15, contained a high-severity path traversal flaw tracked as GHSA-r28c-9q8g-f849. The vulnerable code path lives inside PostCSS's previous source map auto-loading feature: the logic that reads a sourceMappingURL comment from an existing CSS file and automatically loads the referenced .map file before applying further transformations.

When PostCSS encounters a line like:

/*# sourceMappingURL=../../../etc/passwd.map */

the vulnerable versions did not adequately sanitize the embedded path before attempting to open it on disk. That single oversight is enough to turn a CSS processing step into an arbitrary file-read primitive.


The Vulnerability Explained

What is sourceMappingURL Auto-Loading?

When PostCSS processes a CSS file that was previously compiled (e.g., by Sass or another PostCSS run), it can optionally read the existing source map to preserve accurate source positions. It does this by parsing the trailing comment:

/*# sourceMappingURL=styles.css.map */

PostCSS then constructs a filesystem path by joining the directory of the CSS file with the value of sourceMappingURL and opens that file. The vulnerability is in the path-construction step: versions before 8.5.18 did not strip or reject path traversal sequences (../, URL-encoded variants, etc.) before resolving the final path.

The Vulnerable Pattern

Conceptually, the vulnerable logic resembled:

// Simplified representation of the vulnerable behavior in postcss < 8.5.18
const mapPath = path.resolve(cssFileDir, sourceMappingURLValue);
const mapContent = fs.readFileSync(mapPath, 'utf8');  // ← no traversal check

If sourceMappingURLValue is ../../../../etc/app-secrets.map, then mapPath resolves to a location entirely outside the project directory. PostCSS would dutifully read that file and expose its contents through the source map data structure — available to any subsequent plugin or caller that inspects the parsed result.

Attack Scenario

Consider a build server or CI pipeline that:

  1. Accepts user-submitted CSS files as part of a theme-customization feature.
  2. Runs those files through a PostCSS pipeline to apply vendor prefixes or other transforms.
  3. Returns the processed CSS (or logs/exposes the PostCSS result object) back to the requester.

An attacker submits a CSS file containing:

.evil { color: red; }
/*# sourceMappingURL=../../../config/database.json.map */

PostCSS (≤ 8.5.15) resolves the path, reads config/database.json.map (or any other .map-suffixed file the process has read access to), and embeds its contents in the internal map property of the result. If the application forwards that data — in an error message, a debug endpoint, or a compiled asset — the attacker receives the file contents.

Even in scenarios where the output is not directly returned, this constitutes an exploit primitive: a reliable file-read gadget that automated exploit-chaining tools can combine with other weaknesses (e.g., a separate information-disclosure bug) to achieve meaningful impact.

Real-World Impact for This Application

The affected file is frontend/package-lock.json, meaning PostCSS is part of the frontend build toolchain (Vite, based on the vite devDependency in package.json). In a typical CI/CD setup:

  • The build server processes CSS files from the repository.
  • If an attacker can influence the CSS content (e.g., via a pull request, a compromised upstream package, or a misconfigured artifact pipeline), they could trigger the traversal during the build step.
  • Sensitive files readable by the build process — credentials, .env files, internal configs — become potential targets.

The Fix

What Changed

The fix touches two files:

1. frontend/package-lock.json — the direct version pin:

 "node_modules/postcss": {
-  "version": "8.5.15",
-  "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
-  "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
+  "version": "8.5.18",
+  "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.18.tgz",
+  "integrity": "sha512-xdB1oSLHbz1vRWgCDalrCqEFTWzFlhqFC5tIHLMOSUIjhm3XXQ1qrFy8S/ESr1JYRRXqM3c1QFiMZUJdUTqyMQ==",

This replaces the vulnerable 8.5.15 artifact (and its SHA-512 integrity hash) with the patched 8.5.18 artifact. The new integrity hash ensures npm verifies the exact bytes downloaded from the registry, preventing substitution attacks.

2. frontend/package.json — the overrides pin:

+  "overrides": {
+    "postcss": "8.5.18"
+  }

This is the critical second layer of defense. Without it, a transitive dependency that declares "postcss": "^8.5.0" could cause npm to resolve a version anywhere in the 8.5.x range — potentially re-introducing 8.5.15 if the lockfile is regenerated or if a dependency update pulls in a new resolution. The overrides block forces every package in the dependency tree to use exactly 8.5.18, regardless of what their individual package.json files request.

How the Fix Closes the Vulnerability

PostCSS 8.5.18 tightens the path validation inside its source map loading code. The patched version rejects or normalizes paths that contain traversal sequences before constructing the final filesystem path, ensuring that the resolved file always remains within the expected directory boundary. Valid, well-formed sourceMappingURL values (e.g., styles.css.map or maps/styles.css.map) continue to work without any behavioral change — only malicious or malformed traversal paths are rejected.


Key Takeaways

  • PostCSS's sourceMappingURL parser was the attack surface — not generic CSS processing. Any pipeline that auto-loads previous source maps from untrusted CSS input was exposed.
  • Upgrading the lockfile is not enough on its own — the overrides block in package.json is what prevents transitive dependencies from silently re-introducing the vulnerable 8.5.15 build.
  • Build-time dependencies carry real riskpostcss is a devDependency, but it runs on the build server with access to the project filesystem, making file disclosure a credible threat.
  • Integrity hashes matter — the new integrity field in package-lock.json cryptographically binds the resolved package to the exact patched artifact, preventing registry substitution.
  • Exploit primitives deserve proactive removal — even if the traversal is not directly exploitable in your current configuration, it is a reliable gadget that automated exploit-chaining tools can leverage alongside other weaknesses.

How Orbis AppSec Detected This

  • Source: The sourceMappingURL value embedded in a CSS file processed by PostCSS — externally controllable if user-supplied or third-party CSS is accepted.
  • Sink: PostCSS's internal source map auto-loading logic, which calls fs.readFileSync (or equivalent) on a path derived from the sourceMappingURL comment without adequate traversal sanitization — present in node_modules/postcss at version 8.5.15 as locked in frontend/package-lock.json.
  • Missing control: No path normalization or boundary check to ensure the resolved .map file path remained within the project or CSS file directory.
  • CWE: CWE-22 — Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').
  • Fix: PostCSS was upgraded from 8.5.15 to 8.5.18 in frontend/package-lock.json, and an overrides block was added to frontend/package.json to pin the version across all transitive dependencies.

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.


Conclusion

GHSA-r28c-9q8g-f849 is a sharp reminder that CSS processing is not a passive, read-only operation. PostCSS's source map auto-loading feature — a convenience for preserving accurate source positions across multi-step builds — became an arbitrary file-read primitive when path traversal sequences in sourceMappingURL comments were not sanitized. The fix is precise: upgrade to PostCSS 8.5.18, which tightens path validation, and lock that version in place with an overrides block so no transitive dependency can quietly pull the vulnerability back in.

For teams running PostCSS in their build pipelines — which is nearly every modern frontend project using Vite, webpack, or Create React App — this upgrade is straightforward and carries zero risk of breaking valid CSS workflows. The only inputs rejected by the patch are malformed, traversal-containing paths that should never have been accepted in the first place.

Audit your package-lock.json today, add SCA scanning to your CI pipeline, and treat your build toolchain with the same security rigor you apply to your production runtime.


Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #463

Related Articles

critical

How path traversal happens in PHP virtual filesystem adapters and how to fix it

A critical path traversal flaw in `VirtualAdapter.php`'s `resolveMount()` method allowed attackers to escape mounted directory boundaries using sequences like `../../../etc/passwd`. The fix introduces `PathPolicy::normalizeRelative()` to sanitize the remaining path segment before it ever reaches the underlying storage adapter.

high

How Path Traversal Happens in TensorFlow's Data Service and How to Fix It

TensorFlow's data service dispatcher validated dataset IDs against forward-slash traversal attacks but overlooked backslash characters on non-Windows platforms, allowing attackers to escape the root directory. A targeted fix adds explicit backslash validation across all platforms, closing a high-severity path traversal vulnerability in the snapshot management system.

high

How Trust-Prefix Bypass via Path Traversal Happens in Python Copier and How to Fix It

CVE-2026-53951 is a high-severity path traversal vulnerability in Copier 9.15.0 that allowed attackers to bypass trust-prefix checks and execute tasks without user confirmation. Upgrading to Copier 9.15.2 eliminates this attack vector by properly validating file paths before task execution.

critical

How Path Traversal in basic-ftp Leads to File Overwrite Attacks and How to Fix It

CVE-2026-27699 is a critical path traversal vulnerability in basic-ftp versions before 5.3.1 that allows attackers to overwrite arbitrary files on the system by crafting malicious file paths. This vulnerability was fixed by upgrading basic-ftp and enforcing strict version constraints across dependent packages. Understanding this attack and its mitigation is essential for developers using FTP libraries in production environments.

critical

How Command Injection Vulnerabilities Happen in Python Subprocess Calls and How to Fix Them

A critical command injection vulnerability was discovered in `src/unused/server/fft.py` where external binaries like `oggenc` and `cocoa_text` were executed with file path parameters that could be manipulated by user input. Although `shell=False` was used, the lack of input validation allowed attackers to potentially trigger processing of arbitrary files or cause denial of service. This fix implements proper path validation to prevent exploitation.

critical

How Path Traversal happens in Node.js Express servers and how to fix it

A path traversal vulnerability in `src/server.js` allowed attackers to escape the intended wiki directory by sending encoded traversal sequences through the `/api/pages/:slug(*)` wildcard endpoint. The flawed `startsWith` boundary check could be bypassed after `decodeURIComponent` processing, potentially exposing arbitrary files on the server. The fix replaces the inline filesystem logic with a dedicated `readWikiPage()` function that enforces proper path validation.