shadow1ng/fscan · error · ErrInvalidURL

%w: %w (ErrInvalidURL)

Error message

%w: %w (ErrInvalidURL)

What it means

buildTargetURL in webscan/web_scan.go normalizes a HostInfo into a canonical scheme://host URL and returns ErrInvalidURL wrapped around the underlying cause when url.Parse rejects the input. It also returns ErrInvalidURL for an empty host or an invalid/out-of-range port. The sentinel wrapping lets callers use errors.Is(err, ErrInvalidURL) to detect bad targets.

Source

Thrown at webscan/web_scan.go:112

}

// buildTargetURL 构建规范的目标URL
func buildTargetURL(info *common.HostInfo) (string, error) {
	// 自动构建URL
	if info.URL == "" {
		protocol := protocolHTTP
		if isTLSPort(info.Port) {
			protocol = protocolHTTPS
		}
		info.URL = protocol + net.JoinHostPort(info.Host, fmt.Sprint(info.Port))
	} else if !hasProtocolPrefix(info.URL) {
		info.URL = protocolHTTP + normalizeSchemelessWebTarget(info.URL)
	}

	// 解析URL以提取基础部分
	parsedURL, err := url.Parse(info.URL)
	if err != nil {
		return "", fmt.Errorf("%w: %w", ErrInvalidURL, err)
	}
	if parsedURL.Hostname() == "" {
		return "", fmt.Errorf("%w: empty host", ErrInvalidURL)
	}
	portStr := parsedURL.Port()
	if portStr == "" {
		if hasMalformedWebURLPort(parsedURL.Host) {
			return "", fmt.Errorf("%w: invalid port", ErrInvalidURL)
		}
	} else {
		port, err := strconv.Atoi(portStr)
		if err != nil || port < 1 || port > 65535 {
			return "", fmt.Errorf("%w: invalid port %q", ErrInvalidURL, portStr)
		}
	}
	parsedURL.Host = normalizeWebURLHost(parsedURL.Host)

	return fmt.Sprintf("%s://%s", parsedURL.Scheme, parsedURL.Host), nil

View on GitHub (pinned to 95cc12e753)

Solutions

  1. Run url.Parse on the offending URL locally to see the exact parse failure in the wrapped cause.
  2. Ensure the target includes a valid scheme (http:// or https://) and a resolvable hostname with no spaces or control characters.
  3. Use a numeric port between 1 and 65535, or omit the port; for TLS targets on 443/8443/4443/9443 omit the URL and let the scanner pick https by port.
  4. Validate targets in a pre-scan pass (url.Parse + hostname/port checks) and skip or fix invalid entries instead of failing the run.

Example fix

// before
info.URL = "http://host:99999" // ErrInvalidURL: invalid port
// after
info.URL = "https://host" // or http://host:8443, port within 1-65535
Defensive patterns

Strategy: validation

Validate before calling

func validateTargetURL(raw string) error {
    if !strings.HasPrefix(strings.ToLower(raw), "http://") && !strings.HasPrefix(strings.ToLower(raw), "https://") {
        raw = "http://" + raw
    }
    u, err := url.Parse(raw)
    if err != nil {
        return fmt.Errorf("unparsable: %w", err)
    }
    if u.Hostname() == "" {
        return fmt.Errorf("empty host")
    }
    if p := u.Port(); p != "" {
        n, err := strconv.Atoi(p)
        if err != nil || n < 1 || n > 65535 {
            return fmt.Errorf("invalid port %q", p)
        }
    }
    return nil
}

Type guard

func isWellFormedWebTarget(raw string) bool {
    return validateTargetURL(raw) == nil
}

Try / catch

base, err := buildTargetURL(info)
if err != nil {
    if errors.Is(err, ErrInvalidURL) {
        log.Printf("skipping invalid target %q: %v", info.URL, err)
        return
    }
    return err
}

Prevention

When it happens

Trigger: info.URL (after auto-prefixing http:// if schemeless) cannot be parsed by url.Parse; or the parsed URL has no hostname; or its port fails strconv.Atoi or is outside 1-65535.

Common situations: Targets supplied from CLI or asset lists like "http://exa mple.com" (space), "http://host:99999" (port out of range), "http://host:abc" (non-numeric port), or URLs with stray control characters; HostInfo populated with a host:port string pasted including a path or credentials that break parsing.

Understand the failure class

Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.

Related errors


AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06). Data as JSON: /api/errors/d953e631c1ca23d3. Report an issue: GitHub.