{"record":{"id":"2722da3756d716be","repo":"jeecgboot/JeecgBoot","slug":"error-2722da","errorCode":null,"errorMessage":"非法文件路径，禁止访问上传目录之外的文件: ","messagePattern":"非法文件路径，禁止访问上传目录之外的文件: ","errorType":"validation","errorClass":"JeecgBootException","httpStatus":null,"severity":"critical","filePath":"jeecg-boot/jeecg-boot-module/jeecg-boot-module-airag/src/main/java/org/jeecg/modules/airag/llm/handler/AIChatHandler.java","lineNumber":628,"sourceCode":"                            java.io.ByteArrayOutputStream buffer = new java.io.ByteArrayOutputStream();\n                            int nRead;\n                            byte[] data = new byte[1024];\n                            while ((nRead = in.read(data, 0, data.length)) != -1) {\n                                buffer.write(data, 0, nRead);\n                            }\n                            buffer.flush();\n                            fileContent = buffer.toByteArray();\n                        }\n                    } else {\n                        //update-begin---author:liusq ---date:2026-03-30  for：【issues/9431】修复getFirstImageBase64路径遍历漏洞(CWE-22)-----------\n                        // 本地文件\n                        String filePath = uploadpath + File.separator + imageUrl;\n                        SsrfFileTypeFilter.checkPathTraversal(filePath);\n                        // 路径遍历校验：规范化后确保文件在uploadpath目录内\n                        File uploadDir = new File(uploadpath).getCanonicalFile();\n                        File targetFile = new File(filePath).getCanonicalFile();\n                        if (!targetFile.toPath().startsWith(uploadDir.toPath())) {\n                            throw new JeecgBootException(\"非法文件路径，禁止访问上传目录之外的文件: \" + imageUrl);\n                        }\n                        fileContent = Files.readAllBytes(targetFile.toPath());\n                        //update-end---author:liusq ---date:2026-03-30  for：【issues/9431】修复getFirstImageBase64路径遍历漏洞(CWE-22)-----------\n                    }\n                    originalImageBase64List.add(Base64.getEncoder().encodeToString(fileContent));\n                } catch (Exception e) {\n                    log.error(\"图片读取失败: {}\", imageUrl, e);\n                    throw new JeecgBootException(\"图片读取失败: \" + imageUrl);\n                }\n            }\n        }\n        return originalImageBase64List;\n    }\n    //================================================= end 【QQYUN-12145】【AI】AI 绘画创作 ========================================\n\n    /**\n     * 将 LLM 调用异常统一翻译为友好的 JeecgBootException。\n     * <p>","sourceCodeStart":610,"sourceCodeEnd":646,"githubUrl":"https://github.com/jeecgboot/JeecgBoot/blob/96fb33f5ec68516da0b0147da06b2eb0419e063a/jeecg-boot/jeecg-boot-module/jeecg-boot-module-airag/src/main/java/org/jeecg/modules/airag/llm/handler/AIChatHandler.java#L610-L646","documentation":"This error is thrown by AIChatHandler.getFirstImageBase64() when the resolved canonical path of a local image file does not start with the upload directory's canonical path. This is a path traversal security guard (CWE-22, issues/9431) for the image-edit pipeline. The code resolves uploadpath + imageUrl to a canonical File, then checks if it's within the upload directory.","triggerScenarios":"An image-edit request provides a local imageUrl (not matching WEB_PATTERN for URLs) that resolves outside the upload directory after canonicalization. For example: imageUrl='../../../etc/passwd' resolves to /etc/passwd which does not start with the upload directory. This triggers after SsrfFileTypeFilter.checkPathTraversal but the canonical path check is a second layer of defense.","commonSituations":"Malicious input attempting to access files outside the upload directory via ../ sequences or symlinks. A symlink within the upload directory points outside, causing canonical resolution to escape the root. Legitimate but incorrectly configured uploadpath where image files are stored in a sibling directory.","solutions":["This is a security guard functioning correctly — investigate the source of the imageUrl input.","Ensure client-side code only sends simple filenames (e.g. 'image_123.jpg'), not paths.","Verify the uploadpath configuration is correct and images are actually stored there.","Check for symlinks within the upload directory that could resolve outside it.","If legitimate images are stored elsewhere, update uploadpath or move the images into the configured directory."],"exampleFix":"// before (client) — sending traversal path\nimages: ['../../../data/secret.png']\n\n// after — sending clean filename\nimages: ['user_upload_123.png']","handlingStrategy":"validation","validationCode":"// Server-side: validate imageUrl is safe before path construction\npublic static boolean isSafeLocalImageRef(String imageUrl, String uploadpath) {\n    if (imageUrl == null || imageUrl.isEmpty()) return false;\n    if (imageUrl.contains(\"..\")) return false;\n    try {\n        File uploadDir = new File(uploadpath).getCanonicalFile();\n        File target = new File(uploadpath + File.separator + imageUrl).getCanonicalFile();\n        return target.toPath().startsWith(uploadDir.toPath());\n    } catch (IOException e) {\n        return false;\n    }\n}","typeGuard":"public static boolean isWithinUploadDir(String uploadpath, String imageUrl) {\n    try {\n        File uploadDir = new File(uploadpath).getCanonicalFile();\n        File target = new File(uploadpath + File.separator + imageUrl).getCanonicalFile();\n        return target.toPath().startsWith(uploadDir.toPath());\n    } catch (Exception e) {\n        return false;\n    }\n}","tryCatchPattern":"// This is a security guard — do not suppress the exception.\n// Log it as a security event.\ntry {\n    List<String> base64List = getFirstImageBase64(images);\n} catch (JeecgBootException e) {\n    if (e.getMessage().contains(\"非法文件路径\")) {\n        log.warn(\"[SECURITY] Path traversal in image edit blocked: {}\", imageUrl);\n        auditLogService.recordSecurityEvent(\"IMG_PATH_TRAVERSAL\", imageUrl);\n    }\n    throw e;\n}","preventionTips":["Only accept simple filenames as imageUrl, never paths","Store images with UUID-based names to eliminate traversal possibilities","Validate at the API boundary that imageUrl matches a safe pattern (alphanumeric + dots)","Check for symlinks within the upload directory that could resolve outside it","Log all path-traversal rejections as security events"],"tags":["security","path-traversal","cwe-22","airag","image-processing"],"backgroundTag":null,"analyzedSha":"96fb33f5ec68516da0b0147da06b2eb0419e063a","analyzedAt":"2026-08-14T00:04:16.786Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}