Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/fuzz/component/path.go
2070 views
1
package component
2
3
import (
4
"context"
5
"strconv"
6
"strings"
7
8
"github.com/projectdiscovery/nuclei/v3/pkg/fuzz/dataformat"
9
"github.com/projectdiscovery/retryablehttp-go"
10
urlutil "github.com/projectdiscovery/utils/url"
11
)
12
13
// Path is a component for a request Path
14
type Path struct {
15
value *Value
16
17
req *retryablehttp.Request
18
}
19
20
var _ Component = &Path{}
21
22
// NewPath creates a new URL component
23
func NewPath() *Path {
24
return &Path{}
25
}
26
27
// Name returns the name of the component
28
func (q *Path) Name() string {
29
return RequestPathComponent
30
}
31
32
// Parse parses the component and returns the
33
// parsed component
34
func (q *Path) Parse(req *retryablehttp.Request) (bool, error) {
35
q.req = req
36
q.value = NewValue("")
37
38
splitted := strings.Split(req.Path, "/")
39
values := make(map[string]interface{})
40
for i, segment := range splitted {
41
if segment == "" && i == 0 {
42
// Skip the first empty segment from leading "/"
43
continue
44
}
45
if segment == "" {
46
// Skip any other empty segments
47
continue
48
}
49
// Use 1-based indexing and store individual segments
50
key := strconv.Itoa(len(values) + 1)
51
values[key] = segment
52
}
53
q.value.SetParsed(dataformat.KVMap(values), "")
54
return true, nil
55
}
56
57
// Iterate iterates through the component
58
func (q *Path) Iterate(callback func(key string, value interface{}) error) (err error) {
59
q.value.parsed.Iterate(func(key string, value any) bool {
60
if errx := callback(key, value); errx != nil {
61
err = errx
62
return false
63
}
64
return true
65
})
66
return
67
}
68
69
// SetValue sets a value in the component
70
// for a key
71
func (q *Path) SetValue(key string, value string) error {
72
escaped := urlutil.PathEncode(value)
73
if !q.value.SetParsedValue(key, escaped) {
74
return ErrSetValue
75
}
76
return nil
77
}
78
79
// Delete deletes a key from the component
80
func (q *Path) Delete(key string) error {
81
if !q.value.Delete(key) {
82
return ErrKeyNotFound
83
}
84
return nil
85
}
86
87
// Rebuild returns a new request with the
88
// component rebuilt
89
func (q *Path) Rebuild() (*retryablehttp.Request, error) {
90
// Get the original path segments
91
originalSplitted := strings.Split(q.req.Path, "/")
92
93
// Create a new slice to hold the rebuilt segments
94
rebuiltSegments := make([]string, 0, len(originalSplitted))
95
96
// Add the first empty segment (from leading "/")
97
if len(originalSplitted) > 0 && originalSplitted[0] == "" {
98
rebuiltSegments = append(rebuiltSegments, "")
99
}
100
101
// Process each segment
102
segmentIndex := 1 // 1-based indexing for our stored values
103
for i := 1; i < len(originalSplitted); i++ {
104
originalSegment := originalSplitted[i]
105
if originalSegment == "" {
106
// Skip empty segments
107
continue
108
}
109
110
// Check if we have a replacement for this segment
111
key := strconv.Itoa(segmentIndex)
112
if newValue, exists := q.value.parsed.Map.GetOrDefault(key, "").(string); exists && newValue != "" {
113
rebuiltSegments = append(rebuiltSegments, newValue)
114
} else {
115
rebuiltSegments = append(rebuiltSegments, originalSegment)
116
}
117
segmentIndex++
118
}
119
120
// Join the segments back into a path
121
rebuiltPath := strings.Join(rebuiltSegments, "/")
122
123
if unescaped, err := urlutil.PathDecode(rebuiltPath); err == nil {
124
// this is handle the case where anyportion of path has url encoded data
125
// by default the http/request official library will escape/encode special characters in path
126
// to avoid double encoding we unescape/decode already encoded value
127
//
128
// if there is a invalid url encoded value like %99 then it will still be encoded as %2599 and not %99
129
// the only way to make sure it stays as %99 is to implement raw request and unsafe for fuzzing as well
130
rebuiltPath = unescaped
131
}
132
133
// Clone the request and update the path
134
cloned := q.req.Clone(context.Background())
135
if err := cloned.UpdateRelPath(rebuiltPath, true); err != nil {
136
cloned.RawPath = rebuiltPath
137
}
138
return cloned, nil
139
}
140
141
// Clones current state to a new component
142
func (q *Path) Clone() Component {
143
return &Path{
144
value: q.value.Clone(),
145
req: q.req.Clone(context.Background()),
146
}
147
}
148
149