github.com/projectdiscovery/nuclei/v2@v2.9.15/pkg/templates/compile_test.go (about) 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/v2/pkg/catalog/config" 15 "github.com/projectdiscovery/nuclei/v2/pkg/catalog/disk" 16 "github.com/projectdiscovery/nuclei/v2/pkg/model" 17 "github.com/projectdiscovery/nuclei/v2/pkg/model/types/severity" 18 "github.com/projectdiscovery/nuclei/v2/pkg/model/types/stringslice" 19 "github.com/projectdiscovery/nuclei/v2/pkg/operators" 20 "github.com/projectdiscovery/nuclei/v2/pkg/operators/matchers" 21 "github.com/projectdiscovery/nuclei/v2/pkg/parsers" 22 "github.com/projectdiscovery/nuclei/v2/pkg/progress" 23 "github.com/projectdiscovery/nuclei/v2/pkg/protocols" 24 "github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/generators" 25 "github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/variables" 26 "github.com/projectdiscovery/nuclei/v2/pkg/protocols/http" 27 "github.com/projectdiscovery/nuclei/v2/pkg/templates" 28 "github.com/projectdiscovery/nuclei/v2/pkg/testutils" 29 "github.com/projectdiscovery/nuclei/v2/pkg/workflows" 30 "github.com/projectdiscovery/ratelimit" 31 "github.com/stretchr/testify/require" 32 ) 33 34 var executerOpts protocols.ExecutorOptions 35 36 func setup() { 37 options := testutils.DefaultOptions 38 testutils.Init(options) 39 progressImpl, _ := progress.NewStatsTicker(0, false, false, false, false, 0) 40 41 executerOpts = protocols.ExecutorOptions{ 42 Output: testutils.NewMockOutputWriter(), 43 Options: options, 44 Progress: progressImpl, 45 ProjectFile: nil, 46 IssuesClient: nil, 47 Browser: nil, 48 Catalog: disk.NewCatalog(config.DefaultConfig.TemplatesDirectory), 49 RateLimiter: ratelimit.New(context.Background(), uint(options.RateLimit), time.Second), 50 } 51 workflowLoader, err := parsers.NewLoader(&executerOpts) 52 if err != nil { 53 log.Fatalf("Could not create workflow loader: %s\n", err) 54 } 55 executerOpts.WorkflowLoader = workflowLoader 56 57 } 58 59 func Test_ParseFromURL(t *testing.T) { 60 router := httprouter.New() 61 router.GET("/match-1.yaml", func(w netHttp.ResponseWriter, r *netHttp.Request, _ httprouter.Params) { 62 b, err := os.ReadFile("tests/match-1.yaml") 63 if err != nil { 64 w.Write([]byte(err.Error())) // nolint: errcheck 65 } 66 w.Write(b) // nolint: errcheck 67 }) 68 ts := httptest.NewServer(router) 69 defer ts.Close() 70 var expectedTemplate = &templates.Template{ 71 ID: "basic-get", 72 Info: model.Info{ 73 Name: "Basic GET Request", 74 Authors: stringslice.StringSlice{Value: []string{"pdteam"}}, 75 SeverityHolder: severity.Holder{Severity: severity.Info}, 76 }, 77 RequestsHTTP: []*http.Request{{ 78 Operators: operators.Operators{ 79 Matchers: []*matchers.Matcher{{ 80 Type: matchers.MatcherTypeHolder{ 81 MatcherType: matchers.WordsMatcher, 82 }, 83 Words: []string{"This is test matcher text"}, 84 }}, 85 }, 86 Path: []string{"{{BaseURL}}"}, 87 AttackType: generators.AttackTypeHolder{}, 88 Method: http.HTTPMethodTypeHolder{ 89 MethodType: http.HTTPGet, 90 }, 91 }}, 92 TotalRequests: 1, 93 Executer: nil, 94 Path: ts.URL + "/match-1.yaml", 95 } 96 setup() 97 got, err := templates.Parse(ts.URL+"/match-1.yaml", nil, executerOpts) 98 require.Nilf(t, err, "could not parse template (%s)", fmt.Sprint(err)) 99 require.Nil(t, err, "could not parse template") 100 require.Equal(t, expectedTemplate.ID, got.ID) 101 require.Equal(t, expectedTemplate.Info, got.Info) 102 require.Equal(t, expectedTemplate.TotalRequests, got.TotalRequests) 103 require.Equal(t, expectedTemplate.Path, got.Path) 104 require.Equal(t, expectedTemplate.RequestsHTTP[0].Path, got.RequestsHTTP[0].Path) 105 require.Equal(t, expectedTemplate.RequestsHTTP[0].Operators.Matchers[0].Words, got.RequestsHTTP[0].Operators.Matchers[0].Words) 106 require.Equal(t, len(expectedTemplate.RequestsHTTP), len(got.RequestsHTTP)) 107 } 108 109 func Test_ParseFromFile(t *testing.T) { 110 filePath := "tests/match-1.yaml" 111 expectedTemplate := &templates.Template{ 112 ID: "basic-get", 113 Info: model.Info{ 114 Name: "Basic GET Request", 115 Authors: stringslice.StringSlice{Value: []string{"pdteam"}}, 116 SeverityHolder: severity.Holder{Severity: severity.Info}, 117 }, 118 RequestsHTTP: []*http.Request{{ 119 Operators: operators.Operators{ 120 Matchers: []*matchers.Matcher{{ 121 Type: matchers.MatcherTypeHolder{ 122 MatcherType: matchers.WordsMatcher, 123 }, 124 Words: []string{"This is test matcher text"}, 125 }}, 126 }, 127 Path: []string{"{{BaseURL}}"}, 128 AttackType: generators.AttackTypeHolder{}, 129 Method: http.HTTPMethodTypeHolder{ 130 MethodType: http.HTTPGet, 131 }, 132 }}, 133 TotalRequests: 1, 134 Executer: nil, 135 Path: "tests/match-1.yaml", 136 } 137 setup() 138 got, err := templates.Parse(filePath, nil, executerOpts) 139 require.Nil(t, err, "could not parse template") 140 require.Equal(t, expectedTemplate.ID, got.ID) 141 require.Equal(t, expectedTemplate.Info, got.Info) 142 require.Equal(t, expectedTemplate.TotalRequests, got.TotalRequests) 143 require.Equal(t, expectedTemplate.Path, got.Path) 144 require.Equal(t, expectedTemplate.RequestsHTTP[0].Path, got.RequestsHTTP[0].Path) 145 require.Equal(t, expectedTemplate.RequestsHTTP[0].Operators.Matchers[0].Words, got.RequestsHTTP[0].Operators.Matchers[0].Words) 146 require.Equal(t, len(expectedTemplate.RequestsHTTP), len(got.RequestsHTTP)) 147 148 // Test cache 149 got, err = templates.Parse(filePath, nil, executerOpts) 150 require.Nil(t, err, "could not parse template") 151 require.Equal(t, expectedTemplate.ID, got.ID) 152 } 153 154 func Test_ParseWorkflow(t *testing.T) { 155 filePath := "tests/workflow.yaml" 156 expectedTemplate := &templates.Template{ 157 ID: "workflow-example", 158 Info: model.Info{ 159 Name: "Test Workflow Template", 160 Authors: stringslice.StringSlice{Value: []string{"pdteam"}}, 161 SeverityHolder: severity.Holder{Severity: severity.Info}, 162 }, 163 Workflow: workflows.Workflow{ 164 Workflows: []*workflows.WorkflowTemplate{{Template: "tests/match-1.yaml"}, {Template: "tests/match-1.yaml"}}, 165 Options: &protocols.ExecutorOptions{}, 166 }, 167 CompiledWorkflow: &workflows.Workflow{}, 168 SelfContained: false, 169 StopAtFirstMatch: false, 170 Signature: http.SignatureTypeHolder{}, 171 Variables: variables.Variable{}, 172 TotalRequests: 0, 173 Executer: nil, 174 Path: "tests/workflow.yaml", 175 } 176 setup() 177 got, err := templates.Parse(filePath, nil, executerOpts) 178 require.Nil(t, err, "could not parse template") 179 require.Equal(t, expectedTemplate.ID, got.ID) 180 require.Equal(t, expectedTemplate.Info, got.Info) 181 require.Equal(t, expectedTemplate.TotalRequests, got.TotalRequests) 182 require.Equal(t, expectedTemplate.Path, got.Path) 183 require.Equal(t, expectedTemplate.Workflow.Workflows[0].Template, got.Workflow.Workflows[0].Template) 184 require.Equal(t, len(expectedTemplate.Workflows), len(got.Workflows)) 185 } 186 187 func Test_WrongTemplate(t *testing.T) { 188 setup() 189 190 filePath := "tests/no-author.yaml" 191 got, err := templates.Parse(filePath, nil, executerOpts) 192 require.Nil(t, got, "could not parse template") 193 require.ErrorContains(t, err, "no template author field provided") 194 195 filePath = "tests/no-req.yaml" 196 got, err = templates.Parse(filePath, nil, executerOpts) 197 require.Nil(t, got, "could not parse template") 198 require.ErrorContains(t, err, "no requests defined ") 199 }