Back to Blog
high SEVERITY9 min read

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

A high-severity path traversal vulnerability in PostCSS versions before 8.5.18 allowed attackers to manipulate `sourceMappingURL` directives to load arbitrary `.map` files from the filesystem, potentially disclosing sensitive source code and build metadata. The fix upgrades PostCSS from 8.5.15 to 8.5.18 in `console/web/package-lock.json`, closing the path traversal vector in the source map auto-loading feature. This change protects applications that process untrusted CSS input through their Post

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 source map auto-loading feature, affecting versions before 8.5.18. When PostCSS processes CSS containing a crafted `sourceMappingURL` comment, it failed to sanitize the file path, allowing traversal sequences like `../` to reach arbitrary `.map` files outside the intended directory. The fix is to upgrade PostCSS to version 8.5.18 or later, which validates and restricts the paths resolved from `sourceMappingURL` directives.

Vulnerability at a Glance

cweCWE-22
fixUpgrade PostCSS from 8.5.15 to 8.5.18, which adds path validation to the source map auto-loading routine
riskArbitrary `.map` file disclosure, potentially exposing original source code and build secrets
languageJavaScript / Node.js
root causePostCSS did not sanitize the file path extracted from `sourceMappingURL` comments before reading it from disk
vulnerabilityPath Traversal via sourceMappingURL in PostCSS source map auto-loading

How Path Traversal Happens in PostCSS Source Map Auto-Loading and How to Fix It


Vulnerability at a Glance

Field Detail
Vulnerability Path Traversal via sourceMappingURL auto-loading
CWE CWE-22 – Improper Limitation of a Pathname to a Restricted Directory
Language JavaScript / Node.js
Risk Arbitrary .map file disclosure, potential source code exposure
Root Cause Unsanitized file path from CSS comment used in file I/O
Fix Upgrade PostCSS to 8.5.18

Summary

A high-severity path traversal vulnerability in PostCSS versions before 8.5.18 allowed attackers to manipulate sourceMappingURL directives to load arbitrary .map files from the filesystem, potentially disclosing sensitive source code and build metadata. The fix upgrades PostCSS from 8.5.15 to 8.5.18 in console/web/package-lock.json, closing the path traversal vector in the source map auto-loading feature. This change protects applications that process untrusted CSS input through their PostCSS pipeline.


Direct Answer

GHSA-r28c-9q8g-f849 is a high-severity path traversal vulnerability (CWE-22) in PostCSS's source map auto-loading feature, affecting versions before 8.5.18. When PostCSS processes CSS containing a crafted sourceMappingURL comment, it failed to sanitize the file path, allowing traversal sequences like ../ to reach arbitrary .map files outside the intended directory. The fix is to upgrade PostCSS to version 8.5.18 or later, which validates and restricts the paths resolved from sourceMappingURL directives.


Introduction

The console/web/package-lock.json file locks every JavaScript dependency used by the web console's frontend build pipeline — including PostCSS, the ubiquitous CSS transformation tool. PostCSS has a convenient feature: when it encounters a /*# sourceMappingURL=... */ comment in a CSS file, it automatically reads the referenced .map file to preserve source location information across transformations. That convenience became a security liability.

In PostCSS 8.5.15 (the version pinned before this fix), the path extracted from sourceMappingURL was passed to the filesystem reader without sufficient sanitization. An attacker who could influence the CSS processed by PostCSS — through a malicious npm package, a crafted stylesheet uploaded to the application, or a compromised build artifact — could embed a traversal sequence in that comment and cause PostCSS to read .map files far outside the intended build directory.


The Vulnerability Explained

What Is Source Map Auto-Loading?

Source maps are JSON files (.map) that map minified or transformed code back to the original source. A CSS file signals its source map with a trailing comment:

/* Legitimate usage */
.button { color: red; }
/*# sourceMappingURL=styles.css.map */

PostCSS reads this comment and, when auto-loading is enabled, opens styles.css.map relative to the CSS file's location. The problem is in how that relative path is resolved.

The Vulnerable Pattern

Before the fix (PostCSS 8.5.15), the source map loader extracted the path string from the comment and used it to construct a filesystem path without verifying that the resolved absolute path stayed within the project's expected directory. A malicious CSS file could contain:

.button { color: red; }
/*# sourceMappingURL=../../../../etc/app/secrets.js.map */

Or, in a more targeted attack against a Node.js build server:

/*# sourceMappingURL=../../../.env.map */
/*# sourceMappingURL=../../../../home/ci/.ssh/id_rsa.map */

Because the path was not canonicalized and checked against a safe base directory before the file read, PostCSS would dutifully attempt to open whatever path was constructed — including paths that escape the build directory entirely.

Real-World Impact for This Application

This vulnerability lives in console/web/package-lock.json, meaning it affects the frontend build pipeline of the web console. Build pipelines are high-value targets:

  • They run with filesystem access to source code, environment files, and CI secrets.
  • They often process CSS from third-party packages, which could be compromised in a supply chain attack.
  • A .map file disclosure can reveal original, unminified TypeScript or JavaScript source, exposing business logic, API endpoint structures, and internal variable names that would otherwise be hidden in production bundles.

