{"record":{"id":"fdbc9d6470f6cf7f","repo":"shadow1ng/fscan","slug":"w-invalid-port-errinvalidurl","errorCode":null,"errorMessage":"%w: invalid port (ErrInvalidURL)","messagePattern":"%w: invalid port \\(ErrInvalidURL\\)","errorType":"validation","errorClass":"ErrInvalidURL","httpStatus":null,"severity":"error","filePath":"webscan/web_scan.go","lineNumber":120,"sourceCode":"\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\n}\n\n// hasProtocolPrefix 检查URL是否包含协议前缀\nfunc hasProtocolPrefix(urlStr string) bool {\n\turlStr = strings.ToLower(urlStr)\n\treturn strings.HasPrefix(urlStr, protocolHTTP) || strings.HasPrefix(urlStr, protocolHTTPS)\n}\n","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/shadow1ng/fscan/blob/95cc12e753bf43de7004e5aef42a9ffba3934303/webscan/web_scan.go#L102-L138","documentation":"buildTargetURL detects a structurally malformed port when the URL carries no explicit port but its host part embeds one in an unparseable form (via hasMalformedWebURLPort). It returns ErrInvalidURL so callers can distinguish bad input from transport failures.","triggerScenarios":"URLs like \"http://example.com:/path\" or hosts such as \"example.com:port\" where url.Parse yields an empty Port() but the raw Host contains a colon segment that is not a valid decimal port.","commonSituations":"Template string bugs like fmt.Sprintf(\"http://%s:%s\", host, portVar) with empty/placeholder port, copy-paste of URLs with a trailing colon, or port placeholders like :<port> left unsubstituted.","solutions":["Fix the target URL so the port is either absent or a valid decimal (1-65535), e.g. http://example.com:8080","Check template/placeholder substitution produced a real port value","Validate ports with strconv.Atoi and range check before submitting the target"],"exampleFix":"// before\nurl := fmt.Sprintf(\"http://%s:%s\", host, port) // port may be empty\n// after\nif port != \"\" {\n    n, err := strconv.Atoi(port)\n    if err != nil || n < 1 || n > 65535 {\n        return fmt.Errorf(\"invalid port %q\", port)\n    }\n}\nurl := fmt.Sprintf(\"http://%s\", net.JoinHostPort(host, port))","handlingStrategy":"validation","validationCode":"u, err := url.Parse(target)\nif err != nil || (u.Port() == \"\" && strings.Contains(u.Host, \":\")) {\n    return fmt.Errorf(\"target %q has malformed port\", target)\n}","typeGuard":null,"tryCatchPattern":"if err := WebScan(target); errors.Is(err, ErrInvalidURL) {\n    log.Printf(\"invalid target URL %q: %v\", target, err)\n    return nil\n}","preventionTips":["Use net.JoinHostPort(host, port) instead of Sprintf concatenation","Verify template placeholders for ports are substituted","Reject URLs with trailing colons in input validation"],"tags":["url","port","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-14T05:17:10.506Z"}