Tencent/WeKnora · error

docreader address failed SSRF validation: %w

Error message

docreader address failed SSRF validation: %w

What it means

NewHTTPDocumentReader validates the supplied docreader base URL with secutils.ValidateURLForSSRF before storing it and returns this wrapped error when the URL is not SSRF-safe (e.g. private/loopback/link-local addresses, disallowed scheme, or otherwise forbidden target). This guards against server-side request forgery where an attacker-controlled docreader address would make the service fetch from internal infrastructure.

Source

Thrown at internal/infrastructure/docparser/http_parser.go:69

	MarkdownContent string            `json:"markdown_content"`
	ImageRefs       []httpImageRef    `json:"image_refs,omitempty"`
	ImageDirPath    string            `json:"image_dir_path,omitempty"`
	Metadata        map[string]string `json:"metadata,omitempty"`
	Error           string            `json:"error,omitempty"`
}

// HTTPDocumentReader implements DocumentReader over HTTP/JSON.
type HTTPDocumentReader struct {
	mu      sync.RWMutex
	baseURL string
	client  *http.Client
}

func NewHTTPDocumentReader(baseURL string) (*HTTPDocumentReader, error) {
	baseURL = strings.TrimSuffix(strings.TrimSpace(baseURL), "/")
	if baseURL != "" {
		if err := secutils.ValidateURLForSSRF(baseURL); err != nil {
			return nil, fmt.Errorf("docreader address failed SSRF validation: %w", err)
		}
	}
	clientCfg := secutils.DefaultSSRFSafeHTTPClientConfig()
	clientCfg.Timeout = 5 * time.Minute
	p := &HTTPDocumentReader{
		baseURL: baseURL,
		client:  secutils.NewSSRFSafeHTTPClient(clientCfg),
	}
	if p.baseURL != "" {
		logger.Infof(context.Background(), "INFO: HTTP docreader base URL: %s", p.baseURL)
	}
	return p, nil
}

func (p *HTTPDocumentReader) base() string {
	p.mu.RLock()
	defer p.mu.RUnlock()
	return p.baseURL

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Point the docreader address at a public/allowed URL that passes ValidateURLForSSRF (proper public host, https scheme)
  2. Read the wrapped validation error to see exactly which rule failed (scheme, private IP, loopback, etc.) and adjust the URL accordingly
  3. If the docreader legitimately runs internally, deploy it behind an approved public gateway/proxy endpoint rather than passing the internal address directly
  4. Fix malformed URLs: ensure a single scheme (http:// or https://), no whitespace, no trailing garbage before validation
  5. If this is a trusted local dev setup, use the configuration mechanism intended for dev mode instead of bypassing validation

Example fix

// before
reader, err := docparser.NewHTTPDocumentReader("http://127.0.0.1:8000")
// error: docreader address failed SSRF validation: loopback address forbidden
// after
reader, err := docparser.NewHTTPDocumentReader("https://docreader.example.com")
Defensive patterns

Strategy: validation

Validate before calling

url := strings.TrimSuffix(strings.TrimSpace(cfg.DocreaderBaseURL), "/")
if url != "" {
    if err := secutils.ValidateURLForSSRF(url); err != nil {
        return nil, fmt.Errorf("configured docreader url rejected: %w", err)
    }
}
reader, err := docparser.NewHTTPDocumentReader(url)

Try / catch

reader, err := docparser.NewHTTPDocumentReader(cfg.DocreaderBaseURL)
if err != nil {
    var ssrfErr error
    if strings.Contains(err.Error(), "failed SSRF validation") {
        return nil, fmt.Errorf("config error: docreader url %q is not SSRF-safe: %w", cfg.DocreaderBaseURL, err)
    }
    _ = ssrfErr
    return nil, err
}

Prevention

When it happens

Trigger: Constructing HTTPDocumentReader via NewHTTPDocumentReader (from initDocReaderClient or ResolveDocumentReader) with a baseURL that points at localhost/127.0.0.1, a private RFC1918 address, a metadata endpoint like 169.254.169.254, a non-http(s) scheme, or a malformed URL that fails validation.

Common situations: Local development pointing the docreader at http://localhost:8000 which SSRF policy rejects in production-mode builds; operator configures an internal cluster IP or k8s service name that the validator classifies as private; config/env value contains a typo or extra scheme making validation fail; dynamic address resolution (ResolveDocumentReader) yields a private IP for a hostname.

Related errors


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