{"record":{"id":"714ad5fd2572d4d5","repo":"jeecgboot/JeecgBoot","slug":"bizpath","errorCode":null,"errorMessage":"非法业务路径，禁止访问上传目录之外的路径: ${bizPath}","messagePattern":"非法业务路径，禁止访问上传目录之外的路径: (.+?)","errorType":"validation","errorClass":"JeecgBootException","httpStatus":null,"severity":"critical","filePath":"jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/CommonUtils.java","lineNumber":180,"sourceCode":"    }\n    /**\n     * 本地文件上传\n     * @param mf 文件\n     * @param bizPath  自定义路径\n     * @return\n     */\n    public static String uploadLocal(MultipartFile mf,String bizPath,String uploadpath){\n        try {\n            // 文件安全校验，防止上传漏洞文件\n            SsrfFileTypeFilter.checkUploadFileType(mf, bizPath);\n            \n            String fileName = null;\n            //update-begin---author:liusq ---date:2026-03-30  for：【issues/9428】修复uploadLocal bizPath路径遍历漏洞(CWE-22)-----------\n            // 路径遍历校验：规范化后确保目标目录在uploadpath内\n            File uploadDir = new File(uploadpath).getCanonicalFile();\n            File file = new File(uploadpath + 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/9428】修复uploadLocal bizPath路径遍历漏洞(CWE-22)-----------\n            if (!file.exists()) {\n                // 创建文件根目录\n                file.mkdirs();\n            }\n            // 获取文件名\n            String orgName = mf.getOriginalFilename();\n            // 无中文情况下进行转码\n            if (orgName != null && !CommonUtils.ifContainChinese(orgName)) {\n                orgName = new String(orgName.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);\n            }\n            orgName = CommonUtils.getFileName(orgName);\n            if(orgName.indexOf(SymbolConstant.SPOT)!=-1){\n                fileName = orgName.substring(0, orgName.lastIndexOf(\".\")) + \"_\" + System.currentTimeMillis() + orgName.substring(orgName.lastIndexOf(\".\"));\n            }else{\n                fileName = orgName+ \"_\" + System.currentTimeMillis();\n            }","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/jeecgboot/JeecgBoot/blob/96fb33f5ec68516da0b0147da06b2eb0419e063a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/CommonUtils.java#L162-L198","documentation":"A second path-traversal guard (CWE-22) in CommonUtils.uploadLocal, covering the MultipartFile-based upload flow (vs the byte[] flow in error 14). It canonicalizes both the uploadpath root and the uploadpath+bizPath target via getCanonicalFile(), then asserts the target starts with the upload dir. If a bizPath escapes the upload directory after canonicalization, it throws JeecgBootException. Added for issues/9428. It also runs SsrfFileTypeFilter.checkUploadFileType to block disallowed file types.","triggerScenarios":"A multipart upload request whose bizPath parameter resolves (after symlink resolution and normalization via getCanonicalFile) to a location outside the configured uploadpath. Attacker-controlled bizPath with traversal sequences or symlink-laden base paths.","commonSituations":"Security testing of the upload endpoint; a frontend bug concatenating paths with '..'; a shared upload root where a symlink points outside; misconfigured uploadpath that itself contains traversal segments.","solutions":["Validate and sanitize bizPath server-side before reaching uploadLocal: reject '..' and absolute paths.","Use a fixed allow-list of subdirectories for bizPath.","Ensure the configured uploadpath is a real, non-symlinked absolute directory.","Run SsrfFileTypeFilter checks earlier and fail fast with a clear message."],"exampleFix":"// before — bizPath built from unvalidated input\nString bizPath = request.getParameter(\"path\"); // \"../secret\"\n\n// after — validate against allow-list\nString bizPath = sanitizeBizPath(request.getParameter(\"path\"));\nif (bizPath == null) throw new IllegalArgumentException(\"invalid bizPath\");","handlingStrategy":"validation","validationCode":"// Validate bizPath against the upload dir before uploadLocal\npublic static void safeUploadLocal(MultipartFile mf, String bizPath, String uploadpath) {\n  File dir = new File(uploadpath + File.separator + bizPath).getCanonicalFile();\n  File root = new File(uploadpath).getCanonicalFile();\n  if (!dir.toPath().startsWith(root.toPath()))\n    throw new IllegalArgumentException(\"bizPath escapes upload dir\");\n  CommonUtils.uploadLocal(mf, bizPath, uploadpath);\n}","typeGuard":"public static boolean bizPathWithinRoot(String bizPath, String uploadpath) throws IOException {\n  Path root = Paths.get(uploadpath).toRealPath();\n  Path target = Paths.get(uploadpath, bizPath).toAbsolutePath().normalize();\n  return target.startsWith(root);\n}","tryCatchPattern":"try {\n  CommonUtils.uploadLocal(mf, bizPath, uploadpath);\n} catch (JeecgBootException e) {\n  response.sendError(400, \"Illegal upload path\");\n}","preventionTips":["Sanitize bizPath server-side; reject '..' and absolute paths.","Use a fixed allow-list of upload subdirectories.","Ensure uploadpath is a real, non-symlinked absolute directory.","Combine file-type filtering with path validation."],"tags":["backend","security","path-traversal","cwe-22","upload","jeecg"],"backgroundTag":null,"analyzedSha":"96fb33f5ec68516da0b0147da06b2eb0419e063a","analyzedAt":"2026-08-14T00:04:16.786Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}