From f21a82aac3fb3f088c3d30bf0b11952a7396ed3c Mon Sep 17 00:00:00 2001 From: Dwi Siswanto <25837540+dwisiswant0@users.noreply.github.com> Date: Thu, 19 Dec 2024 22:01:41 +0700 Subject: [PATCH] fix(httpclientpool): rebuild malformed Location URL (#5902) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Dwi Siswanto Co-authored-by: Doğan Can Bakır --- .../http/httpclientpool/clientpool.go | 28 ++++++++++++++++++- pkg/protocols/http/httpclientpool/errors.go | 7 +++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 pkg/protocols/http/httpclientpool/errors.go diff --git a/pkg/protocols/http/httpclientpool/clientpool.go b/pkg/protocols/http/httpclientpool/clientpool.go index 042ee7ac0e..f3e3a60851 100644 --- a/pkg/protocols/http/httpclientpool/clientpool.go +++ b/pkg/protocols/http/httpclientpool/clientpool.go @@ -3,6 +3,7 @@ package httpclientpool import ( "context" "crypto/tls" + "fmt" "net" "net/http" "net/http/cookiejar" @@ -25,6 +26,7 @@ import ( "github.com/projectdiscovery/rawhttp" "github.com/projectdiscovery/retryablehttp-go" mapsutil "github.com/projectdiscovery/utils/maps" + urlutil "github.com/projectdiscovery/utils/url" ) var ( @@ -377,7 +379,7 @@ func makeCheckRedirectFunc(redirectType RedirectFlow, maxRedirects int) checkRed } } -func checkMaxRedirects(_ *http.Request, via []*http.Request, maxRedirects int) error { +func checkMaxRedirects(req *http.Request, via []*http.Request, maxRedirects int) error { if maxRedirects == 0 { if len(via) > defaultMaxRedirects { return http.ErrUseLastResponse @@ -388,5 +390,29 @@ func checkMaxRedirects(_ *http.Request, via []*http.Request, maxRedirects int) e if len(via) > maxRedirects { return http.ErrUseLastResponse } + + // NOTE(dwisiswant0): rebuild request URL. See #5900. + if u := req.URL.String(); !isURLEncoded(u) { + parsed, err := urlutil.Parse(u) + if err != nil { + return fmt.Errorf("%w: %w", ErrRebuildURL, err) + } + + req.URL = parsed.URL + } + return nil } + +// isURLEncoded is an helper function to check if the URL is already encoded +// +// NOTE(dwisiswant0): shall we move this under `projectdiscovery/utils/urlutil`? +func isURLEncoded(s string) bool { + decoded, err := url.QueryUnescape(s) + if err != nil { + // If decoding fails, it may indicate a malformed URL/invalid encoding. + return false + } + + return decoded != s +} diff --git a/pkg/protocols/http/httpclientpool/errors.go b/pkg/protocols/http/httpclientpool/errors.go new file mode 100644 index 0000000000..e5c915f93e --- /dev/null +++ b/pkg/protocols/http/httpclientpool/errors.go @@ -0,0 +1,7 @@ +package httpclientpool + +import "errors" + +var ( + ErrRebuildURL = errors.New("could not rebuild request URL") +)