Path: blob/dev/pkg/fuzz/dataformat/multipart.go
2070 views
package dataformat12import (3"bytes"4"fmt"5"io"6"mime"7"mime/multipart"8"net/textproto"910mapsutil "github.com/projectdiscovery/utils/maps"11)1213type MultiPartForm struct {14boundary string15filesMetadata map[string]FileMetadata16}1718type FileMetadata struct {19ContentType string20Filename string21}2223var (24_ DataFormat = &MultiPartForm{}25)2627// NewMultiPartForm returns a new MultiPartForm encoder28func NewMultiPartForm() *MultiPartForm {29return &MultiPartForm{}30}3132// IsType returns true if the data is MultiPartForm encoded33func (m *MultiPartForm) IsType(data string) bool {34// This method should be implemented to detect if the data is multipart form encoded35return false36}3738// Encode encodes the data into MultiPartForm format39func (m *MultiPartForm) Encode(data KV) (string, error) {40var b bytes.Buffer41w := multipart.NewWriter(&b)42if err := w.SetBoundary(m.boundary); err != nil {43return "", err44}4546var Itererr error47data.Iterate(func(key string, value any) bool {48var fw io.Writer49var err error5051if fileMetadata, ok := m.filesMetadata[key]; ok {52if filesArray, isArray := value.([]any); isArray {53for _, file := range filesArray {54h := make(textproto.MIMEHeader)55h.Set("Content-Disposition",56fmt.Sprintf(`form-data; name=%q; filename=%q`,57key, fileMetadata.Filename))58h.Set("Content-Type", fileMetadata.ContentType)5960if fw, err = w.CreatePart(h); err != nil {61Itererr = err62return false63}6465if _, err = fw.Write([]byte(file.(string))); err != nil {66Itererr = err67return false68}69}7071return true72}73}7475// Add field76var values []string77switch v := value.(type) {78case nil:79values = []string{""}80case string:81values = []string{v}82case []string:83values = v84case []any:85values = make([]string, len(v))86for i, item := range v {87if item == nil {88values[i] = ""89} else {90values[i] = fmt.Sprint(item)91}92}93default:94values = []string{fmt.Sprintf("%v", v)}95}9697for _, val := range values {98if fw, err = w.CreateFormField(key); err != nil {99Itererr = err100return false101}102if _, err = fw.Write([]byte(val)); err != nil {103Itererr = err104return false105}106}107return true108})109if Itererr != nil {110return "", Itererr111}112113_ = w.Close()114return b.String(), nil115}116117// ParseBoundary parses the boundary from the content type118func (m *MultiPartForm) ParseBoundary(contentType string) error {119_, params, err := mime.ParseMediaType(contentType)120if err != nil {121return err122}123m.boundary = params["boundary"]124if m.boundary == "" {125return fmt.Errorf("no boundary found in the content type")126}127return nil128}129130// Decode decodes the data from MultiPartForm format131func (m *MultiPartForm) Decode(data string) (KV, error) {132// Create a buffer from the string data133b := bytes.NewBufferString(data)134// The boundary parameter should be extracted from the Content-Type header of the HTTP request135// which is not available in this context, so this is a placeholder for demonstration.136// You will need to pass the actual boundary value to this function.137r := multipart.NewReader(b, m.boundary)138139form, err := r.ReadForm(32 << 20) // 32MB is the max memory used to parse the form140if err != nil {141return KV{}, err142}143defer func() {144_ = form.RemoveAll()145}()146147result := mapsutil.NewOrderedMap[string, any]()148for key, values := range form.Value {149if len(values) > 1 {150result.Set(key, values)151} else {152result.Set(key, values[0])153}154}155m.filesMetadata = make(map[string]FileMetadata)156for key, files := range form.File {157fileContents := []interface{}{}158for _, fileHeader := range files {159file, err := fileHeader.Open()160if err != nil {161return KV{}, err162}163defer func() {164_ = file.Close()165}()166167buffer := new(bytes.Buffer)168if _, err := buffer.ReadFrom(file); err != nil {169return KV{}, err170}171fileContents = append(fileContents, buffer.String())172173m.filesMetadata[key] = FileMetadata{174ContentType: fileHeader.Header.Get("Content-Type"),175Filename: fileHeader.Filename,176}177}178result.Set(key, fileContents)179}180return KVOrderedMap(&result), nil181}182183// Name returns the name of the encoder184func (m *MultiPartForm) Name() string {185return "multipart/form-data"186}187188189