Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/fuzz/dataformat/multipart_test.go
2070 views
1
package dataformat
2
3
import (
4
"testing"
5
6
mapsutil "github.com/projectdiscovery/utils/maps"
7
"github.com/stretchr/testify/assert"
8
"github.com/stretchr/testify/require"
9
)
10
11
func TestMultiPartFormEncode(t *testing.T) {
12
tests := []struct {
13
name string
14
fields map[string]any
15
wantErr bool
16
expected map[string]any
17
}{
18
{
19
name: "duplicate fields ([]string) - checkbox scenario",
20
fields: map[string]any{
21
"interests": []string{"sports", "music", "reading"},
22
"colors": []string{"red", "blue"},
23
},
24
expected: map[string]any{
25
"interests": []string{"sports", "music", "reading"},
26
"colors": []string{"red", "blue"},
27
},
28
},
29
{
30
name: "single string fields - backward compatibility",
31
fields: map[string]any{
32
"username": "john",
33
"email": "[email protected]",
34
},
35
expected: map[string]any{
36
"username": "john",
37
"email": "[email protected]",
38
},
39
},
40
{
41
name: "mixed types",
42
fields: map[string]any{
43
"string": "text",
44
"array": []string{"item1", "item2"},
45
"number": 42, // tests fmt.Sprint fallback
46
"float": 3.14, // tests float conversion
47
"boolean": true, // tests boolean conversion
48
"zero": 0, // tests zero value
49
"emptyStr": "", // tests empty string
50
"negative": -123, // tests negative number
51
"nil": nil, // tests nil value
52
"mixedArray": []any{"str", 123, false, nil}, // tests mixed type array
53
},
54
expected: map[string]any{
55
"string": "text",
56
"array": []string{"item1", "item2"},
57
"number": "42", // numbers are converted to strings in multipart
58
"float": "3.14", // floats are converted to strings
59
"boolean": "true", // booleans are converted to strings
60
"zero": "0", // zero value converted to string
61
"emptyStr": "", // empty string remains empty
62
"negative": "-123", // negative numbers converted to strings
63
"nil": "", // nil values converted to "" string
64
"mixedArray": []string{"str", "123", "false", ""}, // mixed array converted to string array
65
},
66
},
67
{
68
name: "empty array - should not appear in output",
69
fields: map[string]any{
70
"emptyArray": []string{},
71
"normalField": "value",
72
},
73
expected: map[string]any{
74
"normalField": "value",
75
// emptyArray should not appear in decoded output
76
},
77
},
78
}
79
80
for _, tt := range tests {
81
t.Run(tt.name, func(t *testing.T) {
82
defer func() {
83
if r := recover(); r != nil {
84
t.Errorf("Test panicked: %v", r)
85
}
86
}()
87
88
form := NewMultiPartForm()
89
form.boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW"
90
91
kv := mapsutil.NewOrderedMap[string, any]()
92
for k, v := range tt.fields {
93
kv.Set(k, v)
94
}
95
96
encoded, err := form.Encode(KVOrderedMap(&kv))
97
98
if tt.wantErr {
99
require.Error(t, err)
100
return
101
}
102
103
require.NoError(t, err)
104
105
// Decode the encoded multipart data
106
decoded, err := form.Decode(encoded)
107
require.NoError(t, err)
108
109
// Compare decoded values with expected values
110
for expectedKey, expectedValue := range tt.expected {
111
actualValue := decoded.Get(expectedKey)
112
switch expected := expectedValue.(type) {
113
case []string:
114
actual, ok := actualValue.([]string)
115
require.True(t, ok, "Expected []string for key %s, got %T", expectedKey, actualValue)
116
assert.ElementsMatch(t, expected, actual, "Values mismatch for key %s", expectedKey)
117
case []any:
118
actual, ok := actualValue.([]any)
119
require.True(t, ok, "Expected []any for key %s, got %T", expectedKey, actualValue)
120
assert.ElementsMatch(t, expected, actual, "Values mismatch for key %s", expectedKey)
121
case string:
122
actual, ok := actualValue.(string)
123
require.True(t, ok, "Expected string for key %s, got %T", expectedKey, actualValue)
124
assert.Equal(t, expected, actual, "Values mismatch for key %s", expectedKey)
125
default:
126
assert.Equal(t, expected, actualValue, "Values mismatch for key %s", expectedKey)
127
}
128
}
129
130
// Ensure no unexpected keys are present in decoded output
131
decoded.Iterate(func(key string, value any) bool {
132
_, exists := tt.expected[key]
133
assert.True(t, exists, "Unexpected key %s found in decoded output", key)
134
return true
135
})
136
137
t.Logf("Encoded output:\n%s", encoded)
138
})
139
}
140
}
141
142
func TestMultiPartFormRoundTrip(t *testing.T) {
143
defer func() {
144
if r := recover(); r != nil {
145
t.Errorf("Test panicked: %v", r)
146
}
147
}()
148
149
form := NewMultiPartForm()
150
form.boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW"
151
152
original := mapsutil.NewOrderedMap[string, any]()
153
original.Set("username", "john")
154
original.Set("interests", []string{"sports", "music", "reading"})
155
156
encoded, err := form.Encode(KVOrderedMap(&original))
157
require.NoError(t, err)
158
159
decoded, err := form.Decode(encoded)
160
require.NoError(t, err)
161
162
assert.Equal(t, "john", decoded.Get("username"))
163
assert.ElementsMatch(t, []string{"sports", "music", "reading"}, decoded.Get("interests"))
164
165
t.Logf("Encoded output:\n%s", encoded)
166
}
167
168
func TestMultiPartFormFileUpload(t *testing.T) {
169
defer func() {
170
if r := recover(); r != nil {
171
t.Errorf("Test panicked: %v", r)
172
}
173
}()
174
175
// Test decoding of a manually crafted multipart form with files
176
form := NewMultiPartForm()
177
form.boundary = "----WebKitFormBoundaryFileUploadTest"
178
179
// Manually craft a multipart form with file uploads
180
multipartData := `------WebKitFormBoundaryFileUploadTest
181
Content-Disposition: form-data; name="name"
182
183
John Doe
184
------WebKitFormBoundaryFileUploadTest
185
Content-Disposition: form-data; name="email"
186
187
[email protected]
188
------WebKitFormBoundaryFileUploadTest
189
Content-Disposition: form-data; name="profile_picture"; filename="profile.jpg"
190
Content-Type: image/jpeg
191
192
fake_jpeg_binary_data_here
193
------WebKitFormBoundaryFileUploadTest
194
Content-Disposition: form-data; name="documents"; filename="resume.pdf"
195
Content-Type: application/pdf
196
197
fake_pdf_content_1
198
------WebKitFormBoundaryFileUploadTest
199
Content-Disposition: form-data; name="documents"; filename="cover_letter.pdf"
200
Content-Type: application/pdf
201
202
fake_pdf_content_2
203
------WebKitFormBoundaryFileUploadTest
204
Content-Disposition: form-data; name="skills"
205
206
Go
207
------WebKitFormBoundaryFileUploadTest
208
Content-Disposition: form-data; name="skills"
209
210
JavaScript
211
------WebKitFormBoundaryFileUploadTest
212
Content-Disposition: form-data; name="skills"
213
214
Python
215
------WebKitFormBoundaryFileUploadTest--
216
`
217
218
// Test decoding
219
decoded, err := form.Decode(multipartData)
220
require.NoError(t, err)
221
222
// Verify regular fields
223
assert.Equal(t, "John Doe", decoded.Get("name"))
224
assert.Equal(t, "[email protected]", decoded.Get("email"))
225
assert.Equal(t, []string{"Go", "JavaScript", "Python"}, decoded.Get("skills"))
226
227
// Verify file fields
228
profilePicture := decoded.Get("profile_picture")
229
require.NotNil(t, profilePicture)
230
profileArray, ok := profilePicture.([]interface{})
231
require.True(t, ok, "Expected []interface{} for profile_picture")
232
require.Len(t, profileArray, 1)
233
assert.Equal(t, "fake_jpeg_binary_data_here", profileArray[0])
234
235
documents := decoded.Get("documents")
236
require.NotNil(t, documents)
237
documentsArray, ok := documents.([]interface{})
238
require.True(t, ok, "Expected []interface{} for documents")
239
require.Len(t, documentsArray, 2)
240
assert.Contains(t, documentsArray, "fake_pdf_content_1")
241
assert.Contains(t, documentsArray, "fake_pdf_content_2")
242
}
243
244