Path: blob/dev/pkg/protocols/offlinehttp/operators_test.go
2070 views
package offlinehttp12import (3"net/http"4"testing"5"time"67"github.com/stretchr/testify/require"89"github.com/projectdiscovery/nuclei/v3/pkg/model"10"github.com/projectdiscovery/nuclei/v3/pkg/model/types/severity"11"github.com/projectdiscovery/nuclei/v3/pkg/operators"12"github.com/projectdiscovery/nuclei/v3/pkg/operators/extractors"13"github.com/projectdiscovery/nuclei/v3/pkg/operators/matchers"14"github.com/projectdiscovery/nuclei/v3/pkg/output"15"github.com/projectdiscovery/nuclei/v3/pkg/testutils"16)1718func TestResponseToDSLMap(t *testing.T) {19options := testutils.DefaultOptions2021testutils.Init(options)22templateID := "testing-http"23request := &Request{}24executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{25ID: templateID,26Info: model.Info{SeverityHolder: severity.Holder{Severity: severity.Low}, Name: "test"},27})28executerOpts.Operators = []*operators.Operators{{}}29err := request.Compile(executerOpts)30require.Nil(t, err, "could not compile file request")3132resp := &http.Response{}33resp.Header = make(http.Header)34resp.Header.Set("Test", "Test-Response")35host := "http://example.com/test/"36matched := "http://example.com/test/?test=1"3738event := request.responseToDSLMap(resp, host, matched, exampleRawRequest, exampleRawResponse, exampleResponseBody, exampleResponseHeader, 1*time.Second, map[string]interface{}{})39require.Len(t, event, 14, "could not get correct number of items in dsl map")40require.Equal(t, exampleRawResponse, event["response"], "could not get correct resp")41require.Equal(t, "Test-Response", event["test"], "could not get correct resp for header")42}4344func TestHTTPOperatorMatch(t *testing.T) {45options := testutils.DefaultOptions4647testutils.Init(options)48templateID := "testing-http"49request := &Request{}50executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{51ID: templateID,52Info: model.Info{SeverityHolder: severity.Holder{Severity: severity.Low}, Name: "test"},53})54executerOpts.Operators = []*operators.Operators{{}}55err := request.Compile(executerOpts)56require.Nil(t, err, "could not compile file request")5758resp := &http.Response{}59resp.Header = make(http.Header)60resp.Header.Set("Test", "Test-Response")61host := "http://example.com/test/"62matched := "http://example.com/test/?test=1"6364event := request.responseToDSLMap(resp, host, matched, exampleRawRequest, exampleRawResponse, exampleResponseBody, exampleResponseHeader, 1*time.Second, map[string]interface{}{})65require.Len(t, event, 14, "could not get correct number of items in dsl map")66require.Equal(t, exampleRawResponse, event["response"], "could not get correct resp")67require.Equal(t, "Test-Response", event["test"], "could not get correct resp for header")6869t.Run("valid", func(t *testing.T) {70matcher := &matchers.Matcher{71Part: "body",72Type: matchers.MatcherTypeHolder{MatcherType: matchers.WordsMatcher},73Words: []string{"1.1.1.1"},74}75err = matcher.CompileMatchers()76require.Nil(t, err, "could not compile matcher")7778isMatched, matched := request.Match(event, matcher)79require.True(t, isMatched, "could not match valid response")80require.Equal(t, matcher.Words, matched)81})8283t.Run("negative", func(t *testing.T) {84matcher := &matchers.Matcher{85Part: "body",86Type: matchers.MatcherTypeHolder{MatcherType: matchers.WordsMatcher},87Negative: true,88Words: []string{"random"},89}90err := matcher.CompileMatchers()91require.Nil(t, err, "could not compile negative matcher")9293isMatched, matched := request.Match(event, matcher)94require.True(t, isMatched, "could not match valid negative response matcher")95require.Equal(t, []string{}, matched)96})9798t.Run("invalid", func(t *testing.T) {99matcher := &matchers.Matcher{100Part: "body",101Type: matchers.MatcherTypeHolder{MatcherType: matchers.WordsMatcher},102Words: []string{"random"},103}104err := matcher.CompileMatchers()105require.Nil(t, err, "could not compile matcher")106107isMatched, matched := request.Match(event, matcher)108require.False(t, isMatched, "could match invalid response matcher")109require.Equal(t, []string{}, matched)110})111}112113func TestHTTPOperatorExtract(t *testing.T) {114options := testutils.DefaultOptions115116testutils.Init(options)117templateID := "testing-http"118request := &Request{}119executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{120ID: templateID,121Info: model.Info{SeverityHolder: severity.Holder{Severity: severity.Low}, Name: "test"},122})123executerOpts.Operators = []*operators.Operators{{}}124err := request.Compile(executerOpts)125require.Nil(t, err, "could not compile file request")126127resp := &http.Response{}128resp.Header = make(http.Header)129resp.Header.Set("Test-Header", "Test-Response")130host := "http://example.com/test/"131matched := "http://example.com/test/?test=1"132133event := request.responseToDSLMap(resp, host, matched, exampleRawRequest, exampleRawResponse, exampleResponseBody, exampleResponseHeader, 1*time.Second, map[string]interface{}{})134require.Len(t, event, 14, "could not get correct number of items in dsl map")135require.Equal(t, exampleRawResponse, event["response"], "could not get correct resp")136require.Equal(t, "Test-Response", event["test_header"], "could not get correct resp for header")137138t.Run("extract", func(t *testing.T) {139extractor := &extractors.Extractor{140Part: "body",141Type: extractors.ExtractorTypeHolder{ExtractorType: extractors.RegexExtractor},142Regex: []string{"[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+"},143}144err = extractor.CompileExtractors()145require.Nil(t, err, "could not compile extractor")146147data := request.Extract(event, extractor)148require.Greater(t, len(data), 0, "could not extractor valid response")149require.Equal(t, map[string]struct{}{"1.1.1.1": {}}, data, "could not extract correct data")150})151152t.Run("kval", func(t *testing.T) {153extractor := &extractors.Extractor{154Type: extractors.ExtractorTypeHolder{ExtractorType: extractors.KValExtractor},155KVal: []string{"test_header"},156Part: "header",157}158err = extractor.CompileExtractors()159require.Nil(t, err, "could not compile kval extractor")160161data := request.Extract(event, extractor)162require.Greater(t, len(data), 0, "could not extractor kval valid response")163require.Equal(t, map[string]struct{}{"Test-Response": {}}, data, "could not extract correct kval data")164})165}166167func TestHTTPMakeResult(t *testing.T) {168options := testutils.DefaultOptions169170testutils.Init(options)171templateID := "testing-http"172request := &Request{}173executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{174ID: templateID,175Info: model.Info{SeverityHolder: severity.Holder{Severity: severity.Low}, Name: "test"},176})177executerOpts.Operators = []*operators.Operators{{178Matchers: []*matchers.Matcher{{179Name: "test",180Part: "body",181Type: matchers.MatcherTypeHolder{MatcherType: matchers.WordsMatcher},182Words: []string{"1.1.1.1"},183}},184Extractors: []*extractors.Extractor{{185Part: "body",186Type: extractors.ExtractorTypeHolder{ExtractorType: extractors.RegexExtractor},187Regex: []string{"[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+"},188}},189}}190err := request.Compile(executerOpts)191require.Nil(t, err, "could not compile file request")192193resp := &http.Response{}194resp.Header = make(http.Header)195resp.Header.Set("Test", "Test-Response")196host := "http://example.com/test/"197matched := "http://example.com/test/?test=1"198199event := request.responseToDSLMap(resp, host, matched, exampleRawRequest, exampleRawResponse, exampleResponseBody, exampleResponseHeader, 1*time.Second, map[string]interface{}{})200require.Len(t, event, 14, "could not get correct number of items in dsl map")201require.Equal(t, exampleRawResponse, event["response"], "could not get correct resp")202require.Equal(t, "Test-Response", event["test"], "could not get correct resp for header")203204event["ip"] = "192.169.1.1"205finalEvent := &output.InternalWrappedEvent{InternalEvent: event}206for _, operator := range request.compiledOperators {207result, ok := operator.Execute(event, request.Match, request.Extract, false)208if ok && result != nil {209finalEvent.OperatorsResult = result210finalEvent.Results = request.MakeResultEvent(finalEvent)211}212}213require.Equal(t, 1, len(finalEvent.Results), "could not get correct number of results")214require.Equal(t, "test", finalEvent.Results[0].MatcherName, "could not get correct matcher name of results")215require.Equal(t, "1.1.1.1", finalEvent.Results[0].ExtractedResults[0], "could not get correct extracted results")216}217218const exampleRawRequest = `GET / HTTP/1.1219Host: example.com220Upgrade-Insecure-Requests: 1221User-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.36222Accept: 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.9223Accept-Encoding: gzip, deflate224Accept-Language: en-US,en;q=0.9,hi;q=0.8225If-None-Match: "3147526947+gzip"226If-Modified-Since: Thu, 17 Oct 2019 07:18:26 GMT227Connection: close228229`230231const exampleRawResponse = exampleResponseHeader + exampleResponseBody232const exampleResponseHeader = `233HTTP/1.1 200 OK234Accept-Ranges: bytes235Age: 493322236Cache-Control: max-age=604800237Content-Type: text/html; charset=UTF-8238Date: Thu, 04 Feb 2021 12:15:51 GMT239Etag: "3147526947+ident"240Expires: Thu, 11 Feb 2021 12:15:51 GMT241Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT242Server: ECS (nyb/1D1C)243Vary: Accept-Encoding244X-Cache: HIT245Content-Length: 1256246Connection: close247`248249const exampleResponseBody = `250<!doctype html>251<html>252<head>253<title>Example Domain</title>254255<meta charset="utf-8" />256<meta http-equiv="Content-type" content="text/html; charset=utf-8" />257<meta name="viewport" content="width=device-width, initial-scale=1" />258<style type="text/css">259body {260background-color: #f0f0f2;261margin: 0;262padding: 0;263font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;264265}266div {267width: 600px;268margin: 5em auto;269padding: 2em;270background-color: #fdfdff;271border-radius: 0.5em;272box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02);273}274a:link, a:visited {275color: #38488f;276text-decoration: none;277}278@media (max-width: 700px) {279div {280margin: 0 auto;281width: auto;282}283}284</style>285</head>286<a>1.1.1.1</a>287<body>288<div>289<h1>Example Domain</h1>290<p>This domain is for use in illustrative examples in documents. You may use this291domain in literature without prior coordination or asking for permission.</p>292<p><a href="https://www.iana.org/domains/example">More information...</a></p>293</div>294</body>295</html>296`297298299