{"record":{"id":"e6e744201234f086","repo":"jeecgboot/JeecgBoot","slug":"bizpath-e6e744","errorCode":null,"errorMessage":"非法业务路径，禁止访问上传目录之外的路径: ${bizPath}","messagePattern":"非法业务路径，禁止访问上传目录之外的路径: (.+?)","errorType":"exception","errorClass":"JeecgBootException","httpStatus":null,"severity":"error","filePath":"jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/impl/SysCommentServiceImpl.java","lineNumber":361,"sourceCode":"     * @return\n     */\n    private String uploadLocal(MultipartFile mf, String bizPath) {\n        try {\n            // 文件安全校验，防止上传漏洞文件\n            SsrfFileTypeFilter.checkUploadFileType(mf, bizPath);\n        } catch (Exception e) {\n            throw new JeecgBootException(e);\n        }\n        \n        try {\n            String ctxPath = uploadpath;\n            String fileName = null;\n            //update-begin---author:liusq ---date:2026-03-30  for：【issues/9427】修复uploadLocal bizPath路径遍历漏洞(CWE-22)-----------\n            // 路径遍历校验：规范化后确保目标目录在uploadpath内\n            File uploadDir = new File(ctxPath).getCanonicalFile();\n            File file = new File(ctxPath + File.separator + bizPath + File.separator).getCanonicalFile();\n            if (!file.toPath().startsWith(uploadDir.toPath())) {\n                throw new JeecgBootException(\"非法业务路径，禁止访问上传目录之外的路径: \" + bizPath);\n            }\n            //update-end---author:liusq ---date:2026-03-30  for：【issues/9427】修复uploadLocal bizPath路径遍历漏洞(CWE-22)-----------\n            if (!file.exists()) {\n                file.mkdirs();// 创建文件根目录\n            }\n            String orgName = mf.getOriginalFilename();// 获取文件名\n            orgName = CommonUtils.getFileName(orgName);\n            if (orgName.indexOf(\".\") != -1) {\n                fileName = orgName.substring(0, orgName.lastIndexOf(\".\")) + \"_\" + System.currentTimeMillis() + orgName.substring(orgName.indexOf(\".\"));\n            } else {\n                fileName = orgName + \"_\" + System.currentTimeMillis();\n            }\n            String savePath = file.getPath() + File.separator + fileName;\n            File savefile = new File(savePath);\n            FileCopyUtils.copy(mf.getBytes(), savefile);\n            String dbpath = null;\n            if (oConvertUtils.isNotEmpty(bizPath)) {\n                dbpath = bizPath + File.separator + fileName;","sourceCodeStart":343,"sourceCodeEnd":379,"githubUrl":"https://github.com/jeecgboot/JeecgBoot/blob/96fb33f5ec68516da0b0147da06b2eb0419e063a/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/impl/SysCommentServiceImpl.java#L343-L379","documentation":"A CWE-22 path-traversal guard inside uploadLocal: both the configured upload root (uploadpath) and the target folder (uploadpath/bizPath) are canonicalized via getCanonicalFile(), and if the target path does not start with the upload root, the request is rejected. This catches ../ escapes, absolute-path tricks, and symlinked targets that resolve outside the upload directory.","triggerScenarios":"bizPath like '../../etc', '/etc', '..\\\\..\\\\windows'; absolute paths; symlinked folders resolving outside uploadpath; bizPath crafted to escape via OS-specific separators.","commonSituations":"Client-supplied bizPath not sanitized; legacy callers passing user-controlled paths directly; OS differences in canonicalization; misconfigured uploadpath differing between nodes.","solutions":["Ensure bizPath is a simple relative subfolder (e.g. 'temp', 'avatar', 'dict').","Strip any '../', leading '/', and backslashes before calling uploadLocal.","Never pass raw user input as bizPath; resolve it from a server-side allowlist."],"exampleFix":"// before\nString bizPath = request.getParameter(\"biz\"); // attacker: ../../etc\nsavePath = uploadLocal(file, bizPath); // throws 非法业务路径...\n// after\nString bizPath = ALLOWED_BIZ.getOrDefault(request.getParameter(\"biz\"), \"upload\");\n// ALLOWED_BIZ is a fixed map of safe folder names\nsavePath = uploadLocal(file, bizPath);","handlingStrategy":"validation","validationCode":"// Normalize bizPath and confirm it stays within uploadpath BEFORE writing.\nFile root = new File(uploadpath).getCanonicalFile();\nFile target = new File(uploadpath + File.separator + bizPath).getCanonicalFile();\nif (!target.toPath().startsWith(root.toPath())) {\n    throw new IllegalArgumentException(\"非法业务路径: \" + bizPath);\n}","typeGuard":"public boolean isBizPathSafe(String bizPath, String uploadpath) {\n    try {\n        File root = new File(uploadpath).getCanonicalFile();\n        File target = new File(uploadpath + File.separator + bizPath + File.separator).getCanonicalFile();\n        return target.toPath().startsWith(root.toPath());\n    } catch (IOException e) { return false; }\n}","tryCatchPattern":"try {\n    return uploadLocal(file, bizPath);\n} catch (JeecgBootException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"非法业务路径\")) {\n        log.warn(\"路径遍历拦截: bizPath={}\", bizPath);\n        return Result.error(\"非法的存储路径\");\n    }\n    throw e;\n}","preventionTips":["Resolve bizPath from a server-side allowlist of folder names, never from raw input.","Strip ../, leading /, and backslashes from any user-supplied path segment.","Always use getCanonicalFile() comparisons when constraining a path to a root.","Alert on repeated path-traversal attempts — they may indicate probing."],"tags":["upload","security","path-traversal","cwe-22"],"backgroundTag":null,"analyzedSha":"96fb33f5ec68516da0b0147da06b2eb0419e063a","analyzedAt":"2026-08-14T00:04:16.786Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}