{"record":{"id":"77972a5f7f48c5f6","repo":"angular/angular-cli","slug":"access-denied-path-path-is-outside-allowed-r","errorCode":null,"errorMessage":"Access denied: path '${path}' is outside allowed roots.","messagePattern":"Access denied: path '(.+?)' is outside allowed roots\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/angular/cli/src/commands/mcp/host.ts","lineNumber":339,"sourceCode":"              // Reached filesystem root\n              throw err;\n            }\n            current = parent;\n          }\n        }\n      } else {\n        throw e;\n      }\n    }\n\n    const isAllowed = roots.some((root) => {\n      const rel = relative(root, realPath);\n\n      return !rel.startsWith('..') && !isAbsolute(rel);\n    });\n\n    if (!isAllowed) {\n      throw new Error(`Access denied: path '${path}' is outside allowed roots.`);\n    }\n  }\n\n  return {\n    ...baseHost,\n    setRoots(newRoots: string[]) {\n      roots = newRoots.length > 0 ? resolveRoots(newRoots) : defaultRoots;\n    },\n    stat(path: string) {\n      checkPath(path);\n\n      return baseHost.stat(path);\n    },\n    existsSync(path: string) {\n      checkPath(path);\n\n      return baseHost.existsSync(path);\n    },","sourceCodeStart":321,"sourceCodeEnd":357,"githubUrl":"https://github.com/angular/angular-cli/blob/bb72145f9ab45aee29f523236b3a25cd0813a841/packages/angular/cli/src/commands/mcp/host.ts#L321-L357","documentation":"The Angular CLI MCP server sandbox every filesystem access (stat, existsSync, readFile, glob) through checkPath, which resolves symlinks and requires the real path to fall under one of the configured roots. This error is thrown when the requested path, after canonicalization, is not inside any allowed root, preventing the MCP server from reading files outside the workspace.","triggerScenarios":"Calling any MCP host operation (stat/existsSync/readFile/glob/executeNgCommand/startNgProcess) with an absolute path outside the roots set via setRoots, a path containing '..' segments that resolves outside the roots, or a symlink pointing outside the allowed roots.","commonSituations":"MCP clients hardcoding absolute paths to files in another project; referring to a shared directory outside the workspace; symlinked node_modules or home-directory configs resolving outside roots; stale roots after the user switched workspaces without calling setRoots.","solutions":["Move the requested path inside one of the allowed roots (workspace directory) and retry.","Have the MCP client call setRoots with the directory containing the desired path before accessing it.","Resolve or remove symlinks that point outside the allowed roots.","Use MCP tools like list_projects to discover workspaces that are actually accessible."],"exampleFix":"// before\nawait host.readFile('/etc/hosts', 'utf8');\n// after\nhost.setRoots(['/home/dev/my-app']);\nawait host.readFile('/home/dev/my-app/src/main.ts', 'utf8');","handlingStrategy":"validation","validationCode":"import { relative, isAbsolute, resolve, realpathSync } from 'node:path';\nfunction isInsideRoots(path: string, roots: string[]): boolean {\n  const realPath = realpathSync(resolve(path));\n  return roots.some((root) => {\n    const rel = relative(resolve(root), realPath);\n    return !rel.startsWith('..') && !isAbsolute(rel);\n  });\n}\nif (!isInsideRoots(targetPath, allowedRoots)) throw new Error('path outside roots');","typeGuard":"function isWithinRoot(p: string, roots: string[]): p is string {\n  const rel = relative(resolve(roots[0] ?? '/'), resolve(p));\n  return !rel.startsWith('..') && !isAbsolute(rel);\n}","tryCatchPattern":"try {\n  await host.readFile(path, 'utf8');\n} catch (e) {\n  if ((e as Error).message.includes('is outside allowed roots')) {\n    host.setRoots([resolve(path).split('/').slice(0, 3).join('/')]);\n    await host.readFile(path, 'utf8');\n  } else throw e;\n}","preventionTips":["Always pass workspace-relative or verified workspace-absolute paths to MCP host operations","Call setRoots when switching workspaces before any file access","Resolve symlinks before passing paths, since checkPath canonicalizes via realpath","Avoid '..' segments in paths; build paths from the workspace root instead"],"tags":["security","path-traversal","filesystem","angular-cli"],"backgroundTag":"path-outside-allowed-roots","analyzedSha":"bb72145f9ab45aee29f523236b3a25cd0813a841","analyzedAt":"2026-08-30T02:47:34.745Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}