{"record":{"id":"dc2e31fe499b3a01","repo":"alibaba/spring-ai-alibaba","slug":"path-outside-root-directory-dc2e31","errorCode":null,"errorMessage":"Path: outside root directory: ","messagePattern":"Path: outside root directory: ","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/extension/tools/filesystem/FileSystemTools.java","lineNumber":80,"sourceCode":"\t */\n\tpublic FileSystemTools(String rootDir, boolean virtualMode, int maxFileSizeMb) {\n\t\tthis.cwd = rootDir != null ? Paths.get(rootDir).toAbsolutePath().normalize() : Paths.get(\"\").toAbsolutePath();\n\t\tthis.virtualMode = virtualMode;\n\t\tthis.maxFileSizeBytes = maxFileSizeMb * 1024L * 1024L;\n\t}\n\n\t/**\n\t * Resolve a file path with security checks.\n\t */\n\tprivate Path resolvePath(String key) throws IllegalArgumentException {\n\t\tif (virtualMode) {\n\t\t\tString vpath = key.startsWith(\"/\") ? key : \"/\" + key;\n\t\t\tif (vpath.contains(\"..\") || vpath.startsWith(\"~\")) {\n\t\t\t\tthrow new IllegalArgumentException(\"Path traversal not allowed\");\n\t\t\t}\n\t\t\tPath full = cwd.resolve(vpath.substring(1)).normalize();\n\t\t\tif (!full.startsWith(cwd)) {\n\t\t\t\tthrow new IllegalArgumentException(\"Path:\" + full + \" outside root directory: \" + cwd);\n\t\t\t}\n\t\t\treturn full;\n\t\t}\n\n\t\tPath path = Paths.get(key);\n\t\tif (path.isAbsolute()) {\n\t\t\treturn path;\n\t\t}\n\t\treturn cwd.resolve(path).normalize();\n\t}\n\n\t// @formatter:off\n\t@Tool(name = \"read_file\", description = \"\"\"\n\t\tReads a file from the filesystem. You can access any file directly by using this tool.\n\t\tAssume this tool is able to read all files on the machine. If the User provides a path to a file assume that path is valid. It is okay to read a file that does not exist; an error will be returned.\n\n\t\tUsage:\n\t\t- The file_path parameter must be an absolute path, not a relative path","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/extension/tools/filesystem/FileSystemTools.java#L62-L98","documentation":"After resolving and normalizing the requested path, resolvePath verifies the result still starts with the configured root directory (cwd in virtual mode, or the base dir in real mode). If normalization/symlinks or an absolute path moved the result outside the root, it throws \"Path:<path> outside root directory\". This confines all filesystem tool operations to the sandbox root.","triggerScenarios":"Passing an absolute path not under the root directory; a path that resolves (including via symlink) outside the root; in non-virtual mode, an absolute Paths.get(key) that is outside the configured base.","commonSituations":"Hardcoding host-absolute paths like /tmp/data.csv while the tool root is a project sandbox; the root directory configured differently across environments (dev vs container); symlinked directories in the workspace pointing outside the root.","solutions":["Use paths relative to the tool's configured root directory.","Check the root directory printed in the message and move the target file under it, or reconfigure the tool's root to include the needed path.","Remove or replace symlinks that point outside the root; ensure normalization of the real target stays under root."],"exampleFix":"// before\nfsTool.read(\"/var/data/report.csv\"); // outside root directory\n// after\nfsTool.read(\"data/report.csv\"); // relative to the configured root","handlingStrategy":"validation","validationCode":"Path root = Path.of(\"/path/to/tool/root\").toAbsolutePath().normalize();\nPath target = root.resolve(userPath).normalize();\nif (!target.startsWith(root)) {\n    throw new IllegalArgumentException(\"Path escapes root: \" + userPath);\n}","typeGuard":"static boolean withinRoot(Path root, String userPath) {\n    return root.resolve(userPath).normalize().startsWith(root);\n}","tryCatchPattern":"try {\n    fsTool.write(path, content);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"outside root directory\")) {\n        log.warn(\"Path {} is outside the tool root; remapping\", path);\n    }\n}","preventionTips":["Keep the tool root and the files it must access aligned across environments.","Avoid symlinks inside the sandbox that point outside the root.","Resolve absolute paths against the root before calling the tool."],"tags":["java","security","sandbox","filesystem-tools"],"backgroundTag":"path-traversal-blocked","analyzedSha":"f82da0b50f35744c13968191be2b1cd2452ef550","analyzedAt":"2026-09-09T15:32:42.421Z","contentChangedAt":"2026-09-09T15:32:42.421Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}