Skip to content

Commit 6a02559

Browse files
authored
Merge branch 'dev' into dependabot/go_modules/dev/github.com/projectdiscovery/networkpolicy-0.1.45
2 parents cdb352b + d58fdd9 commit 6a02559

4 files changed

Lines changed: 119 additions & 7 deletions

File tree

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ require (
118118
github.com/pierrec/lz4/v4 v4.1.23 // indirect
119119
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
120120
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
121-
github.com/projectdiscovery/blackrock v0.0.1 // indirect
121+
github.com/projectdiscovery/blackrock v0.0.2 // indirect
122122
github.com/projectdiscovery/fastdialer v0.5.13 // indirect
123123
github.com/projectdiscovery/hmap v0.0.101 // indirect
124124
github.com/projectdiscovery/machineid v0.0.0-20250715113114-c77eb3567582 // indirect

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -339,8 +339,8 @@ github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt
339339
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
340340
github.com/projectdiscovery/asnmap v1.1.1 h1:ImJiKIaACOT7HPx4Pabb5dksolzaFYsD1kID2iwsDqI=
341341
github.com/projectdiscovery/asnmap v1.1.1/go.mod h1:QT7jt9nQanj+Ucjr9BqGr1Q2veCCKSAVyUzLXfEcQ60=
342-
github.com/projectdiscovery/blackrock v0.0.1 h1:lHQqhaaEFjgf5WkuItbpeCZv2DUIE45k0VbGJyft6LQ=
343-
github.com/projectdiscovery/blackrock v0.0.1/go.mod h1:ANUtjDfaVrqB453bzToU+YB4cUbvBRpLvEwoWIwlTss=
342+
github.com/projectdiscovery/blackrock v0.0.2 h1:mxXdu0uM8P2L2Qi210COlU8QiICPFW/Rxk5QUhlPO2k=
343+
github.com/projectdiscovery/blackrock v0.0.2/go.mod h1:ANUtjDfaVrqB453bzToU+YB4cUbvBRpLvEwoWIwlTss=
344344
github.com/projectdiscovery/fastdialer v0.5.13 h1:Tocdk3yy7WKLmRV1YmWctPI2yU1QfJIlUbMarIzdgGg=
345345
github.com/projectdiscovery/fastdialer v0.5.13/go.mod h1:iSf7DMOttk4LH/YSNAaztliqVCo1cVlmyudR+YXJWW8=
346346
github.com/projectdiscovery/goflags v0.1.74 h1:n85uTRj5qMosm0PFBfsvOL24I7TdWRcWq/1GynhXS7c=

pkg/client/client.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -187,16 +187,18 @@ func New(options *Options) (*Client, error) {
187187
return nil, errkit.Wrap(err, "failed to decode public key")
188188
}
189189
client.pubKey = pubKey
190-
if serverURL, err := url.Parse(options.SessionInfo.ServerURL); err == nil {
190+
registrationServerURL := options.SessionInfo.ServerURL
191+
if serverURL, err := parseServerURL(options.SessionInfo.ServerURL); err == nil {
191192
client.serverURL = serverURL
193+
registrationServerURL = serverURL.String()
192194
}
193195
// attempts to re-register - server will reject is already existing
194196
registrationRequest, err := encodeRegistrationRequest(options.SessionInfo.PublicKey, options.SessionInfo.SecretKey, options.SessionInfo.CorrelationID)
195197
if err != nil {
196198
return nil, err
197199
}
198200
// silently fails to re-register if the session is still alive
199-
_ = client.performRegistration(options.SessionInfo.ServerURL, registrationRequest)
201+
_ = client.performRegistration(registrationServerURL, registrationRequest)
200202
} else {
201203
payload, err := client.initializeRSAKeys()
202204
if err != nil {
@@ -327,11 +329,11 @@ func (c *Client) parseServerURLs(serverURL string, payload []byte) error {
327329
}
328330

329331
values := strings.Split(serverURL, ",")
330-
registerFunc := func(idx int, value string) error {
332+
registerFunc := func(_ int, value string) error {
331333
if !stringsutil.HasPrefixAny(value, "http://", "https://") {
332334
value = fmt.Sprintf("https://%s", value)
333335
}
334-
parsed, err := url.Parse(value)
336+
parsed, err := parseServerURL(value)
335337
if err != nil {
336338
return errkit.Wrap(err, "could not parse server URL")
337339
}
@@ -369,6 +371,20 @@ func (c *Client) parseServerURLs(serverURL string, payload []byte) error {
369371
return nil
370372
}
371373

374+
func parseServerURL(value string) (*url.URL, error) {
375+
parsed, err := url.Parse(value)
376+
if err != nil {
377+
return nil, err
378+
}
379+
380+
for strings.HasSuffix(parsed.EscapedPath(), "/") {
381+
parsed.Path = strings.TrimSuffix(parsed.Path, "/")
382+
parsed.RawPath = strings.TrimSuffix(parsed.RawPath, "/")
383+
}
384+
385+
return parsed, nil
386+
}
387+
372388
// InteractionCallback is a callback function for a reported interaction
373389
type InteractionCallback func(*server.Interaction)
374390

pkg/client/client_test.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package client
2+
3+
import (
4+
"crypto/x509"
5+
"encoding/json"
6+
"fmt"
7+
"net/http"
8+
"net/http/httptest"
9+
"sync/atomic"
10+
"testing"
11+
12+
"github.com/projectdiscovery/interactsh/pkg/options"
13+
"github.com/projectdiscovery/interactsh/pkg/server"
14+
"github.com/projectdiscovery/retryablehttp-go"
15+
"github.com/stretchr/testify/require"
16+
)
17+
18+
func TestNewAcceptsServerURLWithOptionalTrailingSlash(t *testing.T) {
19+
var registrationCount atomic.Int64
20+
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
21+
switch r.URL.EscapedPath() {
22+
case "//register":
23+
// Some deployments canonicalize this path with a redirect that drops the POST body.
24+
http.Redirect(w, r, "/register", http.StatusMovedPermanently)
25+
case "/register", "/base%2F/register":
26+
request := &server.RegisterRequest{}
27+
if err := json.NewDecoder(r.Body).Decode(request); err != nil {
28+
http.Error(w, fmt.Sprintf(`{"error":"could not decode json body: %s"}`, err), http.StatusBadRequest)
29+
return
30+
}
31+
registrationCount.Add(1)
32+
_, _ = w.Write([]byte(`{"message":"registration successful"}`))
33+
case "/poll":
34+
_, _ = w.Write([]byte(`{"data":[],"extra":[],"aes_key":""}`))
35+
case "/deregister", "/base%2F/deregister":
36+
w.WriteHeader(http.StatusOK)
37+
default:
38+
http.NotFound(w, r)
39+
}
40+
})
41+
42+
testServer := httptest.NewTLSServer(handler)
43+
t.Cleanup(testServer.Close)
44+
45+
httpClient := retryablehttp.NewClient(retryablehttp.DefaultOptionsSpraying)
46+
httpClient.HTTPClient = testServer.Client()
47+
48+
for _, test := range []struct {
49+
name string
50+
suffix string
51+
}{
52+
{name: "without trailing slash"},
53+
{name: "with trailing slash", suffix: "/"},
54+
{name: "with percent-encoded slash", suffix: "/base%2F/"},
55+
} {
56+
t.Run(test.name, func(t *testing.T) {
57+
interactshClient, err := New(&Options{
58+
ServerURL: testServer.URL + test.suffix,
59+
DisableHTTPFallback: true,
60+
HTTPClient: httpClient,
61+
})
62+
require.NoError(t, err)
63+
require.NoError(t, interactshClient.Close())
64+
})
65+
}
66+
67+
t.Run("resumed session with trailing slash", func(t *testing.T) {
68+
originalClient, err := New(&Options{
69+
ServerURL: testServer.URL,
70+
DisableHTTPFallback: true,
71+
HTTPClient: httpClient,
72+
})
73+
require.NoError(t, err)
74+
75+
publicKey, err := encodePublicKey(originalClient.pubKey)
76+
require.NoError(t, err)
77+
sessionInfo := &options.SessionInfo{
78+
ServerURL: testServer.URL + "/",
79+
PrivateKey: string(x509.MarshalPKCS1PrivateKey(originalClient.privKey)),
80+
CorrelationID: originalClient.correlationID,
81+
SecretKey: originalClient.secretKey,
82+
PublicKey: publicKey,
83+
}
84+
require.NoError(t, originalClient.Close())
85+
86+
registrationsBeforeResume := registrationCount.Load()
87+
resumedClient, err := New(&Options{
88+
SessionInfo: sessionInfo,
89+
HTTPClient: httpClient,
90+
})
91+
require.NoError(t, err)
92+
require.Equal(t, registrationsBeforeResume+1, registrationCount.Load())
93+
require.NoError(t, resumedClient.getInteractions(func(*server.Interaction) {}))
94+
require.NoError(t, resumedClient.Close())
95+
})
96+
}

0 commit comments

Comments
 (0)