grok-faf-mcp: Arbitrary File Read via Unconfined Path in FAF Tools
The grok-faf-mcp MCP server lets any caller read files anywhere on the server's filesystem by passing an absolute path or directory-traversal sequence to several built-in tools, exposing SSH keys…

The problem
Several tools in grok-faf-mcp (refresh_faf, faf_score, faf_get_orchestration_policy, refresh_blend, faf_read, faf_write) accept a caller-controlled path argument. The server applies tilde expansion and path.resolve() then reads the result with no directory boundary check.
faf_read uses a denylist-only approach that an absolute path bypasses entirely. faf_get_orchestration_policy echoes the resolved absolute path and parsed file values back to the caller, making it a reliable confirmation oracle. Any file readable by the server process is reachable: SSH keys, AWS credentials, .env files, /etc/passwd.
Proof of concept
A working proof-of-concept for this issue in grok-faf-mcp, with the exact payload below.
// MCP stdio JSON-RPC call -- send over the server's stdin
// Tool: faf_read with absolute path (denylist bypass)
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"faf_read","arguments":{"path":"/home/user/.ssh/id_rsa"}}}
// Alternatively, tilde-expanded home-relative path
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"faf_read","arguments":{"path":"~/.aws/credentials"}}}
// Or use refresh_faf which echoes full file contents as "fresh DNA"
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"refresh_faf","arguments":{"path":"../../../etc/passwd"}}}The root cause (CWE-22 / CWE-73) is that getProjectPath() and the faf_read/faf_write handlers resolve user input with path.resolve() and os.homedir() tilde expansion, then open the result with no allowlist or prefix check against a trusted root. The denylist on faf_read only blocks a small set of known-dangerous filenames, so an absolute path like /home/user/.ssh/id_rsa sidesteps it completely.
The fix in 1.5.3 introduces safe-path.ts, which canonicalizes paths through symlinks and then asserts the result starts with an approved root (cwd or system temp, overridable via FAF_ALLOWED_ROOTS). Context-reading tools (refresh_faf, faf_score, etc.) are additionally restricted to files with .faf or .fafm extensions, so even an in-bounds path is refused unless it is a recognized context file.
Because the server runs over stdio and is often driven by an LLM processing attacker-controlled content (a README, web page, ticket, or .faf), a prompt-injected tool call is a realistic delivery vector with no user interaction required.
The fix
Upgrade to grok-faf-mcp 1.5.3 or later. Run: npm install -g grok-faf-mcp@1.5.3 (or bunx grok-faf-mcp). If you cannot upgrade immediately, set the FAF_ALLOWED_ROOTS environment variable to a single trusted project directory and run the server only against local, trusted projects.
Reported by Zhihao Zhang (Worcester Polytechnic Institute).
Related research
- high · 7.5faf-mcp Arbitrary File Read/Write via Unconfined Path Argument
- high · 7.5claude-faf-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