{"record":{"id":"b8e1de5bcfedcbff","repo":"OtterMind/Chat2DB","slug":"path-is-outside-of-the-selected-sql-directory","errorCode":null,"errorMessage":"Path is outside of the selected SQL directory","messagePattern":"Path is outside of the selected SQL directory","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"chat2db-community-server/chat2db-community-jcef/src/main/java/ai/chat2db/community/jcef/handler/biz/SqlDirectoryTreeStore.java","lineNumber":125,"sourceCode":"\n    static Map<String, Object> createChild(String rootToken, String parentRelativePath, String rawName, String type)\n            throws IOException {\n        Path root = getRoot(rootToken);\n        Path parent = resolveInRoot(root, parentRelativePath);\n        if (!Files.isDirectory(parent, LinkOption.NOFOLLOW_LINKS)) {\n            throw new IllegalArgumentException(\"Selected path is not a directory\");\n        }\n\n        boolean directory = \"directory\".equals(type);\n        boolean file = \"file\".equals(type);\n        if (!directory && !file) {\n            throw new IllegalArgumentException(\"Unsupported SQL directory child type\");\n        }\n\n        String name = file ? normalizeFileName(rawName, \"sql\") : normalizeDirectoryName(rawName);\n        Path target = parent.resolve(name).normalize();\n        if (!target.startsWith(root)) {\n            throw new IllegalArgumentException(\"Path is outside of the selected SQL directory\");\n        }\n        if (Files.exists(target, LinkOption.NOFOLLOW_LINKS)) {\n            throw new IllegalArgumentException(\"File or directory already exists\");\n        }\n\n        if (directory) {\n            Files.createDirectory(target);\n        } else {\n            Files.createFile(target);\n        }\n\n        Path realTarget = target.toRealPath(LinkOption.NOFOLLOW_LINKS);\n        if (!realTarget.startsWith(root)) {\n            throw new IllegalArgumentException(\"Path is outside of the selected SQL directory\");\n        }\n\n        Map<String, Object> result = new HashMap<>();\n        result.put(\"createdNode\", toNode(rootToken, root, realTarget));","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/OtterMind/Chat2DB/blob/5ee1e990e73fbcae1969dc554be254fedb3ab888/chat2db-community-server/chat2db-community-jcef/src/main/java/ai/chat2db/community/jcef/handler/biz/SqlDirectoryTreeStore.java#L107-L143","documentation":"Thrown by SqlDirectoryTreeStore.createChild() as a defense-in-depth containment guard: after resolving and normalizing the target path (parent.resolve(name).normalize()), it checks target.startsWith(root). This should never fire under normal operation because normalizeDirectoryName() already rejects names containing '/' or '\\' and null bytes, preventing path traversal. If it fires, it indicates a bug in the name sanitization or an unexpected filesystem behavior.","triggerScenarios":"The normalized child target path does not start with the root path. Given that normalizeDirectoryName rejects path separators, this can only fire if the sanitization function has a regression, or if a filesystem-specific normalization (e.g., case-insensitive paths on Windows/macOS) causes startsWith to return false despite the path being logically contained.","commonSituations":"Extremely rare in practice. Could fire on case-insensitive filesystems (Windows, macOS default) if root and target differ in case after normalization; a future code change weakens normalizeDirectoryName; an OS path normalization quirk.","solutions":["This is a security guard that should not fire — investigate why the sanitized name escaped root containment","If on a case-insensitive filesystem, check whether root and target case alignment is the issue","Review normalizeDirectoryName() for any path separator or traversal sequence it fails to reject","Do not weaken or remove this guard — it is defense-in-depth against directory traversal"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// This guard is defense-in-depth and should not fire under normal operation.\n// The only validation to add is ensuring normalizeDirectoryName has no gaps:\nprivate static boolean isSafeName(String name) {\n    return name != null && !name.isEmpty()\n        && !name.contains(\"/\") && !name.contains(\"\\\\\")\n        && !name.contains(\"..\") && name.indexOf('\\0') < 0;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Do not attempt to bypass or weaken this containment guard — it is security-critical","If it fires, audit normalizeDirectoryName() and normalizeFileName() for traversal-sequence gaps","On case-insensitive filesystems, verify root and target case alignment in startsWith checks","Add integration tests that attempt traversal via '..' and separators to verify the guard holds"],"tags":["sql-directory","security","path-traversal","containment","defense-in-depth"],"backgroundTag":null,"analyzedSha":"5ee1e990e73fbcae1969dc554be254fedb3ab888","analyzedAt":"2026-08-14T07:05:03.077Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}