Tencent/WeKnora · error

blocked by SSRF policy: %w

Error message

blocked by SSRF policy: %w

What it means

This error is returned when fetching a remote image is blocked because the target URL fails SSRF (server-side request forgery) validation. Whitelisted image hosts skip the check; all other URLs must pass secutils.ValidateURLForSSRF, which rejects private/loopback/link-local IPs and disallowed schemes. This is a deliberate security control, not an incidental failure.

Source

Thrown at internal/infrastructure/docparser/image_resolver.go:786

// fetchAndStoreRemoteImage applies the SSRF policy, downloads the image,
// rejects icons and uploads the bytes to storage.
//
// Both the Markdown and the HTML scan go through here so that the SSRF check,
// the icon filter and the whitelist behaviour cannot drift apart between the
// two syntaxes.
func fetchAndStoreRemoteImage(
	ctx context.Context,
	client *http.Client,
	fileSvc interfaces.FileService,
	tenantID uint64,
	imgURL string,
) (*remoteImageResult, error) {
	whitelisted := isWhitelistedImageHost(imgURL)

	if !whitelisted {
		if err := secutils.ValidateURLForSSRF(imgURL); err != nil {
			return nil, fmt.Errorf("blocked by SSRF policy: %w", err)
		}
	}

	data, mimeType, err := downloadImage(ctx, client, imgURL)
	if err != nil {
		return nil, fmt.Errorf("download: %w", err)
	}

	if isIconImage(data) {
		return nil, errRemoteImageIsIcon
	}

	if whitelisted {
		return &remoteImageResult{MimeType: mimeType, KeepOriginalURL: true}, nil
	}

	ext := extFromMime(mimeType)
	if ext == "" {

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Check the URL: it must be a public http(s) address resolvable to a public IP
  2. Add the trusted host to the image-host whitelist if it is legitimately required
  3. Do not bypass the validator for user-supplied URLs (SSRF risk)
  4. Test the exact URL with secutils.ValidateURLForSSRF to see the specific rejection reason

Example fix

// before
imgURL := "http://localhost:8080/logo.png" // blocked
// after
imgURL := "https://cdn.example.com/logo.png" // public URL passes SSRF validation
Defensive patterns

Strategy: validation

Validate before calling

u, err := url.Parse(imgURL)
if err != nil || (u.Scheme != "http" && u.Scheme != "https") {
    return fmt.Errorf("reject non-http(s) url: %q", imgURL)
}
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("internal address not allowed: %v", ip)
    }
}

Try / catch

res, err := resolver.fetchAndStoreRemoteImage(ctx, imgURL, tenantID)
if err != nil {
    if strings.Contains(err.Error(), "blocked by SSRF policy") {
        return fmt.Errorf("image url %q rejected by security policy: %w", imgURL, err)
    }
    return err
}

Prevention

When it happens

Trigger: fetchAndStoreRemoteImage called with an imgURL whose host is not whitelisted and which resolves to a private/loopback/metadata IP, uses a non-http(s) scheme, or is otherwise rejected by the SSRF validator.

Common situations: Documents containing images pointing at internal hosts (169.254.169.254, localhost, 10.x), file:// or other schemes, redirects to internal addresses, hosts not added to the whitelist.

Related errors


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