{"record":{"id":"8654a71ae1751cd9","repo":"Tencent/WeKnora","slug":"mineru-url-blocked-by-ssrf-check-v","errorCode":null,"errorMessage":"MinerU URL blocked by SSRF check: %v","messagePattern":"MinerU URL blocked by SSRF check: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/infrastructure/docparser/mineru_converter.go","lineNumber":356,"sourceCode":"\t}\n\n\treturn refs, mdContent\n}\n\n// logMinerUResponseStructure logs the structure of the MinerU API response.\nfunc (c *MinerUReader) logMinerUResponseStructure(obj interface{}, prefix string) {\n\tlogResponseStructure(\"MinerU\", obj, prefix)\n}\n\n// validateMinerUOutboundURL rejects MinerU endpoints that would reach private\n// or otherwise restricted hosts when parsed or probed from the app server.\nfunc validateMinerUOutboundURL(rawURL string) error {\n\trawURL = strings.TrimSpace(rawURL)\n\tif rawURL == \"\" {\n\t\treturn nil\n\t}\n\tif err := utils.ValidateURLForSSRF(rawURL); err != nil {\n\t\treturn fmt.Errorf(\"MinerU URL blocked by SSRF check: %v\", err)\n\t}\n\treturn nil\n}\n\n// PingMinerU checks if the self-hosted MinerU service is reachable.\nfunc PingMinerU(endpoint string) (bool, string) {\n\tendpoint = strings.TrimRight(endpoint, \"/\")\n\tif endpoint == \"\" {\n\t\treturn false, \"未配置 MinerU 端点\"\n\t}\n\tif err := validateMinerUOutboundURL(endpoint); err != nil {\n\t\treturn false, err.Error()\n\t}\n\tclient := utils.NewSSRFSafeHTTPClient(utils.SSRFSafeHTTPClientConfig{\n\t\tTimeout:      5 * time.Second,\n\t\tMaxRedirects: 5,\n\t})\n\tresp, err := client.Get(endpoint + \"/docs\")","sourceCodeStart":338,"sourceCodeEnd":374,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/infrastructure/docparser/mineru_converter.go#L338-L374","documentation":"validateMinerUOutboundURL runs the configured MinerU endpoint through utils.ValidateURLForSSRF before any outbound request is made. If the URL resolves to a loopback, link-local, private, or otherwise forbidden address (or uses a disallowed scheme), the converter refuses to call it and reports this error. It is a deliberate security guard against SSRF attacks, not a MinerU failure.","triggerScenarios":"Calling Read or PingMinerU with a MinerU endpoint that is localhost/127.0.0.1, ::1, 169.254.x.x, 10.x/172.16-31.x/192.168.x, a metadata IP, or a non-http(s) scheme; also hit by the test TestValidateMinerUOutboundURL_RejectsLoopback.","commonSituations":"Developers pointing MinerU config at a local self-hosted service (http://localhost:8080) in an environment where the SSRF guard blocks loopback; misconfigured env var left as a development URL in production; endpoint set to a metadata service address.","solutions":["Set the MinerU endpoint to the real internal/external service hostname (not loopback/link-local), e.g. http://mineru.internal:8000","If a private-network MinerU is intentional, relax the SSRF allowlist configuration explicitly (allowed CIDRs) rather than bypassing the check","Confirm the scheme is http or https; strip whitespace (the validator trims, but empty URLs pass silently as nil)","If this error appears in unit tests, it is expected behavior — use a public-shaped test URL or adjust the test"],"exampleFix":"// before\nendpoint := \"http://127.0.0.1:8888\"\n// after\nendpoint := os.Getenv(\"MINERU_ENDPOINT\") // e.g. \"http://mineru.internal.svc:8888\"","handlingStrategy":"validation","validationCode":"if err := validateMinerUOutboundURL(cfg.Endpoint); err != nil {\n    return nil, fmt.Errorf(\"invalid MinerU endpoint at startup: %w\", err)\n}","typeGuard":"func isSSRFBlocked(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"blocked by SSRF check\")\n}","tryCatchPattern":"res, err := reader.Read(ctx, req)\nif isSSRFBlocked(err) {\n    logger.Error(\"MinerU endpoint rejected by SSRF guard; fix configuration, do not bypass\")\n    return nil, err\n}","preventionTips":["Validate the MinerU endpoint at config-load/startup time, not first request","Never point production config at localhost or link-local addresses","Use DNS names of internal services instead of raw private IPs when allowlisting is required","Keep an explicit, reviewed SSRF allowlist for intentional private deployments"],"tags":["security","ssrf","validation","mineru","configuration"],"backgroundTag":"ssrf-url-blocked","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}