Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/templates/compile_test.go
2845 views
1
package templates_test
2
3
import (
4
"context"
5
"fmt"
6
"log"
7
netHttp "net/http"
8
"net/http/httptest"
9
"os"
10
"testing"
11
"time"
12
13
"github.com/julienschmidt/httprouter"
14
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
15
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/disk"
16
"github.com/projectdiscovery/nuclei/v3/pkg/loader/workflow"
17
"github.com/projectdiscovery/nuclei/v3/pkg/model"
18
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/severity"
19
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/stringslice"
20
"github.com/projectdiscovery/nuclei/v3/pkg/operators"
21
"github.com/projectdiscovery/nuclei/v3/pkg/operators/matchers"
22
"github.com/projectdiscovery/nuclei/v3/pkg/progress"
23
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
24
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
25
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/globalmatchers"
26
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/variables"
27
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/http"
28
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
29
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
30
"github.com/projectdiscovery/nuclei/v3/pkg/workflows"
31
"github.com/projectdiscovery/ratelimit"
32
"github.com/stretchr/testify/require"
33
)
34
35
var executerOpts *protocols.ExecutorOptions
36
37
func setup() {
38
options := testutils.DefaultOptions
39
testutils.Init(options)
40
progressImpl, _ := progress.NewStatsTicker(0, false, false, false, 0)
41
42
executerOpts = &protocols.ExecutorOptions{
43
Output: testutils.NewMockOutputWriter(options.OmitTemplate),
44
Options: options,
45
Progress: progressImpl,
46
ProjectFile: nil,
47
IssuesClient: nil,
48
Browser: nil,
49
Catalog: disk.NewCatalog(config.DefaultConfig.TemplatesDirectory),
50
RateLimiter: ratelimit.New(context.Background(), uint(options.RateLimit), time.Second),
51
Parser: templates.NewParser(),
52
}
53
workflowLoader, err := workflow.NewLoader(executerOpts)
54
if err != nil {
55
log.Fatalf("Could not create workflow loader: %s\n", err)
56
}
57
executerOpts.WorkflowLoader = workflowLoader
58
}
59
60
func Test_ParseFromURL(t *testing.T) {
61
router := httprouter.New()
62
router.GET("/match-1.yaml", func(w netHttp.ResponseWriter, r *netHttp.Request, _ httprouter.Params) {
63
b, err := os.ReadFile("tests/match-1.yaml")
64
if err != nil {
65
w.Write([]byte(err.Error())) // nolint: errcheck
66
}
67
w.Write(b) // nolint: errcheck
68
})
69
ts := httptest.NewServer(router)
70
defer ts.Close()
71
var expectedTemplate = &templates.Template{
72
ID: "basic-get",
73
Info: model.Info{
74
Name: "Basic GET Request",
75
Authors: stringslice.StringSlice{Value: []string{"pdteam"}},
76
SeverityHolder: severity.Holder{Severity: severity.Info},
77
},
78
RequestsHTTP: []*http.Request{{
79
Operators: operators.Operators{
80
Matchers: []*matchers.Matcher{{
81
Type: matchers.MatcherTypeHolder{
82
MatcherType: matchers.WordsMatcher,
83
},
84
Words: []string{"This is test matcher text"},
85
}},
86
},
87
Path: []string{"{{BaseURL}}"},
88
AttackType: generators.AttackTypeHolder{},
89
Method: http.HTTPMethodTypeHolder{
90
MethodType: http.HTTPGet,
91
},
92
}},
93
TotalRequests: 1,
94
Executer: nil,
95
Path: ts.URL + "/match-1.yaml",
96
}
97
setup()
98
got, err := templates.Parse(ts.URL+"/match-1.yaml", nil, executerOpts)
99
require.Nilf(t, err, "could not parse template (%s)", fmt.Sprint(err))
100
require.Nil(t, err, "could not parse template")
101
require.Equal(t, expectedTemplate.ID, got.ID)
102
require.Equal(t, expectedTemplate.Info, got.Info)
103
require.Equal(t, expectedTemplate.TotalRequests, got.TotalRequests)
104
require.Equal(t, expectedTemplate.Path, got.Path)
105
require.Equal(t, expectedTemplate.RequestsHTTP[0].Path, got.RequestsHTTP[0].Path)
106
require.Equal(t, expectedTemplate.RequestsHTTP[0].Operators.Matchers[0].Words, got.RequestsHTTP[0].Operators.Matchers[0].Words)
107
require.Equal(t, len(expectedTemplate.RequestsHTTP), len(got.RequestsHTTP))
108
}
109
110
func Test_ParseFromFile(t *testing.T) {
111
filePath := "tests/match-1.yaml"
112
expectedTemplate := &templates.Template{
113
ID: "basic-get",
114
Info: model.Info{
115
Name: "Basic GET Request",
116
Authors: stringslice.StringSlice{Value: []string{"pdteam"}},
117
SeverityHolder: severity.Holder{Severity: severity.Info},
118
},
119
RequestsHTTP: []*http.Request{{
120
Operators: operators.Operators{
121
Matchers: []*matchers.Matcher{{
122
Type: matchers.MatcherTypeHolder{
123
MatcherType: matchers.WordsMatcher,
124
},
125
Words: []string{"This is test matcher text"},
126
}},
127
},
128
Path: []string{"{{BaseURL}}"},
129
AttackType: generators.AttackTypeHolder{},
130
Method: http.HTTPMethodTypeHolder{
131
MethodType: http.HTTPGet,
132
},
133
}},
134
TotalRequests: 1,
135
Executer: nil,
136
Path: "tests/match-1.yaml",
137
}
138
setup()
139
got, err := templates.Parse(filePath, nil, executerOpts)
140
require.Nil(t, err, "could not parse template")
141
require.Equal(t, expectedTemplate.ID, got.ID)
142
require.Equal(t, expectedTemplate.Info, got.Info)
143
require.Equal(t, expectedTemplate.TotalRequests, got.TotalRequests)
144
require.Equal(t, expectedTemplate.Path, got.Path)
145
require.Equal(t, expectedTemplate.RequestsHTTP[0].Path, got.RequestsHTTP[0].Path)
146
require.Equal(t, expectedTemplate.RequestsHTTP[0].Operators.Matchers[0].Words, got.RequestsHTTP[0].Operators.Matchers[0].Words)
147
require.Equal(t, len(expectedTemplate.RequestsHTTP), len(got.RequestsHTTP))
148
149
// Test cache
150
got, err = templates.Parse(filePath, nil, executerOpts)
151
require.Nil(t, err, "could not parse template")
152
require.Equal(t, expectedTemplate.ID, got.ID)
153
}
154
155
func Test_ParseWorkflow(t *testing.T) {
156
filePath := "tests/workflow.yaml"
157
expectedTemplate := &templates.Template{
158
ID: "workflow-example",
159
Info: model.Info{
160
Name: "Test Workflow Template",
161
Authors: stringslice.StringSlice{Value: []string{"pdteam"}},
162
SeverityHolder: severity.Holder{Severity: severity.Info},
163
},
164
Workflow: workflows.Workflow{
165
Workflows: []*workflows.WorkflowTemplate{{Template: "tests/match-1.yaml"}, {Template: "tests/match-1.yaml"}},
166
Options: &protocols.ExecutorOptions{},
167
},
168
CompiledWorkflow: &workflows.Workflow{},
169
SelfContained: false,
170
StopAtFirstMatch: false,
171
Signature: http.SignatureTypeHolder{},
172
Variables: variables.Variable{},
173
TotalRequests: 0,
174
Executer: nil,
175
Path: "tests/workflow.yaml",
176
}
177
setup()
178
got, err := templates.Parse(filePath, nil, executerOpts)
179
require.Nil(t, err, "could not parse template")
180
require.Equal(t, expectedTemplate.ID, got.ID)
181
require.Equal(t, expectedTemplate.Info, got.Info)
182
require.Equal(t, expectedTemplate.TotalRequests, got.TotalRequests)
183
require.Equal(t, expectedTemplate.Path, got.Path)
184
require.Equal(t, expectedTemplate.Workflow.Workflows[0].Template, got.Workflow.Workflows[0].Template)
185
require.Equal(t, len(expectedTemplate.Workflows), len(got.Workflows))
186
}
187
188
func Test_ParseWorkflowWithGlobalMatchers(t *testing.T) {
189
setup()
190
previousGlobalMatchers := executerOpts.Options.EnableGlobalMatchersTemplates
191
executerOpts.Options.EnableGlobalMatchersTemplates = true
192
defer func() {
193
executerOpts.Options.EnableGlobalMatchersTemplates = previousGlobalMatchers
194
executerOpts.GlobalMatchers = nil
195
}()
196
executerOpts.GlobalMatchers = globalmatchers.New()
197
198
filePath := "tests/workflow-global-matchers.yaml"
199
got, err := templates.Parse(filePath, nil, executerOpts)
200
require.NoError(t, err, "could not parse workflow template")
201
require.NotNil(t, got, "workflow template should not be nil")
202
require.NotNil(t, got.CompiledWorkflow, "compiled workflow should not be nil")
203
require.Len(t, got.CompiledWorkflow.Workflows, 2)
204
require.Len(t, got.CompiledWorkflow.Workflows[0].Executers, 1)
205
require.Len(t, got.CompiledWorkflow.Workflows[1].Executers, 0)
206
}
207
208
func Test_WrongTemplate(t *testing.T) {
209
setup()
210
211
filePath := "tests/no-author.yaml"
212
got, err := templates.Parse(filePath, nil, executerOpts)
213
require.Nil(t, got, "could not parse template")
214
require.ErrorContains(t, err, "no template author field provided")
215
216
filePath = "tests/no-req.yaml"
217
got, err = templates.Parse(filePath, nil, executerOpts)
218
require.Nil(t, got, "could not parse template")
219
require.ErrorContains(t, err, "no requests defined ")
220
}
221
222
func TestWrongWorkflow(t *testing.T) {
223
setup()
224
225
filePath := "tests/workflow-invalid.yaml"
226
got, err := templates.Parse(filePath, nil, executerOpts)
227
require.Nil(t, got, "could not parse template")
228
require.ErrorContains(t, err, "workflows cannot have other protocols")
229
}
230
231