An attacker exploiting this in a CI/CD environment could exfiltrate .map files containing reconstructed source trees, then use that knowledge to craft more precise follow-on attacks against the running application.


The Fix

What Changed

The fix is a targeted version bump in console/web/package-lock.json (and the corresponding package.json), upgrading PostCSS from 8.5.15 to 8.5.18:

 "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==",

The integrity hash change is critical — it confirms that the downloaded package is the new, patched version and not a substituted artifact. The lock file's hash pinning is what makes package-lock.json a security control, not just a convenience.

What PostCSS 8.5.18 Actually Fixes

PostCSS 8.5.18 introduces path validation in the source map auto-loading routine. Before resolving and reading a .map file referenced by sourceMappingURL, the patched version:

  1. Resolves the full absolute path of the referenced file using path.resolve().
  2. Checks that the resolved path is contained within the expected base directory (the directory of the CSS file being processed).
  3. Rejects paths that escape the base directory — traversal sequences like ../ that would land outside the safe zone are blocked before any file I/O occurs.

This is the standard defense against path traversal: normalize first, then check the boundary, then act.

Before vs. After (Conceptual)

Before (8.5.15) — vulnerable pattern:

// Simplified illustration of the vulnerable behavior
const mapPath = extractSourceMappingURL(css); // e.g., "../../../../secrets.js.map"
const resolvedPath = path.resolve(cssDir, mapPath); // escapes cssDir!
const mapContent = fs.readFileSync(resolvedPath);   // reads arbitrary file

After (8.5.18) — patched pattern:

// Simplified illustration of the patched behavior
const mapPath = extractSourceMappingURL(css);
const resolvedPath = path.resolve(cssDir, mapPath);
if (!resolvedPath.startsWith(cssDir + path.sep)) {
  throw new Error('Source map path escapes the CSS file directory');
}
const mapContent = fs.readFileSync(resolvedPath); // safe

The two-line change — resolve, then verify containment — is the entire difference between a path traversal vulnerability and a safe file read.


Prevention & Best Practices

1. Always Validate Paths Before File I/O

Any time a file path is derived from user-controlled input (including CSS comments, JSON fields, or URL parameters), apply the resolve-then-check pattern:

const safePath = path.resolve(baseDir, userInput);
if (!safePath.startsWith(path.resolve(baseDir) + path.sep)) {
  throw new Error('Path traversal detected');
}

2. Pin Dependencies with Integrity Hashes

The integrity field in package-lock.json is not decoration — it cryptographically binds a package version to its content. Always commit your lock file and use npm ci (not npm install) in CI pipelines to enforce the pinned versions and hashes.

3. Run Dependency Scanners in CI

Trivy detected this vulnerability automatically by matching the PostCSS version against its advisory database. Integrate a scanner like Trivy, npm audit, or Snyk into your CI pipeline so vulnerable versions are caught before they reach production builds.

4. Treat Build Pipeline Dependencies as Attack Surface

Build tools process files from many sources — your own code, transitive npm dependencies, and potentially user-uploaded assets. A path traversal in a build tool can be just as dangerous as one in a web server, because build environments often have access to secrets, SSH keys, and internal services.

5. Apply the Principle of Least Privilege to Build Environments

Even if a path traversal succeeds, its impact is limited if the build process runs with minimal filesystem permissions. Use containerized builds with read-only mounts for directories that don't need to be written, and avoid storing long-lived secrets in build worker filesystems.

Relevant Standards


Key Takeaways

  • sourceMappingURL is user-influenced input in any pipeline that processes third-party CSS — treat it with the same skepticism as a query parameter or uploaded filename.
  • PostCSS 8.5.15 and earlier should not be used in any build pipeline that processes CSS from untrusted sources — the path traversal in source map auto-loading is exploitable with a single crafted comment.
  • The integrity hash in package-lock.json changed from sha512-FfR8... to sha512-xdB1... — always verify that dependency upgrades produce a new, valid hash, not just a version number change.
  • Build-time vulnerabilities can be as severe as runtime ones — a .map file disclosure during CI can expose reconstructed source code that attackers use to plan runtime attacks.
  • Upgrading PostCSS to 8.5.18 is a non-breaking change — the fix adds a safety check without altering PostCSS's public API or transformation behavior, so existing tests continue to pass.

How Orbis AppSec Detected This

  • Source: The sourceMappingURL value embedded in a CSS file processed by PostCSS — attacker-controlled string that becomes a filesystem path.
  • Sink: PostCSS's internal source map auto-loading routine in node_modules/postcss, which calls fs.readFileSync() (or equivalent) with the unsanitized path derived from the CSS comment.
  • Missing control: No canonicalization or boundary check was applied to the resolved path before the file read — traversal sequences (../) were not stripped or rejected.
  • CWE: CWE-22 – Improper Limitation of a Pathname to a Restricted Directory ("Path Traversal").
  • Fix: Upgraded PostCSS from 8.5.15 to 8.5.18 in console/web/package-lock.json, replacing the vulnerable source map path resolution with a version that validates the resolved path stays within the expected directory.

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

