{"record":{"id":"93b0c9a24cba70ee","repo":"jeecgboot/JeecgBoot","slug":"error-93b0c9","errorCode":null,"errorMessage":"原始接口路径包含非法字符","messagePattern":"原始接口路径包含非法字符","errorType":"validation","errorClass":"JeecgBootBizTipException","httpStatus":null,"severity":"warning","filePath":"jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/openapi/controller/OpenApiController.java","lineNumber":258,"sourceCode":"        return token;\n    }\n\n    /**\n     * 校验原始接口路径是否合法：\n     *  - 相对路径：必须以 / 开头，不允许 // 和 .. 防止路径穿越\n     *  - 完整URL：仅允许 http/https 协议，禁止 file/ftp/gopher/jar/netdoc 等其它协议（用于微服务模式跨模块调用）\n     */\n    private void validOriginUrl(String originUrl) {\n        if (oConvertUtils.isEmpty(originUrl)) {\n            throw new JeecgBootBizTipException(\"原始接口路径不能为空\");\n        }\n        String decoded;\n        try {\n            decoded = java.net.URLDecoder.decode(originUrl, \"UTF-8\");\n            // 二次解码，防止 %252f 这类双重编码绕过\n            decoded = java.net.URLDecoder.decode(decoded, \"UTF-8\");\n        } catch (Exception e) {\n            throw new JeecgBootBizTipException(\"原始接口路径包含非法字符\");\n        }\n        //update-begin---author:scott ---date:20260429  for：【issues/9590】微服务nginx部署openApi接口访问不到-----------\n        // 微服务部署时，OpenAPI 配置的接口可能位于其他微服务模块（如 erp 7003），允许 originUrl 直接配置完整 http(s) URL\n        String lower = decoded.toLowerCase();\n        boolean isFullHttpUrl = lower.startsWith(\"http://\") || lower.startsWith(\"https://\");\n        if (!isFullHttpUrl) {\n            if (!decoded.startsWith(\"/\")) {\n                throw new JeecgBootBizTipException(\"原始接口路径必须以 / 开头，或填写完整的 http(s) URL\");\n            }\n            if (decoded.startsWith(\"//\") || decoded.startsWith(\"/\\\\\")) {\n                throw new JeecgBootBizTipException(\"原始接口路径不能以 // 或 /\\\\ 开头\");\n            }\n            if (lower.contains(\"://\") || lower.startsWith(\"file:\") || lower.startsWith(\"ftp:\") || lower.startsWith(\"gopher:\")\n                    || lower.startsWith(\"jar:\") || lower.startsWith(\"netdoc:\")) {\n                throw new JeecgBootBizTipException(\"原始接口路径仅支持相对路径或 http(s) 完整URL\");\n            }\n        } else {\n            // 即便是完整URL，也禁止其它危险协议（防止 http://x@file:/... 之类的绕过场景）","sourceCodeStart":240,"sourceCodeEnd":276,"githubUrl":"https://github.com/jeecgboot/JeecgBoot/blob/96fb33f5ec68516da0b0147da06b2eb0419e063a/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/openapi/controller/OpenApiController.java#L240-L276","documentation":"Thrown by OpenApiController.validOriginUrl when URLDecoder.decode fails on the originUrl parameter. The method decodes the URL twice (to defeat %252f-style double-encoding bypass) and any malformed percent-encoding (e.g. a stray '%' not followed by two hex digits) makes the decoder raise IllegalArgumentException, which is wrapped into this JeecgBootBizTipException. It is a hard input-validation failure, not retriable.","triggerScenarios":"Saving/updating an OpenAPI config whose origin_url field contains a malformed escape sequence such as '%zz', a lone '%', or invalid UTF-8 byte sequences. Any POST to the openapi config endpoint (add/edit) passes originUrl through validOriginUrl.","commonSituations":"Operators paste an origin URL copied from a browser address bar that is already decoded but contains a literal '%'; migration scripts that hand-build URLs with unescaped characters; proxy/CDN that re-encodes the path.","solutions":["URL-encode the originUrl value once before submitting (let the server's single decode pass yield the intended path), or send it fully unencoded if it is a plain relative path like /sys/api.","Remove any literal '%' characters or replace them with their percent-encoded form '%25'.","Validate the value with java.net.URLEncoder / a URL sanity check in the admin UI before the request is sent."],"exampleFix":"// before: originUrl = \"/sys/%file\"  (malformed %f)\n// after:  originUrl = \"/sys/%25file\"  (valid %25 -> literal %)","handlingStrategy":"validation","validationCode":"// Pre-validate the originUrl before calling the OpenAPI config API\nprivate static boolean isOriginUrlDecodable(String s) {\n    if (s == null || s.isEmpty()) return false;\n    try {\n        String d = java.net.URLDecoder.decode(s, \"UTF-8\");\n        java.net.URLDecoder.decode(d, \"UTF-8\"); // match server's double-decode\n        return true;\n    } catch (Exception e) {\n        return false;\n    }\n}\nif (!isOriginUrlDecodable(originUrl)) {\n    // reject / fix the value before submit\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Send originUrl either fully unencoded (plain '/path') or correctly single-encoded; never partial '%'-escapes.","Add a client-side URL sanity check in the admin form before posting.","Document the double-decode behavior so integrators do not over-encode."],"tags":["openapi","url-validation","input-validation","ssrf"],"backgroundTag":null,"analyzedSha":"96fb33f5ec68516da0b0147da06b2eb0419e063a","analyzedAt":"2026-08-14T00:04:16.786Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}