claude-faf-mcp Arbitrary File Read/Write via Unconfined Path Argument
An MCP tool in claude-faf-mcp accepts a caller-supplied file path and passes it straight to the filesystem with no directory boundary, letting any MCP client or prompt-injected agent read SSH keys…

The problem
Every tool that calls the shared getProjectPath() helper, plus the general-purpose faf_read and faf_write tools, accepted a caller-controlled path argument and passed it through ~-expansion and path.resolve() directly into a filesystem read or write.
No allowlist, no confinement to the project root, and no symlink canonicalization were applied.
Because the server runs over stdio and is reachable from the LLM itself, a prompt-injected payload in any attacker-controlled content (a README, a ticket, a web page the agent browses) could issue a crafted tool call and read files limited only by OS permissions: SSH private keys, AWS credentials, .env files, /etc/passwd, and more. faf_write extended the risk to arbitrary out-of-project writes.
Proof of concept
A working proof-of-concept for this issue in claude-faf-mcp, with the exact payload below.
// Sent as a standard MCP tools/call request over stdio JSON-RPC
// Reads ~/.ssh/id_rsa via faf_read (absolute path, no traversal needed)
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "faf_read",
"arguments": {
"path": "~/.ssh/id_rsa"
}
}
}
// Alternatively, ../ traversal from a project working directory
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "faf_read",
"arguments": {
"path": "../../../.aws/credentials"
}
}
}The root cause is a missing confinement step. The code expanded ~ and called path.resolve(), which both turn relative and home-relative paths into fully qualified absolute paths, and then handed that result straight to fs.readFile / fs.writeFile. There was no check that the resolved path fell inside the project root.
The 5.7.2 patch introduced safe-path.ts, which canonicalizes paths through symlinks (closing symlink-bypass), then asserts the result starts with an allowlisted root (cwd, system temp, or FAF_ALLOWED_ROOTS). Context-file tools additionally enforce that the target has a .faf or .fafm extension, so even a path inside the project root cannot be used to read arbitrary files.
A central guard in callTool() runs this check before any tool handler is invoked. CWE-22 (path traversal) and CWE-73 (external control of file name) both apply; the information-disclosure impact maps to CWE-200.
The fix
Upgrade to claude-faf-mcp@5.7.2: npm install -g claude-faf-mcp@5.7.2. If an immediate upgrade is not possible, set the FAF_ALLOWED_ROOTS environment variable to a single trusted project directory and run the server only against local, trusted projects. The allowlist is enforced by the patched safe-path.ts guard in callTool().
Related research
- high · 7.5grok-faf-mcp: Arbitrary File Read via Unconfined Path in FAF Tools
- high · 7.5faf-mcp Arbitrary File Read/Write via Unconfined Path Argument
- highFlowise: Authenticated Arbitrary File Write via S3 Directory Loader Path Traversal
- high · 7.5CVE-2026-45623CVE-2026-45623: PostCSS Arbitrary File Read via sourceMappingURL Path Traversal