Skip to content

Commit 8981417

Browse files
Seanstoppablephillip-stephenszakird
authored
Fixes a few issues with redirects (#462)
* Fixes a few issues with redirects First, this prevents a DNS lookup from happening when we encounter a redirect, *even if we don't intend to follow it*. This likely addresses some part of #452 Second, if we aren't following redirects, don't have the scan fail in an 'application-error'. We are succeeding in what we intended to do, which is to scan without following redirects * Make sure to check redirects before we loop through and parse the host Properly handle no redirects wanted to return success * Handle 0 indexing * Pull out the original checkRedirectCode so we deal with consistently * lint * add redirect fix to ipp module --------- Co-authored-by: Phillip Stephens <phillip@cs.stanford.edu> Co-authored-by: Zakir Durumeric <zakird@gmail.com>
1 parent 6d26c72 commit 8981417

File tree

3 files changed

+40
-28
lines changed

3 files changed

+40
-28
lines changed

lib/http/client.go

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -534,8 +534,9 @@ func (c *Client) Do(req *Request) (resp *Response, err error) {
534534
for {
535535
// For all but the first request, create the next
536536
// request hop and replace req.
537+
loc := req.URL.String()
537538
if len(reqs) > 0 {
538-
loc := resp.Header.Get("Location")
539+
loc = resp.Header.Get("Location")
539540
if loc == "" {
540541
return nil, uerr(fmt.Errorf("%d response missing Location header", resp.StatusCode))
541542
}
@@ -571,14 +572,6 @@ func (c *Client) Do(req *Request) (resp *Response, err error) {
571572
if ref := refererForURL(reqs[len(reqs)-1].URL, req.URL); ref != "" {
572573
req.Header.Set("Referer", ref)
573574
}
574-
err = c.checkRedirect(req, resp, reqs)
575-
576-
// Sentinel error to let users select the
577-
// previous response, without closing its
578-
// body. See Issue 10069.
579-
if err == ErrUseLastResponse {
580-
return resp, nil
581-
}
582575

583576
// Close the previous response's body. But
584577
// read at least some of the body so if it's
@@ -590,16 +583,6 @@ func (c *Client) Do(req *Request) (resp *Response, err error) {
590583
io.CopyN(ioutil.Discard, resp.Body, maxBodySlurpSize)
591584
}
592585
resp.Body.Close()
593-
594-
if err != nil {
595-
// Special case for Go 1 compatibility: return both the response
596-
// and an error if the CheckRedirect function failed.
597-
// See https://golang.org/issue/3795
598-
// The resp.Body has already been closed.
599-
ue := uerr(err)
600-
ue.(*url.Error).URL = loc
601-
return resp, ue
602-
}
603586
}
604587

605588
reqs = append(reqs, req)
@@ -614,6 +597,22 @@ func (c *Client) Do(req *Request) (resp *Response, err error) {
614597
}
615598
return nil, uerr(err)
616599
}
600+
err = c.checkRedirect(req, resp, reqs)
601+
602+
// Sentinel error to let users select the
603+
// previous response, without closing its
604+
// body. See Issue 10069.
605+
if err == ErrUseLastResponse {
606+
return resp, nil
607+
}
608+
if err != nil {
609+
// Special case for Go 1 compatibility: return both the response
610+
// and an error if the CheckRedirect function failed.
611+
// See https://golang.org/issue/3795
612+
ue := uerr(err)
613+
ue.(*url.Error).URL = loc
614+
return resp, err
615+
}
617616

618617
var shouldRedirect bool
619618
redirectMethod, shouldRedirect, includeBody = redirectBehavior(req.Method, resp, reqs[0])

modules/http/scanner.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ var (
3737
// ErrTooManyRedirects is returned when the number of HTTP redirects exceeds
3838
// MaxRedirects.
3939
ErrTooManyRedirects = errors.New("too many redirects")
40+
ErrDoNotRedirect = errors.New("no redirects configured")
4041
)
4142

4243
// Flags holds the command-line configuration for the HTTP scan module.
@@ -326,6 +327,13 @@ func redirectsToLocalhost(host string) bool {
326327
// the redirectToLocalhost and MaxRedirects config
327328
func (scan *scan) getCheckRedirect() func(*http.Request, *http.Response, []*http.Request) error {
328329
return func(req *http.Request, res *http.Response, via []*http.Request) error {
330+
if scan.scanner.config.MaxRedirects == 0 {
331+
return ErrDoNotRedirect
332+
}
333+
//len-1 because otherwise we'll return a failure on 1 redirect when we specify only 1 redirect. I.e. we are 0
334+
if len(via)-1 > scan.scanner.config.MaxRedirects {
335+
return ErrTooManyRedirects
336+
}
329337
if !scan.scanner.config.FollowLocalhostRedirects && redirectsToLocalhost(req.URL.Hostname()) {
330338
return ErrRedirLocalhost
331339
}
@@ -353,10 +361,6 @@ func (scan *scan) getCheckRedirect() func(*http.Request, *http.Response, []*http
353361
}
354362
}
355363

356-
if len(via) > scan.scanner.config.MaxRedirects {
357-
return ErrTooManyRedirects
358-
}
359-
360364
return nil
361365
}
362366
}
@@ -496,6 +500,8 @@ func (scan *scan) Grab() *zgrab2.ScanError {
496500
}
497501
if err != nil {
498502
switch err {
503+
case ErrDoNotRedirect:
504+
break
499505
case ErrRedirLocalhost:
500506
break
501507
case ErrTooManyRedirects:

modules/ipp/scanner.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"encoding/binary"
1010
"errors"
1111
"fmt"
12-
1312
"io"
1413
"mime"
1514
"net"
@@ -39,6 +38,9 @@ var (
3938
// MaxRedirects.
4039
ErrTooManyRedirects = errors.New("too many redirects")
4140

41+
// ErrDoNotRedirect is returned when the scanner is configured not to follow redirects
42+
ErrDoNotRedirect = errors.New("no redirects configured")
43+
4244
// TODO: Explain this error
4345
ErrVersionNotSupported = errors.New("IPP version not supported")
4446

@@ -510,6 +512,8 @@ func sendIPPRequest(scan *scan, body *bytes.Buffer) (*http.Response, *zgrab2.Sca
510512
break
511513
case ErrTooManyRedirects:
512514
return resp, zgrab2.NewScanError(zgrab2.SCAN_APPLICATION_ERROR, err)
515+
case ErrDoNotRedirect:
516+
break
513517
default:
514518
return resp, zgrab2.DetectScanError(err)
515519
}
@@ -648,6 +652,13 @@ func redirectsToLocalhost(host string) bool {
648652
// Taken from zgrab/zlib/grabber.go -- get a CheckRedirect callback that uses redirectToLocalhost and MaxRedirects config
649653
func (scan *scan) getCheckRedirect(scanner *Scanner) func(*http.Request, *http.Response, []*http.Request) error {
650654
return func(req *http.Request, res *http.Response, via []*http.Request) error {
655+
if scanner.config.MaxRedirects == 0 {
656+
return ErrDoNotRedirect
657+
}
658+
//len-1 because otherwise we'll return a failure on 1 redirect when we specify only 1 redirect. I.e. we are 0
659+
if len(via)-1 > scanner.config.MaxRedirects {
660+
return ErrTooManyRedirects
661+
}
651662
if !scanner.config.FollowLocalhostRedirects && redirectsToLocalhost(req.URL.Hostname()) {
652663
return ErrRedirLocalhost
653664
}
@@ -656,10 +667,6 @@ func (scan *scan) getCheckRedirect(scanner *Scanner) func(*http.Request, *http.R
656667
return zgrab2.NewScanError(zgrab2.SCAN_UNKNOWN_ERROR, fmt.Errorf("could not store body: %v", err))
657668
}
658669

659-
if len(via) > scanner.config.MaxRedirects {
660-
return ErrTooManyRedirects
661-
}
662-
663670
return nil
664671
}
665672
}

0 commit comments

Comments
 (0)