Tencent/WeKnora · error

zip URL blocked by SSRF check: %v

Error message

zip URL blocked by SSRF check: %v

What it means

The full_zip_url returned by MinerU Cloud was rejected by utils.ValidateURLForSSRF before download in downloadAndExtractZip (mineru_cloud_converter.go:357). The library blocks URLs that resolve to private/loopback/link-local addresses or disallowed schemes to prevent server-side request forgery. If the URL is a legitimate public storage link, this indicates an over-restrictive SSRF policy or a DNS/environment quirk (e.g. internal DNS resolving the storage host to a private IP).

Source

Thrown at internal/infrastructure/docparser/mineru_cloud_converter.go:357

		return "", nil, fmt.Errorf("MinerU Cloud state=done but no markdown/content or full_zip_url")
	}

	md, imageRefs, err := downloadAndExtractZip(item.FullZipURL)
	if err != nil {
		return "", nil, fmt.Errorf("extract zip: %w", err)
	}

	logger.Infof(context.Background(), "[MinerUCloud] parsed (zip), markdown=%d chars, images=%d", len(md), len(imageRefs))
	return md, imageRefs, nil
}

// --- ZIP handling ---

var imgRefPattern = regexp.MustCompile(`!\[[^\]]*\]\(([^)]+)\)`)

func downloadAndExtractZip(zipURL string) (string, []types.ImageRef, error) {
	if err := utils.ValidateURLForSSRF(zipURL); err != nil {
		return "", nil, fmt.Errorf("zip URL blocked by SSRF check: %v", err)
	}
	client := utils.NewSSRFSafeHTTPClient(utils.SSRFSafeHTTPClientConfig{Timeout: 120 * time.Second, MaxRedirects: 5})
	resp, err := client.Get(zipURL)
	if err != nil {
		return "", nil, fmt.Errorf("download zip: %w", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return "", nil, fmt.Errorf("download zip status %d", resp.StatusCode)
	}

	zipData, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", nil, fmt.Errorf("read zip body: %w", err)
	}

	zr, err := zip.NewReader(bytes.NewReader(zipData), int64(len(zipData)))
	if err != nil {

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Inspect the wrapped %v cause to see which SSRF rule fired (scheme, private IP, port).
  2. If the storage host is legitimately internal, add it to the SSRF allowlist or configure the allowed CIDR ranges in the SSRF client config.
  3. Ensure MinerU Cloud returns publicly resolvable full_zip_url values (check the deployment's public endpoint configuration).
  4. Never bypass SSRF checks for untrusted URLs; only allowlist verified internal storage hosts.

Example fix

// before — blanket bypass (unsafe)
// md, imageRefs, err := download zip without check
// after — allowlist the trusted internal storage CIDR
utils.NewSSRFSafeHTTPClient(utils.SSRFSafeHTTPClientConfig{
    Timeout:        120 * time.Second,
    MaxRedirects:   5,
    AllowedCIDRs:   []string{"10.0.0.0/8"}, // only if storage is trusted-internal
})
Defensive patterns

Strategy: validation

Validate before calling

u, err := url.Parse(zipURL)
if err != nil {
    return err
}
if u.Scheme != "https" && u.Scheme != "http" {
    return fmt.Errorf("unsupported scheme: %s", u.Scheme)
}
ips, err := net.LookupIP(u.Hostname())
if err != nil {
    return err
}
for _, ip := range ips {
    if ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast() {
        return fmt.Errorf("zip URL resolves to non-public address: %v", ip)
    }
}

Type guard

func isPublicHTTPURL(raw string) bool {
    u, err := url.Parse(raw)
    if err != nil || (u.Scheme != "http" && u.Scheme != "https") {
        return false
    }
    ips, err := net.LookupIP(u.Hostname())
    if err != nil {
        return false
    }
    for _, ip := range ips {
        if ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast() {
            return false
        }
    }
    return true
}

Try / catch

md, images, err := pollBatchResult(ctx, batchID)
if err != nil {
    if strings.Contains(err.Error(), "zip URL blocked by SSRF check") {
        // expected for internal URLs: use an allowlisted fetcher or fail fast
        return fmt.Errorf("result URL not fetchable from this network: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: extractDoneResult -> downloadAndExtractZip receives a zipURL that fails SSRF validation: non-http(s) scheme, localhost/loopback host, private or link-local IP resolution, metadata endpoints (169.254.169.254), or disallowed ports.

Common situations: Self-hosted/proxied MinerU deployments whose result URLs point at internal storage; test environments using local mock URLs; containers where the storage host resolves to a cluster-internal IP; misconfigured public base URL falling back to an internal address.

Related errors


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