Skip to content

Commit 5c68177

Browse files
committed
fix(js): bound next.js manifest fetch with the scan timeout
GetPagesRouterScripts fetched _buildManifest.js with httpx.Client(0), which sets no client timeout, so a slow or hostile manifest host could hang the whole scan on the response read. thread the caller's scan timeout through instead, matching every other fetch in the package.
1 parent 7ea1cd2 commit 5c68177

3 files changed

Lines changed: 35 additions & 6 deletions

File tree

internal/scan/js/frameworks/next.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"net/http"
3030
"regexp"
3131
"strings"
32+
"time"
3233

3334
urlutil "github.com/projectdiscovery/utils/url"
3435
"github.com/vmfunc/sif/internal/httpx"
@@ -41,7 +42,7 @@ var nextPagesRegex = regexp.MustCompile(`\[("([^"]+\.js)"(,?))`)
4142
// cannot exhaust memory.
4243
const maxManifestSize = 5 * 1024 * 1024
4344

44-
func GetPagesRouterScripts(scriptUrl string) ([]string, error) {
45+
func GetPagesRouterScripts(scriptUrl string, timeout time.Duration) ([]string, error) {
4546
baseUrl, err := urlutil.Parse(scriptUrl)
4647
if err != nil {
4748
return nil, err
@@ -53,9 +54,9 @@ func GetPagesRouterScripts(scriptUrl string) ([]string, error) {
5354
return nil, err
5455
}
5556

56-
// no timeout in scope here; 0 matches the previous DefaultClient behavior
57-
// while still routing through the shared transport (proxy/headers/rate-limit).
58-
resp, err := httpx.Client(0).Do(req)
57+
// use the caller's scan timeout so a slow or hostile manifest host cannot
58+
// hang the whole scan; a zero timeout would read with no deadline.
59+
resp, err := httpx.Client(timeout).Do(req)
5960
if err != nil {
6061
fmt.Println(err)
6162
return nil, err

internal/scan/js/frameworks/next_test.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"net/http/httptest"
1919
"strings"
2020
"testing"
21+
"time"
2122
)
2223

2324
func TestGetPagesRouterScriptsReadsPastLongLine(t *testing.T) {
@@ -31,7 +32,7 @@ func TestGetPagesRouterScriptsReadsPastLongLine(t *testing.T) {
3132
}))
3233
defer srv.Close()
3334

34-
scripts, err := GetPagesRouterScripts(srv.URL + "/_buildManifest.js")
35+
scripts, err := GetPagesRouterScripts(srv.URL+"/_buildManifest.js", 5*time.Second)
3536
if err != nil {
3637
t.Fatalf("GetPagesRouterScripts: %v", err)
3738
}
@@ -48,3 +49,30 @@ func TestGetPagesRouterScriptsReadsPastLongLine(t *testing.T) {
4849
t.Errorf("want both early.js and late.js, got %v", scripts)
4950
}
5051
}
52+
53+
func TestGetPagesRouterScriptsHonorsTimeout(t *testing.T) {
54+
// a slow manifest host must not hang the scan: the fetch has to give up
55+
// once the caller's timeout elapses instead of reading with no deadline.
56+
release := make(chan struct{})
57+
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
58+
<-release
59+
w.Write([]byte(`["late.js"]`))
60+
}))
61+
defer srv.Close()
62+
defer close(release)
63+
64+
done := make(chan error, 1)
65+
go func() {
66+
_, err := GetPagesRouterScripts(srv.URL+"/_buildManifest.js", 100*time.Millisecond)
67+
done <- err
68+
}()
69+
70+
select {
71+
case err := <-done:
72+
if err == nil {
73+
t.Fatal("expected a timeout error from the slow manifest host, got nil")
74+
}
75+
case <-time.After(5 * time.Second):
76+
t.Fatal("GetPagesRouterScripts did not honor the timeout and hung")
77+
}
78+
}

internal/scan/js/scan.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ func JavascriptScan(url string, timeout time.Duration, threads int, logdir strin
120120
for _, script := range scripts {
121121
if strings.Contains(script, "/_buildManifest.js") {
122122
log.Info("Detected Next.JS pages router! Getting all scripts from %s", script)
123-
nextScripts, err := frameworks.GetPagesRouterScripts(script)
123+
nextScripts, err := frameworks.GetPagesRouterScripts(script, timeout)
124124
if err != nil {
125125
spin.Stop()
126126
return nil, err

0 commit comments

Comments
 (0)