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 = ` Example Domain 1.1.1.1

Example Domain

This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.

More information...

`