{"record":{"id":"3b6858d8b566c5a5","repo":"modelcontextprotocol/servers","slug":"access-denied-path-outside-allowed-directories","errorCode":null,"errorMessage":"Access denied - path outside allowed directories: ${absolute} not in ${allowedDirectories.join(', ')}","messagePattern":"Access denied - path outside allowed directories: (.+?) not in (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/filesystem/lib.ts","lineNumber":110,"sourceCode":"  \n  // If no valid resolution found, use the first allowed directory as base\n  // This provides a consistent fallback behavior\n  return path.resolve(allowedDirectories[0], relativePath);\n}\n\n// Security & Validation Functions\nexport async function validatePath(requestedPath: string): Promise<string> {\n  const expandedPath = expandHome(requestedPath);\n  const absolute = path.isAbsolute(expandedPath)\n    ? path.resolve(expandedPath)\n    : resolveRelativePathAgainstAllowedDirectories(expandedPath);\n\n  const normalizedRequested = normalizePath(absolute);\n\n  // Security: Check if path is within allowed directories before any file operations\n  const isAllowed = isPathWithinAllowedDirectories(normalizedRequested, allowedDirectories);\n  if (!isAllowed) {\n    throw new Error(`Access denied - path outside allowed directories: ${absolute} not in ${allowedDirectories.join(', ')}`);\n  }\n\n  // Security: Handle symlinks by checking their real path to prevent symlink attacks\n  // This prevents attackers from creating symlinks that point outside allowed directories\n  try {\n    const realPath = await fs.realpath(absolute);\n    const normalizedReal = normalizePath(realPath);\n    if (!isPathWithinAllowedDirectories(normalizedReal, allowedDirectories)) {\n      throw new Error(`Access denied - symlink target outside allowed directories: ${realPath} not in ${allowedDirectories.join(', ')}`);\n    }\n    return realPath;\n  } catch (error) {\n    // Security: For new files that don't exist yet, verify parent directory\n    // This ensures we can't create files in unauthorized locations\n    if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n      const parentDir = path.dirname(absolute);\n      try {\n        const realParentPath = await fs.realpath(parentDir);","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/modelcontextprotocol/servers/blob/76d64c822f5125032f89eb71dbdb94e42b434821/src/filesystem/lib.ts#L92-L128","documentation":"Thrown by `validatePath` in the filesystem server when the normalized requested path does not lie within any allowed directory. This is the primary security boundary: every file operation runs through `validatePath`, which checks the resolved absolute path before any I/O. Symlink resolution is then applied separately (error 17).","triggerScenarios":"Any filesystem tool call (`read_text_file`, `write_file`, `list_directory`, etc.) whose `path` resolves outside all configured allowed directories — e.g. requesting `/etc/passwd` when only `/home/me/projects` is allowed, or a relative path that escapes via `..`.","commonSituations":"Clients passing absolute paths outside the allowlist, `..` traversal in relative paths, home shorthand (`~/`) expanding outside the allowlist, or a misconfigured allowlist that omits the needed directory.","solutions":["Request a path inside one of the allowed directories shown in the error message.","Add the needed directory to the server's allowed directories (CLI args or client roots).","Avoid `..` segments; resolve and normalize the path client-side before sending."],"exampleFix":"# before (only /home/me/projects allowed)\nread_text_file({ path: '/etc/passwd' })\n# after\nread_text_file({ path: '/home/me/projects/notes.txt' })","handlingStrategy":"validation","validationCode":"import path from 'node:path';\nfunction isWithinAllowed(p: string, allowed: string[]): boolean {\n  const a = path.resolve(p);\n  return allowed.some(d => a === path.resolve(d) || a.startsWith(path.resolve(d) + path.sep));\n}\nif (!isWithinAllowed(args.path, allowedDirs)) { /* surface error */ }","typeGuard":null,"tryCatchPattern":"try {\n  await readFile({ path });\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Access denied - path outside')) {\n    // path resolves outside allowlist; choose an allowed path or extend allowlist\n  }\n}","preventionTips":["Only request paths inside the configured allowed directories.","Avoid '..' traversal; normalize paths client-side first.","Extend the allowlist (CLI or roots) when legitimate access is needed."],"tags":["mcp","typescript","filesystem-server","security","access-control","validation"],"backgroundTag":null,"analyzedSha":"76d64c822f5125032f89eb71dbdb94e42b434821","analyzedAt":"2026-08-12T10:02:41.718Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}