Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/fuzz/dataformat/multipart.go
2070 views
1
package dataformat
2
3
import (
4
"bytes"
5
"fmt"
6
"io"
7
"mime"
8
"mime/multipart"
9
"net/textproto"
10
11
mapsutil "github.com/projectdiscovery/utils/maps"
12
)
13
14
type MultiPartForm struct {
15
boundary string
16
filesMetadata map[string]FileMetadata
17
}
18
19
type FileMetadata struct {
20
ContentType string
21
Filename string
22
}
23
24
var (
25
_ DataFormat = &MultiPartForm{}
26
)
27
28
// NewMultiPartForm returns a new MultiPartForm encoder
29
func NewMultiPartForm() *MultiPartForm {
30
return &MultiPartForm{}
31
}
32
33
// IsType returns true if the data is MultiPartForm encoded
34
func (m *MultiPartForm) IsType(data string) bool {
35
// This method should be implemented to detect if the data is multipart form encoded
36
return false
37
}
38
39
// Encode encodes the data into MultiPartForm format
40
func (m *MultiPartForm) Encode(data KV) (string, error) {
41
var b bytes.Buffer
42
w := multipart.NewWriter(&b)
43
if err := w.SetBoundary(m.boundary); err != nil {
44
return "", err
45
}
46
47
var Itererr error
48
data.Iterate(func(key string, value any) bool {
49
var fw io.Writer
50
var err error
51
52
if fileMetadata, ok := m.filesMetadata[key]; ok {
53
if filesArray, isArray := value.([]any); isArray {
54
for _, file := range filesArray {
55
h := make(textproto.MIMEHeader)
56
h.Set("Content-Disposition",
57
fmt.Sprintf(`form-data; name=%q; filename=%q`,
58
key, fileMetadata.Filename))
59
h.Set("Content-Type", fileMetadata.ContentType)
60
61
if fw, err = w.CreatePart(h); err != nil {
62
Itererr = err
63
return false
64
}
65
66
if _, err = fw.Write([]byte(file.(string))); err != nil {
67
Itererr = err
68
return false
69
}
70
}
71
72
return true
73
}
74
}
75
76
// Add field
77
var values []string
78
switch v := value.(type) {
79
case nil:
80
values = []string{""}
81
case string:
82
values = []string{v}
83
case []string:
84
values = v
85
case []any:
86
values = make([]string, len(v))
87
for i, item := range v {
88
if item == nil {
89
values[i] = ""
90
} else {
91
values[i] = fmt.Sprint(item)
92
}
93
}
94
default:
95
values = []string{fmt.Sprintf("%v", v)}
96
}
97
98
for _, val := range values {
99
if fw, err = w.CreateFormField(key); err != nil {
100
Itererr = err
101
return false
102
}
103
if _, err = fw.Write([]byte(val)); err != nil {
104
Itererr = err
105
return false
106
}
107
}
108
return true
109
})
110
if Itererr != nil {
111
return "", Itererr
112
}
113
114
_ = w.Close()
115
return b.String(), nil
116
}
117
118
// ParseBoundary parses the boundary from the content type
119
func (m *MultiPartForm) ParseBoundary(contentType string) error {
120
_, params, err := mime.ParseMediaType(contentType)
121
if err != nil {
122
return err
123
}
124
m.boundary = params["boundary"]
125
if m.boundary == "" {
126
return fmt.Errorf("no boundary found in the content type")
127
}
128
return nil
129
}
130
131
// Decode decodes the data from MultiPartForm format
132
func (m *MultiPartForm) Decode(data string) (KV, error) {
133
// Create a buffer from the string data
134
b := bytes.NewBufferString(data)
135
// The boundary parameter should be extracted from the Content-Type header of the HTTP request
136
// which is not available in this context, so this is a placeholder for demonstration.
137
// You will need to pass the actual boundary value to this function.
138
r := multipart.NewReader(b, m.boundary)
139
140
form, err := r.ReadForm(32 << 20) // 32MB is the max memory used to parse the form
141
if err != nil {
142
return KV{}, err
143
}
144
defer func() {
145
_ = form.RemoveAll()
146
}()
147
148
result := mapsutil.NewOrderedMap[string, any]()
149
for key, values := range form.Value {
150
if len(values) > 1 {
151
result.Set(key, values)
152
} else {
153
result.Set(key, values[0])
154
}
155
}
156
m.filesMetadata = make(map[string]FileMetadata)
157
for key, files := range form.File {
158
fileContents := []interface{}{}
159
for _, fileHeader := range files {
160
file, err := fileHeader.Open()
161
if err != nil {
162
return KV{}, err
163
}
164
defer func() {
165
_ = file.Close()
166
}()
167
168
buffer := new(bytes.Buffer)
169
if _, err := buffer.ReadFrom(file); err != nil {
170
return KV{}, err
171
}
172
fileContents = append(fileContents, buffer.String())
173
174
m.filesMetadata[key] = FileMetadata{
175
ContentType: fileHeader.Header.Get("Content-Type"),
176
Filename: fileHeader.Filename,
177
}
178
}
179
result.Set(key, fileContents)
180
}
181
return KVOrderedMap(&result), nil
182
}
183
184
// Name returns the name of the encoder
185
func (m *MultiPartForm) Name() string {
186
return "multipart/form-data"
187
}
188
189