Path: blob/dev/pkg/fuzz/dataformat/multipart_test.go
2070 views
package dataformat12import (3"testing"45mapsutil "github.com/projectdiscovery/utils/maps"6"github.com/stretchr/testify/assert"7"github.com/stretchr/testify/require"8)910func TestMultiPartFormEncode(t *testing.T) {11tests := []struct {12name string13fields map[string]any14wantErr bool15expected map[string]any16}{17{18name: "duplicate fields ([]string) - checkbox scenario",19fields: map[string]any{20"interests": []string{"sports", "music", "reading"},21"colors": []string{"red", "blue"},22},23expected: map[string]any{24"interests": []string{"sports", "music", "reading"},25"colors": []string{"red", "blue"},26},27},28{29name: "single string fields - backward compatibility",30fields: map[string]any{31"username": "john",32"email": "[email protected]",33},34expected: map[string]any{35"username": "john",36"email": "[email protected]",37},38},39{40name: "mixed types",41fields: map[string]any{42"string": "text",43"array": []string{"item1", "item2"},44"number": 42, // tests fmt.Sprint fallback45"float": 3.14, // tests float conversion46"boolean": true, // tests boolean conversion47"zero": 0, // tests zero value48"emptyStr": "", // tests empty string49"negative": -123, // tests negative number50"nil": nil, // tests nil value51"mixedArray": []any{"str", 123, false, nil}, // tests mixed type array52},53expected: map[string]any{54"string": "text",55"array": []string{"item1", "item2"},56"number": "42", // numbers are converted to strings in multipart57"float": "3.14", // floats are converted to strings58"boolean": "true", // booleans are converted to strings59"zero": "0", // zero value converted to string60"emptyStr": "", // empty string remains empty61"negative": "-123", // negative numbers converted to strings62"nil": "", // nil values converted to "" string63"mixedArray": []string{"str", "123", "false", ""}, // mixed array converted to string array64},65},66{67name: "empty array - should not appear in output",68fields: map[string]any{69"emptyArray": []string{},70"normalField": "value",71},72expected: map[string]any{73"normalField": "value",74// emptyArray should not appear in decoded output75},76},77}7879for _, tt := range tests {80t.Run(tt.name, func(t *testing.T) {81defer func() {82if r := recover(); r != nil {83t.Errorf("Test panicked: %v", r)84}85}()8687form := NewMultiPartForm()88form.boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW"8990kv := mapsutil.NewOrderedMap[string, any]()91for k, v := range tt.fields {92kv.Set(k, v)93}9495encoded, err := form.Encode(KVOrderedMap(&kv))9697if tt.wantErr {98require.Error(t, err)99return100}101102require.NoError(t, err)103104// Decode the encoded multipart data105decoded, err := form.Decode(encoded)106require.NoError(t, err)107108// Compare decoded values with expected values109for expectedKey, expectedValue := range tt.expected {110actualValue := decoded.Get(expectedKey)111switch expected := expectedValue.(type) {112case []string:113actual, ok := actualValue.([]string)114require.True(t, ok, "Expected []string for key %s, got %T", expectedKey, actualValue)115assert.ElementsMatch(t, expected, actual, "Values mismatch for key %s", expectedKey)116case []any:117actual, ok := actualValue.([]any)118require.True(t, ok, "Expected []any for key %s, got %T", expectedKey, actualValue)119assert.ElementsMatch(t, expected, actual, "Values mismatch for key %s", expectedKey)120case string:121actual, ok := actualValue.(string)122require.True(t, ok, "Expected string for key %s, got %T", expectedKey, actualValue)123assert.Equal(t, expected, actual, "Values mismatch for key %s", expectedKey)124default:125assert.Equal(t, expected, actualValue, "Values mismatch for key %s", expectedKey)126}127}128129// Ensure no unexpected keys are present in decoded output130decoded.Iterate(func(key string, value any) bool {131_, exists := tt.expected[key]132assert.True(t, exists, "Unexpected key %s found in decoded output", key)133return true134})135136t.Logf("Encoded output:\n%s", encoded)137})138}139}140141func TestMultiPartFormRoundTrip(t *testing.T) {142defer func() {143if r := recover(); r != nil {144t.Errorf("Test panicked: %v", r)145}146}()147148form := NewMultiPartForm()149form.boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW"150151original := mapsutil.NewOrderedMap[string, any]()152original.Set("username", "john")153original.Set("interests", []string{"sports", "music", "reading"})154155encoded, err := form.Encode(KVOrderedMap(&original))156require.NoError(t, err)157158decoded, err := form.Decode(encoded)159require.NoError(t, err)160161assert.Equal(t, "john", decoded.Get("username"))162assert.ElementsMatch(t, []string{"sports", "music", "reading"}, decoded.Get("interests"))163164t.Logf("Encoded output:\n%s", encoded)165}166167func TestMultiPartFormFileUpload(t *testing.T) {168defer func() {169if r := recover(); r != nil {170t.Errorf("Test panicked: %v", r)171}172}()173174// Test decoding of a manually crafted multipart form with files175form := NewMultiPartForm()176form.boundary = "----WebKitFormBoundaryFileUploadTest"177178// Manually craft a multipart form with file uploads179multipartData := `------WebKitFormBoundaryFileUploadTest180Content-Disposition: form-data; name="name"181182John Doe183------WebKitFormBoundaryFileUploadTest184Content-Disposition: form-data; name="email"185186[email protected]187------WebKitFormBoundaryFileUploadTest188Content-Disposition: form-data; name="profile_picture"; filename="profile.jpg"189Content-Type: image/jpeg190191fake_jpeg_binary_data_here192------WebKitFormBoundaryFileUploadTest193Content-Disposition: form-data; name="documents"; filename="resume.pdf"194Content-Type: application/pdf195196fake_pdf_content_1197------WebKitFormBoundaryFileUploadTest198Content-Disposition: form-data; name="documents"; filename="cover_letter.pdf"199Content-Type: application/pdf200201fake_pdf_content_2202------WebKitFormBoundaryFileUploadTest203Content-Disposition: form-data; name="skills"204205Go206------WebKitFormBoundaryFileUploadTest207Content-Disposition: form-data; name="skills"208209JavaScript210------WebKitFormBoundaryFileUploadTest211Content-Disposition: form-data; name="skills"212213Python214------WebKitFormBoundaryFileUploadTest--215`216217// Test decoding218decoded, err := form.Decode(multipartData)219require.NoError(t, err)220221// Verify regular fields222assert.Equal(t, "John Doe", decoded.Get("name"))223assert.Equal(t, "[email protected]", decoded.Get("email"))224assert.Equal(t, []string{"Go", "JavaScript", "Python"}, decoded.Get("skills"))225226// Verify file fields227profilePicture := decoded.Get("profile_picture")228require.NotNil(t, profilePicture)229profileArray, ok := profilePicture.([]interface{})230require.True(t, ok, "Expected []interface{} for profile_picture")231require.Len(t, profileArray, 1)232assert.Equal(t, "fake_jpeg_binary_data_here", profileArray[0])233234documents := decoded.Get("documents")235require.NotNil(t, documents)236documentsArray, ok := documents.([]interface{})237require.True(t, ok, "Expected []interface{} for documents")238require.Len(t, documentsArray, 2)239assert.Contains(t, documentsArray, "fake_pdf_content_1")240assert.Contains(t, documentsArray, "fake_pdf_content_2")241}242243244