mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2025-12-20 17:25:25 +00:00
243 lines
7.6 KiB
Go
243 lines
7.6 KiB
Go
|
|
package dataformat
|
||
|
|
|
||
|
|
import (
|
||
|
|
"testing"
|
||
|
|
|
||
|
|
mapsutil "github.com/projectdiscovery/utils/maps"
|
||
|
|
"github.com/stretchr/testify/assert"
|
||
|
|
"github.com/stretchr/testify/require"
|
||
|
|
)
|
||
|
|
|
||
|
|
func TestMultiPartFormEncode(t *testing.T) {
|
||
|
|
tests := []struct {
|
||
|
|
name string
|
||
|
|
fields map[string]any
|
||
|
|
wantErr bool
|
||
|
|
expected map[string]any
|
||
|
|
}{
|
||
|
|
{
|
||
|
|
name: "duplicate fields ([]string) - checkbox scenario",
|
||
|
|
fields: map[string]any{
|
||
|
|
"interests": []string{"sports", "music", "reading"},
|
||
|
|
"colors": []string{"red", "blue"},
|
||
|
|
},
|
||
|
|
expected: map[string]any{
|
||
|
|
"interests": []string{"sports", "music", "reading"},
|
||
|
|
"colors": []string{"red", "blue"},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "single string fields - backward compatibility",
|
||
|
|
fields: map[string]any{
|
||
|
|
"username": "john",
|
||
|
|
"email": "john@example.com",
|
||
|
|
},
|
||
|
|
expected: map[string]any{
|
||
|
|
"username": "john",
|
||
|
|
"email": "john@example.com",
|
||
|
|
},
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "mixed types",
|
||
|
|
fields: map[string]any{
|
||
|
|
"string": "text",
|
||
|
|
"array": []string{"item1", "item2"},
|
||
|
|
"number": 42, // tests fmt.Sprint fallback
|
||
|
|
"float": 3.14, // tests float conversion
|
||
|
|
"boolean": true, // tests boolean conversion
|
||
|
|
"zero": 0, // tests zero value
|
||
|
|
"emptyStr": "", // tests empty string
|
||
|
|
"negative": -123, // tests negative number
|
||
|
|
"nil": nil, // tests nil value
|
||
|
|
"mixedArray": []any{"str", 123, false, nil}, // tests mixed type array
|
||
|
|
},
|
||
|
|
expected: map[string]any{
|
||
|
|
"string": "text",
|
||
|
|
"array": []string{"item1", "item2"},
|
||
|
|
"number": "42", // numbers are converted to strings in multipart
|
||
|
|
"float": "3.14", // floats are converted to strings
|
||
|
|
"boolean": "true", // booleans are converted to strings
|
||
|
|
"zero": "0", // zero value converted to string
|
||
|
|
"emptyStr": "", // empty string remains empty
|
||
|
|
"negative": "-123", // negative numbers converted to strings
|
||
|
|
"nil": "", // nil values converted to "" string
|
||
|
|
"mixedArray": []string{"str", "123", "false", ""}, // mixed array converted to string array
|
||
|
|
},
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "empty array - should not appear in output",
|
||
|
|
fields: map[string]any{
|
||
|
|
"emptyArray": []string{},
|
||
|
|
"normalField": "value",
|
||
|
|
},
|
||
|
|
expected: map[string]any{
|
||
|
|
"normalField": "value",
|
||
|
|
// emptyArray should not appear in decoded output
|
||
|
|
},
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
for _, tt := range tests {
|
||
|
|
t.Run(tt.name, func(t *testing.T) {
|
||
|
|
defer func() {
|
||
|
|
if r := recover(); r != nil {
|
||
|
|
t.Errorf("Test panicked: %v", r)
|
||
|
|
}
|
||
|
|
}()
|
||
|
|
|
||
|
|
form := NewMultiPartForm()
|
||
|
|
form.boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW"
|
||
|
|
|
||
|
|
kv := mapsutil.NewOrderedMap[string, any]()
|
||
|
|
for k, v := range tt.fields {
|
||
|
|
kv.Set(k, v)
|
||
|
|
}
|
||
|
|
|
||
|
|
encoded, err := form.Encode(KVOrderedMap(&kv))
|
||
|
|
|
||
|
|
if tt.wantErr {
|
||
|
|
require.Error(t, err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
require.NoError(t, err)
|
||
|
|
|
||
|
|
// Decode the encoded multipart data
|
||
|
|
decoded, err := form.Decode(encoded)
|
||
|
|
require.NoError(t, err)
|
||
|
|
|
||
|
|
// Compare decoded values with expected values
|
||
|
|
for expectedKey, expectedValue := range tt.expected {
|
||
|
|
actualValue := decoded.Get(expectedKey)
|
||
|
|
switch expected := expectedValue.(type) {
|
||
|
|
case []string:
|
||
|
|
actual, ok := actualValue.([]string)
|
||
|
|
require.True(t, ok, "Expected []string for key %s, got %T", expectedKey, actualValue)
|
||
|
|
assert.ElementsMatch(t, expected, actual, "Values mismatch for key %s", expectedKey)
|
||
|
|
case []any:
|
||
|
|
actual, ok := actualValue.([]any)
|
||
|
|
require.True(t, ok, "Expected []any for key %s, got %T", expectedKey, actualValue)
|
||
|
|
assert.ElementsMatch(t, expected, actual, "Values mismatch for key %s", expectedKey)
|
||
|
|
case string:
|
||
|
|
actual, ok := actualValue.(string)
|
||
|
|
require.True(t, ok, "Expected string for key %s, got %T", expectedKey, actualValue)
|
||
|
|
assert.Equal(t, expected, actual, "Values mismatch for key %s", expectedKey)
|
||
|
|
default:
|
||
|
|
assert.Equal(t, expected, actualValue, "Values mismatch for key %s", expectedKey)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Ensure no unexpected keys are present in decoded output
|
||
|
|
decoded.Iterate(func(key string, value any) bool {
|
||
|
|
_, exists := tt.expected[key]
|
||
|
|
assert.True(t, exists, "Unexpected key %s found in decoded output", key)
|
||
|
|
return true
|
||
|
|
})
|
||
|
|
|
||
|
|
t.Logf("Encoded output:\n%s", encoded)
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestMultiPartFormRoundTrip(t *testing.T) {
|
||
|
|
defer func() {
|
||
|
|
if r := recover(); r != nil {
|
||
|
|
t.Errorf("Test panicked: %v", r)
|
||
|
|
}
|
||
|
|
}()
|
||
|
|
|
||
|
|
form := NewMultiPartForm()
|
||
|
|
form.boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW"
|
||
|
|
|
||
|
|
original := mapsutil.NewOrderedMap[string, any]()
|
||
|
|
original.Set("username", "john")
|
||
|
|
original.Set("interests", []string{"sports", "music", "reading"})
|
||
|
|
|
||
|
|
encoded, err := form.Encode(KVOrderedMap(&original))
|
||
|
|
require.NoError(t, err)
|
||
|
|
|
||
|
|
decoded, err := form.Decode(encoded)
|
||
|
|
require.NoError(t, err)
|
||
|
|
|
||
|
|
assert.Equal(t, "john", decoded.Get("username"))
|
||
|
|
assert.ElementsMatch(t, []string{"sports", "music", "reading"}, decoded.Get("interests"))
|
||
|
|
|
||
|
|
t.Logf("Encoded output:\n%s", encoded)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestMultiPartFormFileUpload(t *testing.T) {
|
||
|
|
defer func() {
|
||
|
|
if r := recover(); r != nil {
|
||
|
|
t.Errorf("Test panicked: %v", r)
|
||
|
|
}
|
||
|
|
}()
|
||
|
|
|
||
|
|
// Test decoding of a manually crafted multipart form with files
|
||
|
|
form := NewMultiPartForm()
|
||
|
|
form.boundary = "----WebKitFormBoundaryFileUploadTest"
|
||
|
|
|
||
|
|
// Manually craft a multipart form with file uploads
|
||
|
|
multipartData := `------WebKitFormBoundaryFileUploadTest
|
||
|
|
Content-Disposition: form-data; name="name"
|
||
|
|
|
||
|
|
John Doe
|
||
|
|
------WebKitFormBoundaryFileUploadTest
|
||
|
|
Content-Disposition: form-data; name="email"
|
||
|
|
|
||
|
|
john@example.com
|
||
|
|
------WebKitFormBoundaryFileUploadTest
|
||
|
|
Content-Disposition: form-data; name="profile_picture"; filename="profile.jpg"
|
||
|
|
Content-Type: image/jpeg
|
||
|
|
|
||
|
|
fake_jpeg_binary_data_here
|
||
|
|
------WebKitFormBoundaryFileUploadTest
|
||
|
|
Content-Disposition: form-data; name="documents"; filename="resume.pdf"
|
||
|
|
Content-Type: application/pdf
|
||
|
|
|
||
|
|
fake_pdf_content_1
|
||
|
|
------WebKitFormBoundaryFileUploadTest
|
||
|
|
Content-Disposition: form-data; name="documents"; filename="cover_letter.pdf"
|
||
|
|
Content-Type: application/pdf
|
||
|
|
|
||
|
|
fake_pdf_content_2
|
||
|
|
------WebKitFormBoundaryFileUploadTest
|
||
|
|
Content-Disposition: form-data; name="skills"
|
||
|
|
|
||
|
|
Go
|
||
|
|
------WebKitFormBoundaryFileUploadTest
|
||
|
|
Content-Disposition: form-data; name="skills"
|
||
|
|
|
||
|
|
JavaScript
|
||
|
|
------WebKitFormBoundaryFileUploadTest
|
||
|
|
Content-Disposition: form-data; name="skills"
|
||
|
|
|
||
|
|
Python
|
||
|
|
------WebKitFormBoundaryFileUploadTest--
|
||
|
|
`
|
||
|
|
|
||
|
|
// Test decoding
|
||
|
|
decoded, err := form.Decode(multipartData)
|
||
|
|
require.NoError(t, err)
|
||
|
|
|
||
|
|
// Verify regular fields
|
||
|
|
assert.Equal(t, "John Doe", decoded.Get("name"))
|
||
|
|
assert.Equal(t, "john@example.com", decoded.Get("email"))
|
||
|
|
assert.Equal(t, []string{"Go", "JavaScript", "Python"}, decoded.Get("skills"))
|
||
|
|
|
||
|
|
// Verify file fields
|
||
|
|
profilePicture := decoded.Get("profile_picture")
|
||
|
|
require.NotNil(t, profilePicture)
|
||
|
|
profileArray, ok := profilePicture.([]interface{})
|
||
|
|
require.True(t, ok, "Expected []interface{} for profile_picture")
|
||
|
|
require.Len(t, profileArray, 1)
|
||
|
|
assert.Equal(t, "fake_jpeg_binary_data_here", profileArray[0])
|
||
|
|
|
||
|
|
documents := decoded.Get("documents")
|
||
|
|
require.NotNil(t, documents)
|
||
|
|
documentsArray, ok := documents.([]interface{})
|
||
|
|
require.True(t, ok, "Expected []interface{} for documents")
|
||
|
|
require.Len(t, documentsArray, 2)
|
||
|
|
assert.Contains(t, documentsArray, "fake_pdf_content_1")
|
||
|
|
assert.Contains(t, documentsArray, "fake_pdf_content_2")
|
||
|
|
}
|