Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/cmd/integration-test/workflow.go
2070 views
1
package main
2
3
import (
4
"fmt"
5
"io"
6
"log"
7
"net/http"
8
"net/http/httptest"
9
"strings"
10
11
"github.com/julienschmidt/httprouter"
12
13
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
14
"github.com/projectdiscovery/nuclei/v3/pkg/templates/signer"
15
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
16
sliceutil "github.com/projectdiscovery/utils/slice"
17
)
18
19
var workflowTestcases = []TestCaseInfo{
20
{Path: "workflow/basic.yaml", TestCase: &workflowBasic{}},
21
{Path: "workflow/condition-matched.yaml", TestCase: &workflowConditionMatched{}},
22
{Path: "workflow/condition-unmatched.yaml", TestCase: &workflowConditionUnmatch{}},
23
{Path: "workflow/matcher-name.yaml", TestCase: &workflowMatcherName{}},
24
{Path: "workflow/complex-conditions.yaml", TestCase: &workflowComplexConditions{}},
25
{Path: "workflow/http-value-share-workflow.yaml", TestCase: &workflowHttpKeyValueShare{}},
26
{Path: "workflow/dns-value-share-workflow.yaml", TestCase: &workflowDnsKeyValueShare{}},
27
{Path: "workflow/code-value-share-workflow.yaml", TestCase: &workflowCodeKeyValueShare{}, DisableOn: isCodeDisabled}, // isCodeDisabled declared in code.go
28
{Path: "workflow/multiprotocol-value-share-workflow.yaml", TestCase: &workflowMultiProtocolKeyValueShare{}},
29
{Path: "workflow/multimatch-value-share-workflow.yaml", TestCase: &workflowMultiMatchKeyValueShare{}},
30
{Path: "workflow/shared-cookie.yaml", TestCase: &workflowSharedCookies{}},
31
}
32
33
func init() {
34
// sign code templates (unless they are disabled)
35
if !isCodeDisabled() {
36
// allow local file access to load content of file references in template
37
// in order to sign them for testing purposes
38
templates.TemplateSignerLFA()
39
40
// testCertFile and testKeyFile are declared in code.go
41
tsigner, err := signer.NewTemplateSignerFromFiles(testCertFile, testKeyFile)
42
if err != nil {
43
panic(err)
44
}
45
46
// only the code templates are necessary to be signed
47
var templatesToSign = []string{
48
"workflow/code-template-1.yaml",
49
"workflow/code-template-2.yaml",
50
}
51
for _, templatePath := range templatesToSign {
52
if err := templates.SignTemplate(tsigner, templatePath); err != nil {
53
log.Fatalf("Could not sign template %v got: %s\n", templatePath, err)
54
}
55
}
56
}
57
}
58
59
type workflowBasic struct{}
60
61
// Execute executes a test case and returns an error if occurred
62
func (h *workflowBasic) Execute(filePath string) error {
63
router := httprouter.New()
64
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
65
_, _ = fmt.Fprintf(w, "This is test matcher text")
66
})
67
ts := httptest.NewServer(router)
68
defer ts.Close()
69
70
results, err := testutils.RunNucleiWorkflowAndGetResults(filePath, ts.URL, debug)
71
if err != nil {
72
return err
73
}
74
75
return expectResultsCount(results, 2)
76
}
77
78
type workflowConditionMatched struct{}
79
80
// Execute executes a test case and returns an error if occurred
81
func (h *workflowConditionMatched) Execute(filePath string) error {
82
router := httprouter.New()
83
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
84
_, _ = fmt.Fprintf(w, "This is test matcher text")
85
})
86
ts := httptest.NewServer(router)
87
defer ts.Close()
88
89
results, err := testutils.RunNucleiWorkflowAndGetResults(filePath, ts.URL, debug)
90
if err != nil {
91
return err
92
}
93
94
return expectResultsCount(results, 1)
95
}
96
97
type workflowConditionUnmatch struct{}
98
99
// Execute executes a test case and returns an error if occurred
100
func (h *workflowConditionUnmatch) Execute(filePath string) error {
101
router := httprouter.New()
102
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
103
_, _ = fmt.Fprintf(w, "This is test matcher text")
104
})
105
ts := httptest.NewServer(router)
106
defer ts.Close()
107
108
results, err := testutils.RunNucleiWorkflowAndGetResults(filePath, ts.URL, debug)
109
if err != nil {
110
return err
111
}
112
113
return expectResultsCount(results, 0)
114
}
115
116
type workflowMatcherName struct{}
117
118
// Execute executes a test case and returns an error if occurred
119
func (h *workflowMatcherName) Execute(filePath string) error {
120
router := httprouter.New()
121
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
122
_, _ = fmt.Fprintf(w, "This is test matcher text")
123
})
124
ts := httptest.NewServer(router)
125
defer ts.Close()
126
127
results, err := testutils.RunNucleiWorkflowAndGetResults(filePath, ts.URL, debug)
128
if err != nil {
129
return err
130
}
131
132
return expectResultsCount(results, 1)
133
}
134
135
type workflowComplexConditions struct{}
136
137
// Execute executes a test case and returns an error if occurred
138
func (h *workflowComplexConditions) Execute(filePath string) error {
139
router := httprouter.New()
140
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
141
_, _ = fmt.Fprintf(w, "This is test matcher text")
142
})
143
ts := httptest.NewServer(router)
144
defer ts.Close()
145
146
results, err := testutils.RunNucleiWorkflowAndGetResults(filePath, ts.URL, debug)
147
if err != nil {
148
return err
149
}
150
151
for _, result := range results {
152
if !strings.Contains(result, "test-matcher-3") {
153
return fmt.Errorf("incorrect result: the \"basic-get-third:test-matcher-3\" and only that should be matched!\nResults:\n\t%s", strings.Join(results, "\n\t"))
154
}
155
}
156
return expectResultsCount(results, 2)
157
}
158
159
type workflowHttpKeyValueShare struct{}
160
161
// Execute executes a test case and returns an error if occurred
162
func (h *workflowHttpKeyValueShare) Execute(filePath string) error {
163
router := httprouter.New()
164
router.GET("/path1", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
165
_, _ = fmt.Fprintf(w, "href=\"test-value\"")
166
})
167
router.GET("/path2", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
168
body, _ := io.ReadAll(r.Body)
169
_, _ = fmt.Fprintf(w, "%s", body)
170
})
171
ts := httptest.NewServer(router)
172
defer ts.Close()
173
174
results, err := testutils.RunNucleiWorkflowAndGetResults(filePath, ts.URL, debug)
175
if err != nil {
176
return err
177
}
178
179
return expectResultsCount(results, 1)
180
}
181
182
type workflowDnsKeyValueShare struct{}
183
184
// Execute executes a test case and returns an error if occurred
185
func (h *workflowDnsKeyValueShare) Execute(filePath string) error {
186
results, err := testutils.RunNucleiWorkflowAndGetResults(filePath, "http://scanme.sh", debug)
187
if err != nil {
188
return err
189
}
190
191
// no results - ensure that the variable sharing works
192
return expectResultsCount(results, 1)
193
}
194
195
type workflowCodeKeyValueShare struct{}
196
197
// Execute executes a test case and returns an error if occurred
198
func (h *workflowCodeKeyValueShare) Execute(filePath string) error {
199
// provide the Certificate File that the code templates are signed with
200
certEnvVar := signer.CertEnvVarName + "=" + testCertFile
201
202
results, err := testutils.RunNucleiArgsWithEnvAndGetResults(debug, []string{certEnvVar}, "-workflows", filePath, "-target", "input", "-code")
203
if err != nil {
204
return err
205
}
206
207
return expectResultsCount(results, 1)
208
}
209
210
type workflowMultiProtocolKeyValueShare struct{}
211
212
// Execute executes a test case and returns an error if occurred
213
func (h *workflowMultiProtocolKeyValueShare) Execute(filePath string) error {
214
router := httprouter.New()
215
// the response of path1 contains a domain that will be extracted and shared with the second template
216
router.GET("/path1", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
217
_, _ = fmt.Fprintf(w, "href=\"blog.projectdiscovery.io\"")
218
})
219
// path2 responds with the value of the "extracted" query parameter, e.g.: /path2?extracted=blog.projectdiscovery.io => blog.projectdiscovery.io
220
router.GET("/path2", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
221
_, _ = fmt.Fprintf(w, "%s", r.URL.Query().Get("extracted"))
222
})
223
ts := httptest.NewServer(router)
224
defer ts.Close()
225
226
results, err := testutils.RunNucleiWorkflowAndGetResults(filePath, ts.URL, debug)
227
if err != nil {
228
return err
229
}
230
231
return expectResultsCount(results, 2)
232
}
233
234
type workflowMultiMatchKeyValueShare struct{}
235
236
// Execute executes a test case and returns an error if occurred
237
func (h *workflowMultiMatchKeyValueShare) Execute(filePath string) error {
238
var receivedData []string
239
router := httprouter.New()
240
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
241
_, _ = fmt.Fprintf(w, "This is test matcher text")
242
})
243
router.GET("/path1", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
244
_, _ = fmt.Fprintf(w, "href=\"test-value-%s\"", r.URL.Query().Get("v"))
245
})
246
router.GET("/path2", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
247
body, _ := io.ReadAll(r.Body)
248
receivedData = append(receivedData, string(body))
249
_, _ = fmt.Fprintf(w, "test-value")
250
})
251
ts := httptest.NewServer(router)
252
defer ts.Close()
253
254
results, err := testutils.RunNucleiWorkflowAndGetResults(filePath, ts.URL, debug)
255
if err != nil {
256
return err
257
}
258
259
// Check if we received the data from both request to /path1 and it is not overwritten by the later one.
260
// They will appear in brackets because of another bug: https://github.com/orgs/projectdiscovery/discussions/3766
261
if !sliceutil.Contains(receivedData, "[test-value-1]") || !sliceutil.Contains(receivedData, "[test-value-2]") {
262
return fmt.Errorf(
263
"incorrect data: did not receive both extracted data from the first request!\nReceived Data:\n\t%s\nResults:\n\t%s",
264
strings.Join(receivedData, "\n\t"),
265
strings.Join(results, "\n\t"),
266
)
267
}
268
// The number of expected results is 3: the workflow's Matcher Name based condition check forwards both match, and the other branch with simple subtemplates goes with one
269
return expectResultsCount(results, 3)
270
}
271
272
type workflowSharedCookies struct{}
273
274
// Execute executes a test case and returns an error if occurred
275
func (h *workflowSharedCookies) Execute(filePath string) error {
276
handleFunc := func(name string, w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
277
cookie := &http.Cookie{Name: name, Value: name}
278
http.SetCookie(w, cookie)
279
}
280
281
var gotCookies []string
282
router := httprouter.New()
283
router.GET("/http1", func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
284
handleFunc("http1", w, r, p)
285
})
286
router.GET("/http2", func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
287
handleFunc("http2", w, r, p)
288
})
289
router.GET("/headless1", func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
290
handleFunc("headless1", w, r, p)
291
})
292
router.GET("/http3", func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
293
for _, cookie := range r.Cookies() {
294
gotCookies = append(gotCookies, cookie.Name)
295
}
296
})
297
ts := httptest.NewServer(router)
298
defer ts.Close()
299
300
_, err := testutils.RunNucleiWorkflowAndGetResults(filePath, ts.URL, debug, "-headless")
301
if err != nil {
302
return err
303
}
304
305
return expectResultsCount(gotCookies, 3)
306
}
307
308