{"record":{"id":"452e64daf2cd74dc","repo":"googleapis/mcp-toolbox","slug":"local-path-q-is-not-under-any-allowed-local-roots","errorCode":null,"errorMessage":"local path %q is not under any allowed local roots for source %q","messagePattern":"local path %q is not under any allowed local roots for source %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/sources/cloudstorage/cloudstorage.go","lineNumber":133,"sourceCode":"// still matches.\nfunc (s *Source) validateLocalPath(p string) error {\n\tclean, err := cloudstoragecommon.ValidateLocalPath(p)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif len(s.AllowedLocalRoots) == 0 {\n\t\treturn nil\n\t}\n\n\tnameMatched := false\n\tfor _, root := range s.AllowedLocalRoots {\n\t\tif isUnderRoot(clean, root) {\n\t\t\tnameMatched = true\n\t\t\tbreak\n\t\t}\n\t}\n\tif !nameMatched {\n\t\treturn fmt.Errorf(\"local path %q is not under any allowed local roots for source %q\", p, s.Name)\n\t}\n\n\tresolved, err := cloudstoragecommon.ResolveSymlinks(clean)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"local path %q cannot be resolved for source %q: %w\", p, s.Name, err)\n\t}\n\tfor _, root := range s.AllowedLocalRoots {\n\t\t// A root we cannot resolve authorizes nothing; skip it rather than\n\t\t// falling back to the name-level match we already passed.\n\t\tresolvedRoot, err := cloudstoragecommon.ResolveSymlinks(root)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tif isUnderRoot(resolved, resolvedRoot) {\n\t\t\treturn nil\n\t\t}\n\t}\n\treturn fmt.Errorf(\"local path %q resolves through a symbolic link to a target outside the allowed local roots for source %q\", p, s.Name)","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/sources/cloudstorage/cloudstorage.go#L115-L151","documentation":"For local file transfers (download/upload), the CloudStorage source enforces 'allowedLocalRoots'. The requested path must sit under at least one allowed root as written (before symlink resolution); otherwise it is rejected. This is the first of two checks — a second pass rejects paths that escape the roots via symlinks after resolution.","triggerScenarios":"Calling DownloadObject or UploadObject with a local path outside every configured allowedLocalRoot (while roots are configured); also hit by internal symlink-escape tests. Absolute paths pointing elsewhere, sibling directories, or ../ traversals out of the root all trigger this.","commonSituations":"Agent tries to write downloaded objects to /tmp or the workspace when only ~/data is allowlisted; config lists roots on a different machine/container layout; macOS /tmp-vs-/private/tmp symlink differences causing the path-level match to fail before resolution.","solutions":["Add the intended directory to 'allowedLocalRoots' in the cloud-storage source config.","Request operations with paths directly under an already-allowed root.","If the root itself is a symlink (e.g. /tmp on macOS), add the resolved real path (e.g. /private/tmp) to the roots list.","Verify with filepath.Clean-equivalent logic that the path does not traverse above the root via '..'."],"exampleFix":"# before\nallowedLocalRoots: [\"/home/user/data\"]\n# request: /tmp/out.csv -> rejected\n# after\nallowedLocalRoots: [\"/home/user/data\", \"/tmp\"]","handlingStrategy":"validation","validationCode":"roots := []string{\"/home/user/data\"}\np := filepath.Clean(localPath)\nfor _, r := range roots {\n    if strings.HasPrefix(p, filepath.Clean(r)+string(os.PathSeparator)) || p == filepath.Clean(r) {\n        return nil // path is under an allowed root\n    }\n}\nreturn errors.New(\"path outside allowedLocalRoots\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Add every directory agents need to allowedLocalRoots up front","Always request absolute, cleaned paths inside a root","On macOS, account for /tmp -> /private/tmp when listing roots","Remember symlink resolution is checked separately; verify targets resolve inside roots"],"tags":["filesystem","path-validation","allowlist","security","cloud-storage"],"backgroundTag":"path-outside-allowed-root","analyzedSha":"8cc6e09de2ad7b8bffc77751799585a1401a48eb","analyzedAt":"2026-09-05T01:10:36.887Z","contentChangedAt":"2026-09-05T01:10:36.887Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}