Tencent/WeKnora · error

jsonl URL blocked by SSRF check: %v

Error message

jsonl URL blocked by SSRF check: %v

What it means

fetchResults validates the JSONL result URL returned by the cloud service with utils.ValidateURLForSSRF before downloading. If the URL points at a private/loopback/link-local address or otherwise fails SSRF policy, the download is aborted with this error. This is an intentional security guard protecting the host network from a provider-controlled (or attacker-influenced) URL.

Source

Thrown at internal/infrastructure/docparser/paddleocr_vl_cloud_converter.go:266

	return "", fmt.Errorf("task timed out after %d polls", pollCount)
}

// --- result parsing ---

type paddleOCRVLCloudResultLine struct {
	Result struct {
		LayoutParsingResults []struct {
			Markdown struct {
				Text   string            `json:"text"`
				Images map[string]string `json:"images"`
			} `json:"markdown"`
		} `json:"layoutParsingResults"`
	} `json:"result"`
}

func (c *PaddleOCRVLCloudReader) fetchResults(jsonlURL string) (string, map[string]string, error) {
	if err := utils.ValidateURLForSSRF(jsonlURL); err != nil {
		return "", nil, fmt.Errorf("jsonl URL blocked by SSRF check: %v", err)
	}
	client := utils.NewSSRFSafeHTTPClient(utils.SSRFSafeHTTPClientConfig{Timeout: 120 * time.Second, MaxRedirects: 5})
	resp, err := client.Get(jsonlURL)
	if err != nil {
		return "", nil, fmt.Errorf("download jsonl: %w", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return "", nil, fmt.Errorf("download jsonl status %d", resp.StatusCode)
	}
	data, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", nil, fmt.Errorf("read jsonl body: %w", err)
	}

	texts := make([]string, 0)
	images := make(map[string]string)
	for _, line := range strings.Split(strings.TrimSpace(string(data)), "\n") {

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Inspect the URL in the error (%v wraps the SSRF validation reason) and confirm who produced it — a private IP from the public cloud provider signals compromise or misconfiguration.
  2. For self-hosted deployments, add the internal result host to the SSRF allowlist (utils.ValidateURLForSSRF allowlist configuration) instead of disabling the check.
  3. Ensure the provider returns public, HTTPS result URLs (check base-URL/endpoint configuration).
  4. Never disable the SSRF check to make the error go away on internet-facing deployments.
  5. In tests, point the SSRF allowlist at the local mock rather than disabling validation.

Example fix

// before
url, err := reader.Read(ctx, file)
// err: jsonl URL blocked by SSRF check: private address

// after: allowlist the self-hosted result host before calling
utils.AddSSRFAllowedHost("results.ocr.internal")
url, err := reader.Read(ctx, file)
if err != nil {
    if strings.Contains(err.Error(), "SSRF") {
        return fmt.Errorf("provider returned non-public result URL; refusing download: %w", err)
    }
    return err
}
Defensive patterns

Strategy: validation

Validate before calling

// caller-side sanity check mirroring the library guard
u, err := url.Parse(resultURL)
if err != nil || (u.Scheme != "https" && u.Scheme != "http") {
    return fmt.Errorf("suspicious result URL: %q", resultURL)
}
if utils.ValidateURLForSSRF(resultURL) != nil {
    return fmt.Errorf("result URL is not publicly reachable; refusing download")
}

Type guard

func isPublicHTTPResultURL(raw string) bool {
    u, err := url.Parse(raw)
    if err != nil || (u.Scheme != "https" && u.Scheme != "http") {
        return false
    }
    return utils.ValidateURLForSSRF(raw) == nil
}

Try / catch

if err := reader.Read(ctx, file); err != nil {
    if strings.Contains(err.Error(), "SSRF") {
        log.Warnf("provider returned blocked result URL: %v", err)
        return ErrUntrustedResultURL
    }
    return err
}

Prevention

When it happens

Trigger: Read succeeds through pollJob, but the returned jsonl URL fails ValidateURLForSSRF — e.g. it resolves to 127.0.0.1, 10.x/172.16.x/192.168.x, 169.254.x, or uses a blocked scheme/port.

Common situations: Self-hosted PaddleOCR deployment behind an internal network where the genuine result URL is a private IP (SSRF guard is correct but blocks a legitimate internal host); DNS rebinding or a compromised/misconfigured provider returning internal URLs; a stub/mock server returning placeholder URLs like http://localhost/result.jsonl in tests.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/d6705be6fe87d450. Report an issue: GitHub.