From 8eb7d85bbfbd020e0a9b1592461bdd0a490a0459 Mon Sep 17 00:00:00 2001 From: Ayan Rajpoot Date: Sat, 23 May 2026 09:12:05 +0530 Subject: [PATCH 1/2] refactor: change CustomHeaders to support multiple values per header --- common/httputilz/httputilz.go | 8 ++++---- common/httpx/httpx.go | 28 +++++++++++++++------------- common/httpx/option.go | 4 ++-- runner/runner.go | 12 ++++++------ 4 files changed, 27 insertions(+), 25 deletions(-) diff --git a/common/httputilz/httputilz.go b/common/httputilz/httputilz.go index 94bafa881..492fba691 100644 --- a/common/httputilz/httputilz.go +++ b/common/httputilz/httputilz.go @@ -24,8 +24,8 @@ func DumpRequest(req *retryablehttp.Request) (string, error) { } // ParseRequest from raw string -func ParseRequest(req string, unsafe bool) (method, path string, headers map[string]string, body string, err error) { - headers = make(map[string]string) +func ParseRequest(req string, unsafe bool) (method, path string, headers map[string][]string, body string, err error) { + headers = make(map[string][]string) reader := bufio.NewReader(strings.NewReader(req)) s, err := reader.ReadString('\n') if err != nil { @@ -68,7 +68,7 @@ func ParseRequest(req string, unsafe bool) (method, path string, headers map[str value = strings.TrimSpace(value) } - headers[key] = value + headers[key] = append(headers[key], value) } // Handle case with the full http url in path. In that case, @@ -81,7 +81,7 @@ func ParseRequest(req string, unsafe bool) (method, path string, headers map[str return } path = parts[1] - headers["Host"] = parsed.Host + headers["Host"] = []string{parsed.Host} } else { path = parts[1] } diff --git a/common/httpx/httpx.go b/common/httpx/httpx.go index 15c567f06..7e597720e 100644 --- a/common/httpx/httpx.go +++ b/common/httpx/httpx.go @@ -36,7 +36,7 @@ type HTTPX struct { Filters []Filter Options *Options htmlPolicy *bluemonday.Policy - CustomHeaders map[string]string + CustomHeaders map[string][]string cdn *cdncheck.Client Dialer *fastdialer.Dialer NetworkPolicy *networkpolicy.NetworkPolicy @@ -434,19 +434,21 @@ func (h *HTTPX) NewRequestWithContext(ctx context.Context, method, targetURL str } // SetCustomHeaders on the provided request -func (h *HTTPX) SetCustomHeaders(r *retryablehttp.Request, headers map[string]string) { - for name, value := range headers { - switch strings.ToLower(name) { - case "host": - r.Host = value - if h.Options.Unsafe { - r.Header.Set("Host", value) +func (h *HTTPX) SetCustomHeaders(r *retryablehttp.Request, headers map[string][]string) { + for name, values := range headers { + for _, value := range values { + switch strings.ToLower(name) { + case "host": + r.Host = value + if h.Options.Unsafe { + r.Header.Add("Host", value) + } + case "cookie": + // cookies are set in the default branch, and reset during the follow redirect flow + fallthrough + default: + r.Header.Add(name, value) } - case "cookie": - // cookies are set in the default branch, and reset during the follow redirect flow - fallthrough - default: - r.Header.Set(name, value) } } if h.Options.RandomAgent { diff --git a/common/httpx/option.go b/common/httpx/option.go index fb1087296..56d11cbc0 100644 --- a/common/httpx/option.go +++ b/common/httpx/option.go @@ -36,7 +36,7 @@ type Options struct { Timeout time.Duration // RetryMax is the maximum number of retries RetryMax int - CustomHeaders map[string]string + CustomHeaders map[string][]string // VHostSimilarityRatio 1 - 100 VHostSimilarityRatio int FollowRedirects bool @@ -90,7 +90,7 @@ func (options *Options) parseCustomCookies() { // parse and fill the custom field for k, v := range options.CustomHeaders { if strings.EqualFold(k, "cookie") { - req := http.Request{Header: http.Header{"Cookie": []string{v}}} + req := http.Request{Header: http.Header{"Cookie": v}} options.customCookies = req.Cookies() } } diff --git a/runner/runner.go b/runner/runner.go index 352e4c74c..6d6110ec6 100644 --- a/runner/runner.go +++ b/runner/runner.go @@ -30,14 +30,14 @@ import ( "github.com/PuerkitoBio/goquery" "github.com/corona10/goimagehash" "github.com/gocarina/gocsv" + "github.com/happyhackingspace/dit" "github.com/mfonda/simhash" asnmap "github.com/projectdiscovery/asnmap/libs" "github.com/projectdiscovery/fastdialer/fastdialer" + "github.com/projectdiscovery/httpx/common/authprovider" "github.com/projectdiscovery/httpx/common/customextract" "github.com/projectdiscovery/httpx/common/hashes/jarm" "github.com/projectdiscovery/httpx/common/inputformats" - "github.com/happyhackingspace/dit" - "github.com/projectdiscovery/httpx/common/authprovider" "github.com/projectdiscovery/httpx/static" "github.com/projectdiscovery/mapcidr/asn" "github.com/projectdiscovery/networkpolicy" @@ -238,12 +238,12 @@ func New(options *Options) (*Runner, error) { httpxOptions.Protocol = httpx.Proto(options.Protocol) var key, value string - httpxOptions.CustomHeaders = make(map[string]string) + httpxOptions.CustomHeaders = make(map[string][]string) for _, customHeader := range options.CustomHeaders { tokens := strings.SplitN(customHeader, ":", two) // rawhttp skips all checks if options.Unsafe { - httpxOptions.CustomHeaders[customHeader] = "" + httpxOptions.CustomHeaders[customHeader] = []string{""} continue } @@ -253,7 +253,7 @@ func New(options *Options) (*Runner, error) { } key = strings.TrimSpace(tokens[0]) value = strings.TrimSpace(tokens[1]) - httpxOptions.CustomHeaders[key] = value + httpxOptions.CustomHeaders[key] = append(httpxOptions.CustomHeaders[key], value) } httpxOptions.SniName = options.SniName @@ -278,7 +278,7 @@ func New(options *Options) (*Runner, error) { scanopts.Methods = append(scanopts.Methods, rrMethod) scanopts.RequestURI = rrPath for name, value := range rrHeaders { - httpxOptions.CustomHeaders[name] = value + httpxOptions.CustomHeaders[name] = append(httpxOptions.CustomHeaders[name], value...) } scanopts.RequestBody = rrBody options.rawRequest = string(rawRequest) From 3735519769b040cdcd21d42f847947e038bd799c Mon Sep 17 00:00:00 2001 From: Ayan Rajpoot Date: Sat, 23 May 2026 11:22:28 +0530 Subject: [PATCH 2/2] fix: clear existing header values before setting custom headers --- common/httpx/httpx.go | 1 + 1 file changed, 1 insertion(+) diff --git a/common/httpx/httpx.go b/common/httpx/httpx.go index 7e597720e..512926742 100644 --- a/common/httpx/httpx.go +++ b/common/httpx/httpx.go @@ -436,6 +436,7 @@ func (h *HTTPX) NewRequestWithContext(ctx context.Context, method, targetURL str // SetCustomHeaders on the provided request func (h *HTTPX) SetCustomHeaders(r *retryablehttp.Request, headers map[string][]string) { for name, values := range headers { + r.Header.Del(name) for _, value := range values { switch strings.ToLower(name) { case "host":