Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/fuzz/component/body.go
2070 views
1
package component
2
3
import (
4
"bytes"
5
"context"
6
"io"
7
"strconv"
8
"strings"
9
10
"github.com/pkg/errors"
11
"github.com/projectdiscovery/gologger"
12
"github.com/projectdiscovery/nuclei/v3/pkg/fuzz/dataformat"
13
"github.com/projectdiscovery/retryablehttp-go"
14
readerutil "github.com/projectdiscovery/utils/reader"
15
)
16
17
// Body is a component for a request body
18
type Body struct {
19
value *Value
20
21
req *retryablehttp.Request
22
}
23
24
var _ Component = &Body{}
25
26
// NewBody creates a new body component
27
func NewBody() *Body {
28
return &Body{}
29
}
30
31
// Name returns the name of the component
32
func (b *Body) Name() string {
33
return RequestBodyComponent
34
}
35
36
// Parse parses the component and returns the
37
// parsed component
38
func (b *Body) Parse(req *retryablehttp.Request) (bool, error) {
39
if req.Body == nil {
40
return false, nil
41
}
42
b.req = req
43
44
contentType := req.Header.Get("Content-Type")
45
46
data, err := io.ReadAll(req.Body)
47
if err != nil {
48
return false, errors.Wrap(err, "could not read body")
49
}
50
req.Body = io.NopCloser(bytes.NewReader(data))
51
dataStr := string(data)
52
53
if dataStr == "" {
54
return false, nil
55
}
56
57
b.value = NewValue(dataStr)
58
tmp := b.value.Parsed()
59
if !tmp.IsNIL() {
60
return true, nil
61
}
62
63
switch {
64
case strings.Contains(contentType, "application/json") && tmp.IsNIL():
65
return b.parseBody(dataformat.JSONDataFormat, req)
66
case strings.Contains(contentType, "application/xml") && tmp.IsNIL():
67
return b.parseBody(dataformat.XMLDataFormat, req)
68
case strings.Contains(contentType, "multipart/form-data") && tmp.IsNIL():
69
return b.parseBody(dataformat.MultiPartFormDataFormat, req)
70
}
71
parsed, err := b.parseBody(dataformat.FormDataFormat, req)
72
if err != nil {
73
gologger.Warning().Msgf("Could not parse body as form data: %s\n", err)
74
return b.parseBody(dataformat.RawDataFormat, req)
75
}
76
return parsed, err
77
}
78
79
// parseBody parses a body with a custom decoder
80
func (b *Body) parseBody(decoderName string, req *retryablehttp.Request) (bool, error) {
81
decoder := dataformat.Get(decoderName)
82
if decoderName == dataformat.MultiPartFormDataFormat {
83
// set content type to extract boundary
84
if err := decoder.(*dataformat.MultiPartForm).ParseBoundary(req.Header.Get("Content-Type")); err != nil {
85
return false, errors.Wrap(err, "could not parse boundary")
86
}
87
}
88
decoded, err := decoder.Decode(b.value.String())
89
if err != nil {
90
return false, errors.Wrap(err, "could not decode raw")
91
}
92
b.value.SetParsed(decoded, decoder.Name())
93
return true, nil
94
}
95
96
// Iterate iterates through the component
97
func (b *Body) Iterate(callback func(key string, value interface{}) error) (errx error) {
98
b.value.parsed.Iterate(func(key string, value any) bool {
99
if strings.HasPrefix(key, "#_") {
100
return true
101
}
102
if err := callback(key, value); err != nil {
103
errx = err
104
return false
105
}
106
return true
107
})
108
return
109
}
110
111
// SetValue sets a value in the component
112
func (b *Body) SetValue(key string, value string) error {
113
if !b.value.SetParsedValue(key, value) {
114
return ErrSetValue
115
}
116
return nil
117
}
118
119
// Delete deletes a key from the component
120
func (b *Body) Delete(key string) error {
121
if !b.value.Delete(key) {
122
return ErrKeyNotFound
123
}
124
return nil
125
}
126
127
// Rebuild returns a new request with the
128
// component rebuilt
129
func (b *Body) Rebuild() (*retryablehttp.Request, error) {
130
encoded, err := b.value.Encode()
131
if err != nil {
132
return nil, errors.Wrap(err, "could not encode body")
133
}
134
cloned := b.req.Clone(context.Background())
135
reusableReader, err := readerutil.NewReusableReadCloser(encoded)
136
if err != nil {
137
return nil, errors.Wrap(err, "could not create reusable reader")
138
}
139
cloned.Body = reusableReader
140
cloned.ContentLength = int64(len(encoded))
141
cloned.Header.Set("Content-Length", strconv.Itoa(len(encoded)))
142
return cloned, nil
143
}
144
145
func (b *Body) Clone() Component {
146
return &Body{
147
value: b.value.Clone(),
148
req: b.req.Clone(context.Background()),
149
}
150
}
151
152