Tencent/WeKnora · error

invalid scheme: %s (only http/https allowed)

Error message

invalid scheme: %s (only http/https allowed)

What it means

ValidateURLForSSRF only permits http and https schemes. Any other scheme — file://, gopher://, ftp://, dict://, ldap:// — is rejected with 'invalid scheme'. This is a core SSRF defense: a whitelist relaxes host/IP restrictions only and must never legitimize dangerous non-HTTP schemes that can reach local files or internal services.

Source

Thrown at internal/utils/security.go:1205

	if !strings.Contains(normalized, "://") {
		normalized = "https://" + normalized
	}

	parsed, err := url.Parse(normalized)
	if err != nil {
		return fmt.Errorf("invalid URL: %w", err)
	}

	hostname := parsed.Hostname()
	if hostname == "" {
		return fmt.Errorf("URL has no hostname")
	}

	// A whitelist relaxes host/IP restrictions only. It must never turn other
	// schemes (file://, gopher://, etc.) into valid outbound request targets.
	scheme := strings.ToLower(parsed.Scheme)
	if scheme != "http" && scheme != "https" {
		return fmt.Errorf("invalid scheme: %s (only http/https allowed)", scheme)
	}

	// If the host is whitelisted, skip the heavy checks.
	if IsSSRFWhitelisted(hostname) {
		return nil
	}

	// Delegate to the full SSRF validation (uses the normalised URL).
	if safe, reason := isSSRFSafeURL(normalized); !safe {
		return fmt.Errorf("SSRF validation failed: %s", reason)
	}
	return nil
}

// IsSystemProxy 判断是否为系统代理
func IsSystemProxy(host string) bool {
	proxyCfg := httpproxy.FromEnvironment()
	for _, proxyUrl := range []string{

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Use https:// (or http:// for explicitly internal/trusted endpoints) as the URL scheme
  2. Do not use the storage client for local file access — read files directly with os.Open instead
  3. Verify the endpoint config value wasn't accidentally prefixed with the wrong scheme
  4. Note the scheme check is case-insensitive (HTTP:// is fine); the scheme itself is the problem, not casing

Example fix

// before
endpoint := "file:///data/export"
client, err := newOSSClient(endpoint, ...)
// after
endpoint := "https://oss.example.com"
client, err := newOSSClient(endpoint, ...)
Defensive patterns

Strategy: validation

Validate before calling

n := endpoint
if !strings.Contains(n, "://") { n = "https://" + n }
u, err := url.Parse(n)
if err != nil {
    return err
}
s := strings.ToLower(u.Scheme)
if s != "http" && s != "https" {
    return fmt.Errorf("scheme %q not allowed; use http/https", s)
}

Type guard

func isHTTPScheme(raw string) bool {
    if !strings.Contains(raw, "://") { raw = "https://" + raw }
    u, err := url.Parse(raw)
    return err == nil && (strings.EqualFold(u.Scheme, "http") || strings.EqualFold(u.Scheme, "https"))
}

Prevention

When it happens

Trigger: A storage endpoint or connectivity-check URL uses a non-http(s) scheme, e.g. 'file:///etc/passwd', 'gopher://127.0.0.1:70', 'ftp://files.example.com', passed to newS3Client, newOSSClient, newMinioClient, newKS3Client, NewObsFileService, or CheckObsConnectivity.

Common situations: Attempting to point a storage client at a local file path, copying a non-HTTP service URL into the endpoint config, or an attacker-controlled URL reaching the validation function during an SSRF probe.

Related errors


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