{"record":{"id":"937c4bb6b3eafd9d","repo":"alibaba/spring-ai-alibaba","slug":"path-must-start-with-one-of","errorCode":null,"errorMessage":"Path must start with one of : ","messagePattern":"Path must start with one of : ","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"warning","filePath":"spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/extension/interceptor/FilesystemInterceptor.java","lineNumber":183,"sourceCode":"\t\t// Normalize path\n\t\tString normalized = path.replace(\"\\\\\", \"/\");\n\t\tnormalized = Paths.get(normalized).normalize().toString().replace(\"\\\\\", \"/\");\n\n\t\tif (!normalized.startsWith(\"/\")) {\n\t\t\tnormalized = \"/\" + normalized;\n\t\t}\n\n\t\t// Check allowed prefixes if specified\n\t\tif (allowedPrefixes != null && !allowedPrefixes.isEmpty()) {\n\t\t\tboolean hasValidPrefix = false;\n\t\t\tfor (String prefix : allowedPrefixes) {\n\t\t\t\tif (normalized.startsWith(prefix)) {\n\t\t\t\t\thasValidPrefix = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!hasValidPrefix) {\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Path must start with one of \" + allowedPrefixes + \": \" + path\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\treturn normalized;\n\t}\n\n\t@Override\n\tpublic List<ToolCallback> getTools() {\n\t\treturn tools;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"Filesystem\";\n\t}\n","sourceCodeStart":165,"sourceCodeEnd":201,"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/interceptor/FilesystemInterceptor.java#L165-L201","documentation":"After traversal checks and normalization, FilesystemInterceptor.validatePath() enforces an allow-list: when allowedPrefixes is non-empty, the normalized path must start with at least one prefix or it throws IllegalArgumentException(\"Path must start with one of <prefixes>: <path>\"). This constrains filesystem operations to approved directory trees.","triggerScenarios":"validatePath(\"/var/data/file.txt\", List.of(\"/srv/workspace\")) — the normalized path does not begin with any configured allowed prefix.","commonSituations":"Misconfigured allowed-prefixes (missing the directory the app actually uses); paths expressed with trailing slashes or relative forms that don't string-match the configured prefix; moving code to a new deployment layout without updating prefixes.","solutions":["Add the required directory to the allowedPrefixes list passed to validatePath().","Ensure the path and prefixes are normalized identically (same absolute form, separators, no trailing slash mismatch).","Derive prefixes from configuration and log them at startup to spot drift.","Anchor user input under an allowed root (root.resolve(input).normalize()) before validation."],"exampleFix":"// before\nvalidatePath(\"/var/tmp/out.txt\", List.of(\"/srv/workspace\")); // rejected\n\n// after\nvalidatePath(\"/srv/workspace/out.txt\", List.of(\"/srv/workspace\")); // ok","handlingStrategy":"validation","validationCode":"String normalized = Paths.get(path.replace('\\\\', '/')).normalize().toString();\nboolean allowed = allowedPrefixes.stream().anyMatch(normalized::startsWith);\nif (!allowed) throw new IllegalArgumentException(\"Path outside allowed prefixes\");","typeGuard":"boolean withinPrefixes(String path, List<String> prefixes) {\n    String n = Paths.get(path.replace('\\\\', '/')).normalize().toString();\n    return prefixes.stream().anyMatch(n::startsWith);\n}","tryCatchPattern":"try {\n    String canonical = FilesystemInterceptor.validatePath(path, prefixes);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().startsWith(\"Path must start with\")) {\n        log.error(\"Path {} outside allowed prefixes {}\", path, prefixes);\n    }\n    throw e;\n}","preventionTips":["Keep allowedPrefixes in configuration and review them per environment.","Normalize both path and prefixes with the same rules before matching.","Anchor user input under a prefix root instead of accepting absolute paths.","Log the configured prefixes at startup to catch deployment drift early."],"tags":["security","allowlist","interceptor","path-validation"],"backgroundTag":"invalid-argument-value","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"}