Path traversal vulnerabilities have a deceptive simplicity: a few ../ characters in the right place can turn a helpful feature into a file disclosure primitive. PostCSS's sourceMappingURL auto-loading is a perfect illustration — a genuinely useful feature that, without proper path validation, became a vector for reading arbitrary .map files from the build host's filesystem.

The fix is equally simple in concept: resolve the full path, check it's inside the safe zone, then proceed. PostCSS 8.5.18 applies exactly that pattern, and upgrading the pinned version in console/web/package-lock.json from 8.5.15 to 8.5.18 closes the vulnerability with zero functional impact on the build pipeline.

For developers: the lesson here extends beyond PostCSS. Whenever your code reads a file whose path is derived from any external input — a CSS comment, a JSON field, a URL parameter — apply the resolve-then-verify pattern before touching the filesystem. It's a one-time investment that permanently eliminates an entire class of vulnerability in that code path.


References

Frequently Asked Questions

What is a path traversal vulnerability in PostCSS?

It is a flaw where PostCSS reads the file path from a CSS `sourceMappingURL` comment without sanitizing it, allowing sequences like `../../` to escape the intended directory and read arbitrary `.map` files.

How do you prevent path traversal in Node.js CSS processing?

Validate and normalize all file paths derived from user-controlled input using `path.resolve()` combined with a check that the resolved path starts with the expected base directory before any file I/O.

What CWE is path traversal?

Path traversal is classified as CWE-22 (Improper Limitation of a Pathname to a Restricted Directory).

Is upgrading PostCSS enough to prevent this path traversal?

Upgrading to PostCSS 8.5.18 or later fixes the specific `sourceMappingURL` path traversal, but you should also audit any other places in your pipeline where user-controlled strings are used to construct file paths.

Can static analysis detect this path traversal vulnerability?

Yes — tools like Trivy (which flagged this exact issue as GHSA-r28c-9q8g-f849) and Semgrep can detect known vulnerable package versions and unsafe path construction patterns automatically.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #107

Related Articles

critical

How Archive Path Traversal Happens in Node.js and How to Fix It

CVE-2026-53486 is a critical path traversal vulnerability in the Decompress library, where crafted archive entries can write files and symbolic links outside the intended extraction directory. This vulnerability was transitively introduced through `@vitest/browser` and related packages pinned at version 4.1.5, and was resolved by upgrading to 4.1.6 and 5.0.0-beta.3. Left unpatched, an attacker who controls an archive file processed by any downstream consumer of this dependency chain could overwr

high

How Path Traversal happens in Python Flask apps and how to fix it

A path traversal vulnerability in `WAVE2SCORE/app.py` allowed attackers to supply a crafted file path that escaped the application's intended working directory, potentially enabling access to arbitrary files on the server. The fix resolves the absolute path and validates it against the expected `WORK_DIR` boundary before any processing occurs. This kind of boundary check is a critical safeguard in any application that processes user-supplied file paths.

high

How Arbitrary File Read happens in Python LangSmith SDK and how to fix it

A high-severity arbitrary server-side file read vulnerability (GHSA-f4xh-w4cj-qxq8) was discovered in LangSmith SDK's `TracingMiddleware`, affecting versions prior to 0.8.18. An attacker able to influence tracing requests could potentially read arbitrary files from the server's filesystem. Upgrading from version 0.8.15 to 0.8.18 in `poetry.lock` and `pyproject.toml` closes the attack surface entirely.

critical

How Command Injection happens in Python PopClip Extensions and how to fix it

A critical command injection vulnerability was discovered in `contrib/Klipz.popclipext/Klipz.py`, where user-controlled clipboard content was concatenated directly into shell commands executed via `osascript`. The fix replaces unsafe string concatenation with `subprocess` and proper argument lists, and replaces the unsafe `pickle` serialization with `json` to eliminate a secondary deserialization risk. Together, these changes close two distinct attack surfaces in a single file.

critical

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

A path traversal vulnerability in `skills/baoyu-design/agents/import-design-system.mjs` allowed attackers to escape the intended design system directory by supplying absolute paths, bypassing a guard that only checked for `..` prefixes. The fix adds an `isAbsolute()` check alongside the existing relative-path guard, closing the bypass with a single targeted change. This matters because the `dsDir` argument is user-controlled, meaning any caller of the script could redirect file operations to sen

high

How Denial of Service via infinite loop happens in Node.js dependencies and how to fix it

A high-severity Denial of Service vulnerability in the nanoid package (CVE-2026-67213) was discovered in the project's dependency tree, where crafted input could trigger an infinite loop during random ID generation. The fix upgrades nanoid from 3.3.17 to 3.3.18 and adds an npm override to ensure all transitive dependencies use the patched version.