{"record":{"id":"875c42d46540bb0b","repo":"jeecgboot/JeecgBoot","slug":"file-ftp-gopher-jar-netdoc","errorCode":null,"errorMessage":"原始接口路径不允许嵌套 file/ftp/gopher/jar/netdoc 等协议","messagePattern":"原始接口路径不允许嵌套 file/ftp/gopher/jar/netdoc 等协议","errorType":"validation","errorClass":"JeecgBootBizTipException","httpStatus":null,"severity":"error","filePath":"jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/openapi/controller/OpenApiController.java","lineNumber":280,"sourceCode":"        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:/... 之类的绕过场景）\n            String afterScheme = lower.substring(lower.indexOf(\"://\") + 3);\n            if (afterScheme.contains(\"file:\") || afterScheme.contains(\"ftp:\") || afterScheme.contains(\"gopher:\")\n                    || afterScheme.contains(\"jar:\") || afterScheme.contains(\"netdoc:\")) {\n                throw new JeecgBootBizTipException(\"原始接口路径不允许嵌套 file/ftp/gopher/jar/netdoc 等协议\");\n            }\n        }\n        if (decoded.contains(\"..\")) {\n            throw new JeecgBootBizTipException(\"原始接口路径不能包含 ..\");\n        }\n        //update-end---author:scott ---date:20260429  for：【issues/9590】微服务nginx部署openApi接口访问不到-----------\n    }\n\n    @GetMapping(\"/json\")\n    public SwaggerModel swaggerModel() {\n\n        SwaggerModel swaggerModel = new SwaggerModel();\n        swaggerModel.setSwagger(\"2.0\");\n        swaggerModel.setInfo(swaggerInfo());\n        swaggerModel.setHost(\"jeecg.com\");\n        swaggerModel.setBasePath(\"/jeecg-boot\");\n        swaggerModel.setSchemes(Lists.newArrayList(\"http\", \"https\"));\n","sourceCodeStart":262,"sourceCodeEnd":298,"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#L262-L298","documentation":"Thrown in the full-http-URL branch when the substring after '://' still embeds a dangerous scheme token (file:, ftp:, etc.). This defeats bypass tricks like 'http://x@file:/path' or 'http://evil.com/?next=ftp://...' where a nested scheme could be re-resolved downstream.","triggerScenarios":"origin_url such as 'http://attacker.com/redirect?to=file:/etc/passwd', 'https://x@ftp://internal', or any full URL whose authority/query/path contains one of the blocked scheme keywords.","commonSituations":"SSRF payloads crafted to pass the outer http(s) check; legitimate URLs that happen to include 'ftp:' in a query string or path segment (rare but possible in proxy/redirect configs).","solutions":["Strip the nested dangerous scheme from the URL; if it is only in a query param, consider whether it is necessary.","Use a clean http(s) URL with no secondary scheme keywords anywhere in the string.","If a legitimate parameter must contain the substring 'ftp:' etc., route it through a different field, not originUrl."],"exampleFix":"// before: originUrl = \"http://gateway/r?u=ftp://internal\"\n// after:  originUrl = \"http://gateway/r\"  // pass 'u' via a separate signed param","handlingStrategy":"validation","validationCode":"// For full http(s) URLs, ensure no nested dangerous scheme anywhere after '://'\nprivate static boolean isSafeHttpUrl(String s) {\n    String l = s == null ? \"\" : s.toLowerCase();\n    if (!(l.startsWith(\"http://\") || l.startsWith(\"https://\"))) return false;\n    String rest = l.substring(l.indexOf(\"://\") + 3);\n    return !rest.contains(\"file:\") && !rest.contains(\"ftp:\") && !rest.contains(\"gopher:\")\n        && !rest.contains(\"jar:\") && !rest.contains(\"netdoc:\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Audit full http(s) origin URLs for embedded scheme keywords in query/path.","Pass secondary redirect targets through a separate signed parameter, not originUrl.","Treat nested schemes as SSRF attempts in review."],"tags":["openapi","url-validation","ssrf","security"],"backgroundTag":null,"analyzedSha":"96fb33f5ec68516da0b0147da06b2eb0419e063a","analyzedAt":"2026-08-14T00:04:16.786Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}