Skip to content

Commit edc1828

Browse files
committed
add test
1 parent e61f35b commit edc1828

File tree

1 file changed

+265
-0
lines changed

1 file changed

+265
-0
lines changed

pkg/appsec/ja4h/ja4h_test.go

+265
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
package ja4h
2+
3+
import (
4+
"net/http"
5+
"slices"
6+
"strings"
7+
"testing"
8+
)
9+
10+
func TestJA4H_A(t *testing.T) {
11+
tests := []struct {
12+
name string
13+
request func() *http.Request
14+
expectedResult string
15+
}{
16+
{
17+
name: "basic GET request - HTTP1.1 - no accept-language header",
18+
request: func() *http.Request {
19+
req, _ := http.NewRequest("GET", "http://example.com", nil)
20+
return req
21+
},
22+
expectedResult: "ge11nn000000",
23+
},
24+
{
25+
name: "basic GET request - HTTP1.1 - with accept-language header",
26+
request: func() *http.Request {
27+
req, _ := http.NewRequest("GET", "http://example.com", nil)
28+
req.Header.Set("Accept-Language", "en-US")
29+
return req
30+
},
31+
expectedResult: "ge11nn01enus",
32+
},
33+
{
34+
name: "basic POST request - HTTP1.1 - no accept-language header - cookies - referer",
35+
request: func() *http.Request {
36+
req, _ := http.NewRequest("POST", "http://example.com", nil)
37+
req.AddCookie(&http.Cookie{Name: "foo", Value: "bar"})
38+
req.Header.Set("Referer", "http://example.com")
39+
return req
40+
},
41+
expectedResult: "po11cr000000",
42+
},
43+
}
44+
45+
for _, tt := range tests {
46+
t.Run(tt.name, func(t *testing.T) {
47+
result := jA4H_a(tt.request())
48+
if result != tt.expectedResult {
49+
t.Errorf("expected %s, got %s", tt.expectedResult, result)
50+
}
51+
})
52+
}
53+
}
54+
55+
func TestJA4H_B(t *testing.T) {
56+
// This test is only for non-regression
57+
// Because go does not keep headers order, we just want to make sure our code always process the headers in the same order
58+
tests := []struct {
59+
name string
60+
request func() *http.Request
61+
expectedResult string
62+
}{
63+
{
64+
name: "no headers",
65+
request: func() *http.Request {
66+
req, _ := http.NewRequest("GET", "http://example.com", nil)
67+
return req
68+
},
69+
expectedResult: "e3b0c44298fc",
70+
},
71+
{
72+
name: "header with arbitrary content",
73+
request: func() *http.Request {
74+
req, _ := http.NewRequest("GET", "http://example.com", nil)
75+
req.Header.Set("X-Custom-Header", "some value")
76+
return req
77+
},
78+
expectedResult: "0a15aba5bbd6",
79+
},
80+
{
81+
name: "header with multiple headers",
82+
request: func() *http.Request {
83+
req, _ := http.NewRequest("GET", "http://example.com", nil)
84+
req.Header.Set("X-Custom-Header", "some value")
85+
req.Header.Set("Authorization", "Bearer token")
86+
return req
87+
},
88+
expectedResult: "bbfc6cf16ecb",
89+
},
90+
{
91+
name: "curl-like request",
92+
request: func() *http.Request {
93+
req, _ := http.NewRequest("GET", "http://localhost", nil)
94+
req.Header.Set("Host", "localhost")
95+
req.Header.Set("User-Agent", "curl/8.12.1")
96+
req.Header.Set("Accept", "*/*")
97+
return req
98+
},
99+
expectedResult: "4722709a6f34",
100+
},
101+
}
102+
103+
for _, tt := range tests {
104+
t.Run(tt.name, func(t *testing.T) {
105+
result := jA4H_b(tt.request())
106+
if result != tt.expectedResult {
107+
t.Errorf("expected %s, got %s", tt.expectedResult, result)
108+
}
109+
})
110+
}
111+
}
112+
113+
func TestJA4H_C(t *testing.T) {
114+
tests := []struct {
115+
name string
116+
cookies func() []*http.Cookie
117+
expectedResult string
118+
}{
119+
{
120+
name: "no cookies",
121+
cookies: func() []*http.Cookie {
122+
req, _ := http.NewRequest("GET", "http://example.com", nil)
123+
return req.Cookies()
124+
},
125+
expectedResult: "000000000000",
126+
},
127+
{
128+
name: "one cookie",
129+
cookies: func() []*http.Cookie {
130+
req, _ := http.NewRequest("GET", "http://example.com", nil)
131+
req.AddCookie(&http.Cookie{Name: "foo", Value: "bar"})
132+
return req.Cookies()
133+
},
134+
expectedResult: "2c26b46b68ff",
135+
},
136+
{
137+
name: "duplicate cookies",
138+
cookies: func() []*http.Cookie {
139+
req, _ := http.NewRequest("GET", "http://example.com", nil)
140+
req.AddCookie(&http.Cookie{Name: "foo", Value: "bar"})
141+
req.AddCookie(&http.Cookie{Name: "foo", Value: "bar2"})
142+
return req.Cookies()
143+
},
144+
expectedResult: "8990ce24137b",
145+
},
146+
{
147+
name: "multiple cookies",
148+
cookies: func() []*http.Cookie {
149+
req, _ := http.NewRequest("GET", "http://example.com", nil)
150+
req.AddCookie(&http.Cookie{Name: "foo", Value: "bar"})
151+
req.AddCookie(&http.Cookie{Name: "bar", Value: "foo"})
152+
cookies := req.Cookies()
153+
slices.SortFunc(cookies, func(a, b *http.Cookie) int {
154+
return strings.Compare(a.Name, b.Name)
155+
})
156+
return cookies
157+
},
158+
expectedResult: "41557db67d60",
159+
},
160+
}
161+
162+
for _, tt := range tests {
163+
t.Run(tt.name, func(t *testing.T) {
164+
result := jA4H_c(tt.cookies())
165+
if result != tt.expectedResult {
166+
t.Errorf("expected %s, got %s", tt.expectedResult, result)
167+
}
168+
})
169+
}
170+
}
171+
172+
func TestJA4H_D(t *testing.T) {
173+
tests := []struct {
174+
name string
175+
cookies func() []*http.Cookie
176+
expectedResult string
177+
}{
178+
{
179+
name: "no cookies",
180+
cookies: func() []*http.Cookie {
181+
req, _ := http.NewRequest("GET", "http://example.com", nil)
182+
return req.Cookies()
183+
},
184+
expectedResult: "000000000000",
185+
},
186+
{
187+
name: "one cookie",
188+
cookies: func() []*http.Cookie {
189+
req, _ := http.NewRequest("GET", "http://example.com", nil)
190+
req.AddCookie(&http.Cookie{Name: "foo", Value: "bar"})
191+
return req.Cookies()
192+
},
193+
expectedResult: "3ba8907e7a25",
194+
},
195+
{
196+
name: "duplicate cookies",
197+
cookies: func() []*http.Cookie {
198+
req, _ := http.NewRequest("GET", "http://example.com", nil)
199+
req.AddCookie(&http.Cookie{Name: "foo", Value: "bar"})
200+
req.AddCookie(&http.Cookie{Name: "foo", Value: "bar2"})
201+
return req.Cookies()
202+
},
203+
expectedResult: "975821a3a881",
204+
},
205+
{
206+
name: "multiple cookies",
207+
cookies: func() []*http.Cookie {
208+
req, _ := http.NewRequest("GET", "http://example.com", nil)
209+
req.AddCookie(&http.Cookie{Name: "foo", Value: "bar"})
210+
req.AddCookie(&http.Cookie{Name: "bar", Value: "foo"})
211+
cookies := req.Cookies()
212+
slices.SortFunc(cookies, func(a, b *http.Cookie) int {
213+
return strings.Compare(a.Name, b.Name)
214+
})
215+
return cookies
216+
},
217+
expectedResult: "70f8bee1efb8",
218+
},
219+
}
220+
221+
for _, tt := range tests {
222+
t.Run(tt.name, func(t *testing.T) {
223+
result := jA4H_d(tt.cookies())
224+
if result != tt.expectedResult {
225+
t.Errorf("expected %s, got %s", tt.expectedResult, result)
226+
}
227+
})
228+
}
229+
}
230+
231+
func TestJA4H(t *testing.T) {
232+
tests := []struct {
233+
name string
234+
req func() *http.Request
235+
expectedHash string
236+
}{
237+
{
238+
name: "Basic GET - No cookies",
239+
req: func() *http.Request {
240+
req, _ := http.NewRequest("GET", "http://example.com", nil)
241+
return req
242+
},
243+
expectedHash: "ge11nn000000_e3b0c44298fc_000000000000_000000000000",
244+
},
245+
{
246+
name: "Basic GET - With cookies",
247+
req: func() *http.Request {
248+
req, _ := http.NewRequest("GET", "http://example.com", nil)
249+
req.AddCookie(&http.Cookie{Name: "session", Value: "12345"})
250+
return req
251+
},
252+
expectedHash: "ge11cn000000_e3b0c44298fc_3f3af1ecebbd_86a3f0069fcd",
253+
},
254+
}
255+
256+
for _, test := range tests {
257+
t.Run(test.name, func(t *testing.T) {
258+
hash := JA4H(test.req())
259+
if hash != test.expectedHash {
260+
t.Errorf("expected %s, got %s", test.expectedHash, hash)
261+
}
262+
})
263+
}
264+
265+
}

0 commit comments

Comments
 (0)