Skip to main content

MCP-003 Overbroad filesystem root

MCP filesystem servers (the @modelcontextprotocol/server-filesystem family) grant the model read / write reach across the directory the host process points them at. Mounted at ~/Documents they expose every document on the machine. Mounted at $HOME they expose the whole user home including .ssh, .gnupg, browser profiles, and session-token caches. Mounted at / they expose the entire filesystem the host process can reach. Even when the user trusts the model, a single prompt-injection turns "this MCP can read files" into "this MCP can exfiltrate everything".

What Vulkro detects

Rule fires when a filesystem-class MCP server has a path argument that falls into one of two classes:

  • Root: literal /, literal $HOME / ~ / ${HOME}, the unexpanded user-home forms /Users/<name> and /home/<name> with no further path segment. Severity: High. Evidence weight: 0.8, source Pattern.
  • Shallow: a single-segment directory under home (~/Documents, ~/Desktop, $HOME/work) or under root (/tmp, /var). Severity: Medium. Evidence weight: 0.5, source Heuristic.

The detector recognises a filesystem-class server by the package name in args:

  • server-filesystem (the official @modelcontextprotocol/server-filesystem)
  • filesystem-server
  • mcp-server-fs
  • server-fs

Non-filesystem servers are skipped: random server args may legitimately be paths to configuration files, and over-firing there would be the wrong shape.

Path classification is intentionally based on the literal text. The detector does NOT expand ~ against the running user's home: the finding is about the declared scope, not what the OS would resolve to. A literal / mount IS the root mount regardless of who runs the host.

Evidence signal: mcp-server-overbroad-fs-root. Confidence: High for the root class, Medium for the shallow class.

Non-compliant config

{
"mcpServers": {
"fs-root": {
"command": "npx",
"args": [
"@modelcontextprotocol/[email protected]",
"/"
]
},
"fs-home": {
"command": "npx",
"args": [
"@modelcontextprotocol/[email protected]",
"$HOME"
]
},
"fs-shallow": {
"command": "npx",
"args": [
"@modelcontextprotocol/[email protected]",
"~/Documents"
]
}
}
}

Compliant config

{
"mcpServers": {
"fs": {
"command": "npx",
"args": [
"@modelcontextprotocol/[email protected]",
"/Users/me/work/this-project"
]
}
}
}

The mount is scoped to a single project directory the model genuinely needs reach into.

Remediation

  1. Narrow the mount to the smallest directory the model needs. ~/work/<project> is almost always the right answer; ~ and / almost never are.
  2. If multiple projects need reach, declare one MCP server entry per project rather than mounting ~/work as a whole. The detector treats two-segment-or-deeper paths as adequately scoped.
  3. For workflows that genuinely need broad reach (system administration scripts, machine-wide search), document the trust assumption in the host config or your team's MCP setup notes so the next reviewer doesn't have to rediscover it.

See also

  • vulkro mcp-audit - parent CLI command.
  • MCP-001 - the pin-shape rule (filesystem servers are upgraded to High when unpinned because broad-mount + unpinned amplifies the blast radius).
  • Confidence model - what High, Medium, Low mean for MCP findings.

References