From 6cd27493356f37e8e3e1e4c569b7715e2dfdf87b Mon Sep 17 00:00:00 2001 From: Nakul Bharti Date: Mon, 19 Jan 2026 10:44:14 +0530 Subject: [PATCH] fix(utils): santize host when target has host port (#6759) --- pkg/protocols/utils/fields.go | 48 ++++++++-- pkg/protocols/utils/fields_test.go | 138 +++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+), 6 deletions(-) create mode 100644 pkg/protocols/utils/fields_test.go diff --git a/pkg/protocols/utils/fields.go b/pkg/protocols/utils/fields.go index a2b1a80d7..4cd90f0c6 100644 --- a/pkg/protocols/utils/fields.go +++ b/pkg/protocols/utils/fields.go @@ -1,6 +1,9 @@ package utils import ( + "net" + "strings" + "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" iputil "github.com/projectdiscovery/utils/ip" urlutil "github.com/projectdiscovery/utils/url" @@ -28,17 +31,21 @@ func GetJsonFieldsFromURL(URL string) JsonFields { URL: parsed.String(), Path: parsed.Path, } + + host := parsed.Host + host, fields.Port = extractHostPort(host, fields.Port) + if fields.Port == "" { fields.Port = "80" if fields.Scheme == "https" { fields.Port = "443" } } - if iputil.IsIP(parsed.Host) { - fields.Ip = parsed.Host + if iputil.IsIP(host) { + fields.Ip = host } - fields.Host = parsed.Host + fields.Host = host return fields } @@ -56,16 +63,45 @@ func GetJsonFieldsFromMetaInput(ctx *contextargs.MetaInput) JsonFields { fields.Scheme = parsed.Scheme fields.URL = parsed.String() fields.Path = parsed.Path + + host := parsed.Host + host, fields.Port = extractHostPort(host, fields.Port) + if fields.Port == "" { fields.Port = "80" if fields.Scheme == "https" { fields.Port = "443" } } - if iputil.IsIP(parsed.Host) { - fields.Ip = parsed.Host + if iputil.IsIP(host) { + fields.Ip = host } - fields.Host = parsed.Host + fields.Host = host return fields } + +func extractHostPort(host, port string) (string, string) { + if !strings.Contains(host, ":") { + return host, port + } + if strings.HasPrefix(host, "[") { + if idx := strings.Index(host, "]:"); idx != -1 { + if port == "" { + port = host[idx+2:] + } + return host[1:idx], port + } + if strings.HasSuffix(host, "]") { + return host[1 : len(host)-1], port + } + return host, port + } + if h, p, err := net.SplitHostPort(host); err == nil { + if port == "" { + port = p + } + return h, port + } + return host, port +} diff --git a/pkg/protocols/utils/fields_test.go b/pkg/protocols/utils/fields_test.go new file mode 100644 index 000000000..17cd96a2e --- /dev/null +++ b/pkg/protocols/utils/fields_test.go @@ -0,0 +1,138 @@ +package utils + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetJsonFieldsFromURL_HostPortExtraction(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + input string + expectedHost string + expectedPort string + }{ + { + name: "URL with scheme and port", + input: "http://example.com:8080/path", + expectedHost: "example.com", + expectedPort: "8080", + }, + { + name: "URL with scheme no port", + input: "https://example.com/path", + expectedHost: "example.com", + expectedPort: "443", + }, + { + name: "host:port without scheme", + input: "example.com:8080", + expectedHost: "example.com", + expectedPort: "8080", + }, + { + name: "host:port with standard HTTPS port", + input: "example.com:443", + expectedHost: "example.com", + expectedPort: "443", + }, + { + name: "IPv4 with port", + input: "192.168.1.1:8080", + expectedHost: "192.168.1.1", + expectedPort: "8080", + }, + { + name: "IPv6 with port", + input: "[2001:db8::1]:8080", + expectedHost: "2001:db8::1", + expectedPort: "8080", + }, + { + name: "localhost with port", + input: "localhost:3000", + expectedHost: "localhost", + expectedPort: "3000", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + fields := GetJsonFieldsFromURL(tt.input) + + assert.Equal(t, tt.expectedHost, fields.Host) + assert.Equal(t, tt.expectedPort, fields.Port) + }) + } +} + +func TestExtractHostPort(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + host string + port string + expectedHost string + expectedPort string + }{ + { + name: "host without port", + host: "example.com", + port: "", + expectedHost: "example.com", + expectedPort: "", + }, + { + name: "host with port", + host: "example.com:8080", + port: "", + expectedHost: "example.com", + expectedPort: "8080", + }, + { + name: "port already set", + host: "example.com:8080", + port: "443", + expectedHost: "example.com", + expectedPort: "443", + }, + { + name: "IPv6 with port", + host: "[::1]:8080", + port: "", + expectedHost: "::1", + expectedPort: "8080", + }, + { + name: "IPv6 without port", + host: "[::1]", + port: "", + expectedHost: "::1", + expectedPort: "", + }, + { + name: "IPv4 with port", + host: "192.168.1.1:8080", + port: "", + expectedHost: "192.168.1.1", + expectedPort: "8080", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + host, port := extractHostPort(tt.host, tt.port) + + assert.Equal(t, tt.expectedHost, host) + assert.Equal(t, tt.expectedPort, port) + }) + } +}