{"record":{"id":"b1f925ed0ed9668d","repo":"angular/angular-cli","slug":"access-denied-glob-pattern-pattern-contains","errorCode":null,"errorMessage":"Access denied: glob pattern '${pattern}' contains path traversal sequences.","messagePattern":"Access denied: glob pattern '(.+?)' contains path traversal sequences\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/angular/cli/src/commands/mcp/host.ts","lineNumber":365,"sourceCode":"    },\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    },\n    readFile(path: string, encoding: BufferEncoding) {\n      checkPath(path);\n\n      return baseHost.readFile(path, encoding);\n    },\n    glob(pattern: string, options: { cwd: string }) {\n      if (pattern.includes('..')) {\n        throw new Error(\n          `Access denied: glob pattern '${pattern}' contains path traversal sequences.`,\n        );\n      }\n\n      checkPath(options.cwd);\n\n      const firstWildcardIndex = pattern.search(/[*?[{]/);\n      const basePath = firstWildcardIndex >= 0 ? pattern.substring(0, firstWildcardIndex) : pattern;\n\n      const targetDir = resolve(options.cwd, basePath);\n      checkPath(targetDir);\n\n      return baseHost.glob(pattern, options);\n    },\n    executeNgCommand(\n      args: readonly string[],\n      options: Parameters<Host['executeNgCommand']>[1] = {},\n    ) {","sourceCodeStart":347,"sourceCodeEnd":383,"githubUrl":"https://github.com/angular/angular-cli/blob/bb72145f9ab45aee29f523236b3a25cd0813a841/packages/angular/cli/src/commands/mcp/host.ts#L347-L383","documentation":"The MCP host glob() wrapper rejects glob patterns containing '..' outright, before checking cwd or the pattern's base directory. Because glob patterns can traverse directories via wildcard segments, any '..' in the pattern is treated as a path traversal attempt and denied unconditionally.","triggerScenarios":"Calling glob(pattern, { cwd }) where the pattern string contains '..' anywhere (e.g. '../src/**/*.ts' or 'packages/../secret/*').","commonSituations":"Clients computing relative patterns from a different directory and walking up with '..'; templated patterns built from user input; ported shell glob commands that relied on relative parent paths.","solutions":["Remove '..' from the pattern and set cwd to the actual target directory instead.","Anchor the glob at a common root (e.g. cwd set to the workspace root, pattern 'src/**/*.ts').","Ensure the resolved base directory and cwd are inside the allowed MCP roots."],"exampleFix":"// before\nhost.glob('../shared/**/*.ts', { cwd: workspaceRoot });\n// after\nhost.glob('**/*.ts', { cwd: '/home/dev/my-app/shared' });","handlingStrategy":"validation","validationCode":"function safeGlob(pattern: string): string {\n  if (pattern.includes('..')) {\n    throw new Error(`glob pattern '${pattern}' must not contain '..'`);\n  }\n  return pattern;\n}\nconst pattern = safeGlob('src/**/*.ts');","typeGuard":"function isSafeGlobPattern(p: string): p is string {\n  return !p.includes('..');\n}","tryCatchPattern":"try {\n  const files = host.glob(pattern, { cwd: workspaceRoot });\n} catch (e) {\n  if ((e as Error).message.includes('path traversal sequences')) {\n    const files = host.glob(pattern.replaceAll(/(^|\\/|\\\\)\\.\\.($|\\/|\\\\)/g, ''), { cwd: workspaceRoot });\n  } else throw e;\n}","preventionTips":["Anchor globs at the workspace root and never use '..' in patterns","Validate user-supplied glob input before passing it to the host","Prefer absolute or cwd-relative patterns from the workspace directory","Sanitize templated patterns built from user input"],"tags":["security","glob","path-traversal","angular-cli"],"backgroundTag":"glob-path-traversal","analyzedSha":"bb72145f9ab45aee29f523236b3a25cd0813a841","analyzedAt":"2026-08-30T02:47:34.745Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}