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