package http import ( "net/http" "testing" "time" "github.com/projectdiscovery/nuclei/v2/internal/testutils" "github.com/projectdiscovery/nuclei/v2/pkg/operators" "github.com/projectdiscovery/nuclei/v2/pkg/operators/extractors" "github.com/projectdiscovery/nuclei/v2/pkg/operators/matchers" "github.com/projectdiscovery/nuclei/v2/pkg/output" "github.com/stretchr/testify/require" ) func TestResponseToDSLMap(t *testing.T) { options := testutils.DefaultOptions testutils.Init(options) templateID := "testing-http" request := &Request{ ID: templateID, Name: "testing", Path: []string{"{{BaseURL}}?test=1"}, Method: "GET", } executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{ ID: templateID, Info: map[string]interface{}{"severity": "low", "name": "test"}, }) err := request.Compile(executerOpts) require.Nil(t, err, "could not compile file request") resp := &http.Response{} resp.Header = make(http.Header) resp.Header.Set("Test", "Test-Response") host := "http://example.com/test/" matched := "http://example.com/test/?test=1" event := request.responseToDSLMap(resp, host, matched, exampleRawRequest, exampleRawResponse, exampleResponseBody, exampleResponseHeader, 1*time.Second, map[string]interface{}{}) require.Len(t, event, 12, "could not get correct number of items in dsl map") require.Equal(t, exampleRawResponse, event["response"], "could not get correct resp") require.Equal(t, "Test-Response", event["test"], "could not get correct resp for header") } func TestHTTPOperatorMatch(t *testing.T) { options := testutils.DefaultOptions testutils.Init(options) templateID := "testing-http" request := &Request{ ID: templateID, Name: "testing", Path: []string{"{{BaseURL}}?test=1"}, Method: "GET", } executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{ ID: templateID, Info: map[string]interface{}{"severity": "low", "name": "test"}, }) err := request.Compile(executerOpts) require.Nil(t, err, "could not compile file request") resp := &http.Response{} resp.Header = make(http.Header) resp.Header.Set("Test", "Test-Response") host := "http://example.com/test/" matched := "http://example.com/test/?test=1" event := request.responseToDSLMap(resp, host, matched, exampleRawRequest, exampleRawResponse, exampleResponseBody, exampleResponseHeader, 1*time.Second, map[string]interface{}{}) require.Len(t, event, 12, "could not get correct number of items in dsl map") require.Equal(t, exampleRawResponse, event["response"], "could not get correct resp") require.Equal(t, "Test-Response", event["test"], "could not get correct resp for header") t.Run("valid", func(t *testing.T) { matcher := &matchers.Matcher{ Part: "body", Type: "word", Words: []string{"1.1.1.1"}, } err = matcher.CompileMatchers() require.Nil(t, err, "could not compile matcher") matched := request.Match(event, matcher) require.True(t, matched, "could not match valid response") }) t.Run("negative", func(t *testing.T) { matcher := &matchers.Matcher{ Part: "body", Type: "word", Negative: true, Words: []string{"random"}, } err := matcher.CompileMatchers() require.Nil(t, err, "could not compile negative matcher") matched := request.Match(event, matcher) require.True(t, matched, "could not match valid negative response matcher") }) t.Run("invalid", func(t *testing.T) { matcher := &matchers.Matcher{ Part: "body", Type: "word", Words: []string{"random"}, } err := matcher.CompileMatchers() require.Nil(t, err, "could not compile matcher") matched := request.Match(event, matcher) require.False(t, matched, "could match invalid response matcher") }) } func TestHTTPOperatorExtract(t *testing.T) { options := testutils.DefaultOptions testutils.Init(options) templateID := "testing-http" request := &Request{ ID: templateID, Name: "testing", Path: []string{"{{BaseURL}}?test=1"}, Method: "GET", } executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{ ID: templateID, Info: map[string]interface{}{"severity": "low", "name": "test"}, }) err := request.Compile(executerOpts) require.Nil(t, err, "could not compile file request") resp := &http.Response{} resp.Header = make(http.Header) resp.Header.Set("Test-Header", "Test-Response") host := "http://example.com/test/" matched := "http://example.com/test/?test=1" event := request.responseToDSLMap(resp, host, matched, exampleRawRequest, exampleRawResponse, exampleResponseBody, exampleResponseHeader, 1*time.Second, map[string]interface{}{}) require.Len(t, event, 12, "could not get correct number of items in dsl map") require.Equal(t, exampleRawResponse, event["response"], "could not get correct resp") require.Equal(t, "Test-Response", event["test-header"], "could not get correct resp for header") t.Run("extract", func(t *testing.T) { extractor := &extractors.Extractor{ Part: "body", Type: "regex", Regex: []string{"[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+"}, } err = extractor.CompileExtractors() require.Nil(t, err, "could not compile extractor") data := request.Extract(event, extractor) require.Greater(t, len(data), 0, "could not extractor valid response") require.Equal(t, map[string]struct{}{"1.1.1.1": {}}, data, "could not extract correct data") }) t.Run("kval", func(t *testing.T) { extractor := &extractors.Extractor{ Type: "kval", KVal: []string{"test-header"}, } err = extractor.CompileExtractors() require.Nil(t, err, "could not compile kval extractor") data := request.Extract(event, extractor) require.Greater(t, len(data), 0, "could not extractor kval valid response") require.Equal(t, map[string]struct{}{"Test-Response": {}}, data, "could not extract correct kval data") }) } func TestHTTPMakeResult(t *testing.T) { options := testutils.DefaultOptions testutils.Init(options) templateID := "testing-http" request := &Request{ ID: templateID, Name: "testing", Path: []string{"{{BaseURL}}?test=1"}, Method: "GET", Operators: operators.Operators{ Matchers: []*matchers.Matcher{{ Name: "test", Part: "body", Type: "word", Words: []string{"1.1.1.1"}, }}, Extractors: []*extractors.Extractor{{ Part: "body", Type: "regex", Regex: []string{"[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+"}, }}, }, } executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{ ID: templateID, Info: map[string]interface{}{"severity": "low", "name": "test"}, }) err := request.Compile(executerOpts) require.Nil(t, err, "could not compile file request") resp := &http.Response{} resp.Header = make(http.Header) resp.Header.Set("Test", "Test-Response") host := "http://example.com/test/" matched := "http://example.com/test/?test=1" event := request.responseToDSLMap(resp, host, matched, exampleRawRequest, exampleRawResponse, exampleResponseBody, exampleResponseHeader, 1*time.Second, map[string]interface{}{}) require.Len(t, event, 12, "could not get correct number of items in dsl map") require.Equal(t, exampleRawResponse, event["response"], "could not get correct resp") require.Equal(t, "Test-Response", event["test"], "could not get correct resp for header") event["ip"] = "192.169.1.1" finalEvent := &output.InternalWrappedEvent{InternalEvent: event} if request.CompiledOperators != nil { result, ok := request.CompiledOperators.Execute(event, request.Match, request.Extract) if ok && result != nil { finalEvent.OperatorsResult = result finalEvent.Results = request.MakeResultEvent(finalEvent) } } require.Equal(t, 1, len(finalEvent.Results), "could not get correct number of results") require.Equal(t, "test", finalEvent.Results[0].MatcherName, "could not get correct matcher name of results") require.Equal(t, "1.1.1.1", finalEvent.Results[0].ExtractedResults[0], "could not get correct extracted results") } const exampleRawRequest = `GET / HTTP/1.1 Host: example.com Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Accept-Encoding: gzip, deflate Accept-Language: en-US,en;q=0.9,hi;q=0.8 If-None-Match: "3147526947+gzip" If-Modified-Since: Thu, 17 Oct 2019 07:18:26 GMT Connection: close ` const exampleRawResponse = exampleResponseHeader + exampleResponseBody const exampleResponseHeader = ` HTTP/1.1 200 OK Accept-Ranges: bytes Age: 493322 Cache-Control: max-age=604800 Content-Type: text/html; charset=UTF-8 Date: Thu, 04 Feb 2021 12:15:51 GMT Etag: "3147526947+ident" Expires: Thu, 11 Feb 2021 12:15:51 GMT Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT Server: ECS (nyb/1D1C) Vary: Accept-Encoding X-Cache: HIT Content-Length: 1256 Connection: close ` const exampleResponseBody = `
This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.