{"record":{"id":"d953e631c1ca23d3","repo":"shadow1ng/fscan","slug":"w-w-errinvalidurl","errorCode":null,"errorMessage":"%w: %w (ErrInvalidURL)","messagePattern":"%w: %w \\(ErrInvalidURL\\)","errorType":"validation","errorClass":"ErrInvalidURL","httpStatus":null,"severity":"error","filePath":"webscan/web_scan.go","lineNumber":112,"sourceCode":"}\n\n// buildTargetURL 构建规范的目标URL\nfunc buildTargetURL(info *common.HostInfo) (string, error) {\n\t// 自动构建URL\n\tif info.URL == \"\" {\n\t\tprotocol := protocolHTTP\n\t\tif isTLSPort(info.Port) {\n\t\t\tprotocol = protocolHTTPS\n\t\t}\n\t\tinfo.URL = protocol + net.JoinHostPort(info.Host, fmt.Sprint(info.Port))\n\t} else if !hasProtocolPrefix(info.URL) {\n\t\tinfo.URL = protocolHTTP + normalizeSchemelessWebTarget(info.URL)\n\t}\n\n\t// 解析URL以提取基础部分\n\tparsedURL, err := url.Parse(info.URL)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"%w: %w\", ErrInvalidURL, err)\n\t}\n\tif parsedURL.Hostname() == \"\" {\n\t\treturn \"\", fmt.Errorf(\"%w: empty host\", ErrInvalidURL)\n\t}\n\tportStr := parsedURL.Port()\n\tif portStr == \"\" {\n\t\tif hasMalformedWebURLPort(parsedURL.Host) {\n\t\t\treturn \"\", fmt.Errorf(\"%w: invalid port\", ErrInvalidURL)\n\t\t}\n\t} else {\n\t\tport, err := strconv.Atoi(portStr)\n\t\tif err != nil || port < 1 || port > 65535 {\n\t\t\treturn \"\", fmt.Errorf(\"%w: invalid port %q\", ErrInvalidURL, portStr)\n\t\t}\n\t}\n\tparsedURL.Host = normalizeWebURLHost(parsedURL.Host)\n\n\treturn fmt.Sprintf(\"%s://%s\", parsedURL.Scheme, parsedURL.Host), nil","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/shadow1ng/fscan/blob/95cc12e753bf43de7004e5aef42a9ffba3934303/webscan/web_scan.go#L94-L130","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Run url.Parse on the offending URL locally to see the exact parse failure in the wrapped cause.","Ensure the target includes a valid scheme (http:// or https://) and a resolvable hostname with no spaces or control characters.","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.","Validate targets in a pre-scan pass (url.Parse + hostname/port checks) and skip or fix invalid entries instead of failing the run."],"exampleFix":"// before\ninfo.URL = \"http://host:99999\" // ErrInvalidURL: invalid port\n// after\ninfo.URL = \"https://host\" // or http://host:8443, port within 1-65535","handlingStrategy":"validation","validationCode":"func validateTargetURL(raw string) error {\n    if !strings.HasPrefix(strings.ToLower(raw), \"http://\") && !strings.HasPrefix(strings.ToLower(raw), \"https://\") {\n        raw = \"http://\" + raw\n    }\n    u, err := url.Parse(raw)\n    if err != nil {\n        return fmt.Errorf(\"unparsable: %w\", err)\n    }\n    if u.Hostname() == \"\" {\n        return fmt.Errorf(\"empty host\")\n    }\n    if p := u.Port(); p != \"\" {\n        n, err := strconv.Atoi(p)\n        if err != nil || n < 1 || n > 65535 {\n            return fmt.Errorf(\"invalid port %q\", p)\n        }\n    }\n    return nil\n}","typeGuard":"func isWellFormedWebTarget(raw string) bool {\n    return validateTargetURL(raw) == nil\n}","tryCatchPattern":"base, err := buildTargetURL(info)\nif err != nil {\n    if errors.Is(err, ErrInvalidURL) {\n        log.Printf(\"skipping invalid target %q: %v\", info.URL, err)\n        return\n    }\n    return err\n}","preventionTips":["Validate every target with url.Parse plus hostname/port checks before adding to scan lists","Always include an explicit scheme and a numeric port within 1-65535","Strip spaces and control characters from targets at ingestion time","Use errors.Is(err, ErrInvalidURL) to branch on invalid targets instead of string matching"],"tags":["go","webscan","url-validation","input-validation"],"backgroundTag":"invalid-url","analyzedSha":"95cc12e753bf43de7004e5aef42a9ffba3934303","analyzedAt":"2026-09-06T17:07:30.094Z","contentChangedAt":"2026-09-06T17:07:30.094Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}