mirror of https://github.com/daffainfo/nuclei.git
feat: Improve DSL function UX #1295
Added support for letting people know if: * the DSL expression does not return a boolean value * an invalid custom function signature was provided and then display all available function signatures * an invalid function was provided and then display the correct signature Unified the DSL function names to use snake case. The old signatures are also kept for backward compatibility.dev
parent
c61ec5f673
commit
79aed22d46
|
@ -20,10 +20,12 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/Knetic/govaluate"
|
||||
"github.com/logrusorgru/aurora"
|
||||
"github.com/spaolacci/murmur3"
|
||||
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/helpers/deserialization"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
||||
"github.com/spaolacci/murmur3"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -32,7 +34,7 @@ const (
|
|||
)
|
||||
|
||||
var invalidDslFunctionError = errors.New("invalid DSL function signature")
|
||||
var invalidDslFunctionMessageTemplate = "correct method signature '%s'. %w"
|
||||
var invalidDslFunctionMessageTemplate = "%w. correct method signature %q"
|
||||
|
||||
var dslFunctions map[string]dslFunction
|
||||
|
||||
|
@ -47,10 +49,10 @@ func init() {
|
|||
length := len(types.ToString(args[0]))
|
||||
return float64(length), nil
|
||||
}),
|
||||
"toupper": makeDslFunction(1, func(args ...interface{}) (interface{}, error) {
|
||||
"to_upper": makeDslFunction(1, func(args ...interface{}) (interface{}, error) {
|
||||
return strings.ToUpper(types.ToString(args[0])), nil
|
||||
}),
|
||||
"tolower": makeDslFunction(1, func(args ...interface{}) (interface{}, error) {
|
||||
"to_lower": makeDslFunction(1, func(args ...interface{}) (interface{}, error) {
|
||||
return strings.ToLower(types.ToString(args[0])), nil
|
||||
}),
|
||||
"replace": makeDslFunction(3, func(args ...interface{}) (interface{}, error) {
|
||||
|
@ -66,19 +68,19 @@ func init() {
|
|||
"trim": makeDslFunction(2, func(args ...interface{}) (interface{}, error) {
|
||||
return strings.Trim(types.ToString(args[0]), types.ToString(args[1])), nil
|
||||
}),
|
||||
"trimleft": makeDslFunction(2, func(args ...interface{}) (interface{}, error) {
|
||||
"trim_left": makeDslFunction(2, func(args ...interface{}) (interface{}, error) {
|
||||
return strings.TrimLeft(types.ToString(args[0]), types.ToString(args[1])), nil
|
||||
}),
|
||||
"trimright": makeDslFunction(2, func(args ...interface{}) (interface{}, error) {
|
||||
"trim_right": makeDslFunction(2, func(args ...interface{}) (interface{}, error) {
|
||||
return strings.TrimRight(types.ToString(args[0]), types.ToString(args[1])), nil
|
||||
}),
|
||||
"trimspace": makeDslFunction(1, func(args ...interface{}) (interface{}, error) {
|
||||
"trim_space": makeDslFunction(1, func(args ...interface{}) (interface{}, error) {
|
||||
return strings.TrimSpace(types.ToString(args[0])), nil
|
||||
}),
|
||||
"trimprefix": makeDslFunction(2, func(args ...interface{}) (interface{}, error) {
|
||||
"trim_prefix": makeDslFunction(2, func(args ...interface{}) (interface{}, error) {
|
||||
return strings.TrimPrefix(types.ToString(args[0]), types.ToString(args[1])), nil
|
||||
}),
|
||||
"trimsuffix": makeDslFunction(2, func(args ...interface{}) (interface{}, error) {
|
||||
"trim_suffix": makeDslFunction(2, func(args ...interface{}) (interface{}, error) {
|
||||
return strings.TrimSuffix(types.ToString(args[0]), types.ToString(args[1])), nil
|
||||
}),
|
||||
"reverse": makeDslFunction(1, func(args ...interface{}) (interface{}, error) {
|
||||
|
@ -98,6 +100,7 @@ func init() {
|
|||
return buffer.String(), nil
|
||||
}),
|
||||
"base64_py": makeDslFunction(1, func(args ...interface{}) (interface{}, error) {
|
||||
// python encodes to base64 with lines of 76 bytes terminated by new line "\n"
|
||||
stdBase64 := base64.StdEncoding.EncodeToString([]byte(types.ToString(args[0])))
|
||||
return deserialization.InsertInto(stdBase64, 76, '\n'), nil
|
||||
}),
|
||||
|
@ -154,33 +157,32 @@ func init() {
|
|||
}
|
||||
return compiled.MatchString(types.ToString(args[1])), nil
|
||||
}),
|
||||
"remove_bad_chars": makeDslFunction(2, func(args ...interface{}) (interface{}, error) {
|
||||
input := types.ToString(args[0])
|
||||
badChars := types.ToString(args[1])
|
||||
return trimAll(input, badChars), nil
|
||||
}),
|
||||
"rand_char": makeDslWithOptionalArgsFunction(
|
||||
"(optionalCharSet, optionalBachChars) string",
|
||||
"(optionalCharSet string) string",
|
||||
func(args ...interface{}) (interface{}, error) {
|
||||
charSet := letters + numbers
|
||||
badChars := ""
|
||||
|
||||
argSize := len(args)
|
||||
if argSize != 1 && argSize != 2 {
|
||||
if argSize != 0 && argSize != 1 {
|
||||
return nil, invalidDslFunctionError
|
||||
}
|
||||
|
||||
if argSize >= 1 {
|
||||
charSet = types.ToString(args[0])
|
||||
}
|
||||
if argSize == 2 {
|
||||
badChars = types.ToString(args[1])
|
||||
}
|
||||
|
||||
charSet = trimAll(charSet, badChars)
|
||||
return charSet[rand.Intn(len(charSet))], nil
|
||||
},
|
||||
),
|
||||
"rand_base": makeDslWithOptionalArgsFunction(
|
||||
"(length, optionalCharSet, optionalBadChars) string",
|
||||
"(length uint, optionalCharSet string) string",
|
||||
func(args ...interface{}) (interface{}, error) {
|
||||
var length int
|
||||
badChars := ""
|
||||
charSet := letters + numbers
|
||||
|
||||
argSize := len(args)
|
||||
|
@ -190,18 +192,14 @@ func init() {
|
|||
|
||||
length = int(args[0].(float64))
|
||||
|
||||
if argSize >= 2 {
|
||||
badChars = types.ToString(args[1])
|
||||
if argSize == 2 {
|
||||
charSet = types.ToString(args[1])
|
||||
}
|
||||
if argSize == 3 {
|
||||
charSet = types.ToString(args[2])
|
||||
}
|
||||
charSet = trimAll(charSet, badChars)
|
||||
return randSeq(charSet, length), nil
|
||||
},
|
||||
),
|
||||
"rand_text_alphanumeric": makeDslWithOptionalArgsFunction(
|
||||
"(length, optionalBadChars) string",
|
||||
"(length uint, optionalBadChars string) string",
|
||||
func(args ...interface{}) (interface{}, error) {
|
||||
length := 0
|
||||
badChars := ""
|
||||
|
@ -221,7 +219,7 @@ func init() {
|
|||
},
|
||||
),
|
||||
"rand_text_alpha": makeDslWithOptionalArgsFunction(
|
||||
"(length, optionalBadChars) string",
|
||||
"(length uint, optionalBadChars string) string",
|
||||
func(args ...interface{}) (interface{}, error) {
|
||||
var length int
|
||||
badChars := ""
|
||||
|
@ -241,7 +239,7 @@ func init() {
|
|||
},
|
||||
),
|
||||
"rand_text_numeric": makeDslWithOptionalArgsFunction(
|
||||
"(size int, optionalBadNumbers string) string",
|
||||
"(length uint, optionalBadNumbers string) string",
|
||||
func(args ...interface{}) (interface{}, error) {
|
||||
argSize := len(args)
|
||||
if argSize != 1 && argSize != 2 {
|
||||
|
@ -249,7 +247,7 @@ func init() {
|
|||
}
|
||||
|
||||
length := args[0].(int)
|
||||
var badNumbers = ""
|
||||
badNumbers := ""
|
||||
|
||||
if argSize == 2 {
|
||||
badNumbers = types.ToString(args[1])
|
||||
|
@ -260,7 +258,7 @@ func init() {
|
|||
},
|
||||
),
|
||||
"rand_int": makeDslWithOptionalArgsFunction(
|
||||
"(optionalMin, optionalMax int) int",
|
||||
"(optionalMin, optionalMax uint) int",
|
||||
func(args ...interface{}) (interface{}, error) {
|
||||
argSize := len(args)
|
||||
if argSize >= 2 {
|
||||
|
@ -286,7 +284,7 @@ func init() {
|
|||
data := deserialization.GenerateJavaGadget(gadget, cmd, encoding)
|
||||
return data, nil
|
||||
}),
|
||||
"unixtime": makeDslWithOptionalArgsFunction(
|
||||
"unix_time": makeDslWithOptionalArgsFunction(
|
||||
"(optionalSeconds uint) float64",
|
||||
func(args ...interface{}) (interface{}, error) {
|
||||
seconds := 0
|
||||
|
@ -302,7 +300,7 @@ func init() {
|
|||
return float64(offset.Unix()), nil
|
||||
},
|
||||
),
|
||||
"waitfor": makeDslWithOptionalArgsFunction(
|
||||
"wait_for": makeDslWithOptionalArgsFunction(
|
||||
"(seconds uint)",
|
||||
func(args ...interface{}) (interface{}, error) {
|
||||
if len(args) != 1 {
|
||||
|
@ -323,15 +321,6 @@ func init() {
|
|||
return true, nil
|
||||
},
|
||||
),
|
||||
"time_now": makeDslWithOptionalArgsFunction(
|
||||
"() float64",
|
||||
func(args ...interface{}) (interface{}, error) {
|
||||
if len(args) == 0 {
|
||||
return nil, invalidDslFunctionError
|
||||
}
|
||||
return float64(time.Now().Unix()), nil
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
dslFunctions = make(map[string]dslFunction, len(tempDslFunctions))
|
||||
|
@ -364,7 +353,7 @@ func makeDslFunction(numberOfParameters int, dslFunctionLogic govaluate.Expressi
|
|||
signature,
|
||||
func(args ...interface{}) (interface{}, error) {
|
||||
if len(args) != numberOfParameters {
|
||||
return nil, fmt.Errorf(invalidDslFunctionMessageTemplate, signature, invalidDslFunctionError)
|
||||
return nil, fmt.Errorf(invalidDslFunctionMessageTemplate, invalidDslFunctionError, signature)
|
||||
}
|
||||
return dslFunctionLogic(args...)
|
||||
},
|
||||
|
@ -378,12 +367,42 @@ func HelperFunctions() map[string]govaluate.ExpressionFunction {
|
|||
|
||||
for functionName, dslFunction := range dslFunctions {
|
||||
helperFunctions[functionName] = dslFunction.expressFunc
|
||||
helperFunctions[strings.ReplaceAll(functionName, "_", "")] = dslFunction.expressFunc // for backwards compatibility
|
||||
}
|
||||
|
||||
return helperFunctions
|
||||
}
|
||||
|
||||
func GetDslFunctionSignatures() []string {
|
||||
// AddHelperFunction allows creation of additional helper functions to be supported with templates
|
||||
//goland:noinspection GoUnusedExportedFunction
|
||||
func AddHelperFunction(key string, value func(args ...interface{}) (interface{}, error)) error {
|
||||
if _, ok := dslFunctions[key]; !ok {
|
||||
dslFunction := dslFunctions[key]
|
||||
dslFunction.signature = "(args ...interface{}) interface{}"
|
||||
dslFunction.expressFunc = value
|
||||
return nil
|
||||
}
|
||||
return errors.New("duplicate helper function key defined")
|
||||
}
|
||||
|
||||
func GetPrintableDslFunctionSignatures(noColor bool) string {
|
||||
aggregateSignatures := func(values []string) string {
|
||||
builder := &strings.Builder{}
|
||||
for _, value := range values {
|
||||
builder.WriteRune('\t')
|
||||
builder.WriteString(value)
|
||||
builder.WriteRune('\n')
|
||||
}
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
if noColor {
|
||||
return aggregateSignatures(getDslFunctionSignatures())
|
||||
}
|
||||
return aggregateSignatures(colorizeDslFunctionSignatures())
|
||||
}
|
||||
|
||||
func getDslFunctionSignatures() []string {
|
||||
result := make([]string, 0, len(dslFunctions))
|
||||
|
||||
for _, dslFunction := range dslFunctions {
|
||||
|
@ -393,15 +412,49 @@ func GetDslFunctionSignatures() []string {
|
|||
return result
|
||||
}
|
||||
|
||||
// AddHelperFunction allows creation of additional helper functions to be supported with templates
|
||||
func AddHelperFunction(key string, value func(args ...interface{}) (interface{}, error)) error {
|
||||
if _, ok := dslFunctions[key]; !ok {
|
||||
dslFunction := dslFunctions[key]
|
||||
dslFunction.signature = "(args ...interface{}) interface{}"
|
||||
dslFunction.expressFunc = value
|
||||
return nil
|
||||
var functionSignaturePattern = regexp.MustCompile(`(\w+)\s*\((?:([\w\d,\s]+)\s+([.\w\d{}&*]+))?\)([\s.\w\d{}&*]+)?`)
|
||||
|
||||
func colorizeDslFunctionSignatures() []string {
|
||||
signatures := getDslFunctionSignatures()
|
||||
|
||||
colorToOrange := func(value string) string {
|
||||
return aurora.Index(208, value).String()
|
||||
}
|
||||
return errors.New("duplicate helper function key defined")
|
||||
|
||||
result := make([]string, 0, len(signatures))
|
||||
|
||||
for _, signature := range signatures {
|
||||
subMatchSlices := functionSignaturePattern.FindAllStringSubmatch(signature, -1)
|
||||
if len(subMatchSlices) != 1 {
|
||||
// TODO log when #1166 is implemented
|
||||
return signatures
|
||||
}
|
||||
matches := subMatchSlices[0]
|
||||
if len(matches) != 5 {
|
||||
// TODO log when #1166 is implemented
|
||||
return signatures
|
||||
}
|
||||
|
||||
functionParameters := strings.Split(matches[2], ",")
|
||||
|
||||
var coloredParameterAndTypes []string
|
||||
for _, functionParameter := range functionParameters {
|
||||
functionParameter = strings.TrimSpace(functionParameter)
|
||||
paramAndType := strings.Split(functionParameter, " ")
|
||||
if len(paramAndType) == 1 {
|
||||
coloredParameterAndTypes = append(coloredParameterAndTypes, paramAndType[0])
|
||||
} else if len(paramAndType) == 2 {
|
||||
coloredParameterAndTypes = append(coloredParameterAndTypes, fmt.Sprintf("%s %s", paramAndType[0], colorToOrange(paramAndType[1])))
|
||||
}
|
||||
}
|
||||
|
||||
highlightedParams := strings.TrimSpace(fmt.Sprintf("%s %s", strings.Join(coloredParameterAndTypes, ", "), colorToOrange(matches[3])))
|
||||
colorizedDslSignature := fmt.Sprintf("%s(%s)%s", aurora.BrightYellow(matches[1]).String(), highlightedParams, colorToOrange(matches[4]))
|
||||
|
||||
result = append(result, colorizedDslSignature)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func reverseString(s string) string {
|
||||
|
|
|
@ -2,14 +2,17 @@ package dsl
|
|||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/Knetic/govaluate"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
||||
)
|
||||
|
||||
func TestDSLURLEncodeDecode(t *testing.T) {
|
||||
|
@ -45,3 +48,91 @@ func TestDSLGzipSerialize(t *testing.T) {
|
|||
|
||||
require.Equal(t, "hello world", string(data), "could not get gzip encoded data")
|
||||
}
|
||||
|
||||
func Test1(t *testing.T) {
|
||||
type testCase struct {
|
||||
methodName string
|
||||
arguments []interface{}
|
||||
expected interface{}
|
||||
err string
|
||||
}
|
||||
|
||||
toUpperSignatureError := createSignatureError("to_upper(arg1 interface{}) interface{}")
|
||||
removeBadCharsSignatureError := createSignatureError("remove_bad_chars(arg1, arg2 interface{}) interface{}")
|
||||
|
||||
testCases := []testCase{
|
||||
{"to_upper", []interface{}{}, nil, toUpperSignatureError},
|
||||
{"to_upper", []interface{}{"a"}, "A", ""},
|
||||
{"toupper", []interface{}{"a"}, "A", ""},
|
||||
{"to_upper", []interface{}{"a", "b", "c"}, nil, toUpperSignatureError},
|
||||
|
||||
{"remove_bad_chars", []interface{}{}, nil, removeBadCharsSignatureError},
|
||||
{"remove_bad_chars", []interface{}{"a"}, nil, removeBadCharsSignatureError},
|
||||
{"remove_bad_chars", []interface{}{"abba baab", "b"}, "aa aa", ""},
|
||||
{"remove_bad_chars", []interface{}{"a", "b", "c"}, nil, removeBadCharsSignatureError},
|
||||
}
|
||||
|
||||
helperFunctions := HelperFunctions()
|
||||
for _, currentTestCase := range testCases {
|
||||
methodName := currentTestCase.methodName
|
||||
t.Run(methodName, func(t *testing.T) {
|
||||
actualResult, err := helperFunctions[methodName](currentTestCase.arguments...)
|
||||
|
||||
if currentTestCase.err == "" {
|
||||
assert.Nil(t, err)
|
||||
} else {
|
||||
assert.Equal(t, err.Error(), currentTestCase.err)
|
||||
}
|
||||
assert.Equal(t, currentTestCase.expected, actualResult)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func createSignatureError(signature string) string {
|
||||
return fmt.Errorf(invalidDslFunctionMessageTemplate, invalidDslFunctionError, signature).Error()
|
||||
}
|
||||
|
||||
func Test(t *testing.T) {
|
||||
expectedColorizedSignatures := []string{
|
||||
"\x1b[93mbase64_py\x1b[0m(arg1 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93mprint_debug\x1b[0m(args \x1b[38;5;208m...interface{}\x1b[0m)\x1b[38;5;208m\x1b[0m",
|
||||
"\x1b[93mregex\x1b[0m(arg1, arg2 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93mmmh3\x1b[0m(arg1 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93mto_lower\x1b[0m(arg1 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93mmd5\x1b[0m(arg1 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93mreplace_regex\x1b[0m(arg1, arg2, arg3 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93mhtml_unescape\x1b[0m(arg1 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93mhex_encode\x1b[0m(arg1 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93mrand_base\x1b[0m(length \x1b[38;5;208muint\x1b[0m, optionalCharSet \x1b[38;5;208mstring\x1b[0m)\x1b[38;5;208m string\x1b[0m",
|
||||
"\x1b[93msha1\x1b[0m(arg1 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93mtrim_right\x1b[0m(arg1, arg2 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93mwait_for\x1b[0m(seconds \x1b[38;5;208muint\x1b[0m)\x1b[38;5;208m\x1b[0m",
|
||||
"\x1b[93mtrim\x1b[0m(arg1, arg2 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93murl_encode\x1b[0m(arg1 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93mto_upper\x1b[0m(arg1 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93mrand_text_alpha\x1b[0m(length \x1b[38;5;208muint\x1b[0m, optionalBadChars \x1b[38;5;208mstring\x1b[0m)\x1b[38;5;208m string\x1b[0m",
|
||||
"\x1b[93msha256\x1b[0m(arg1 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93mgzip\x1b[0m(arg1 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93mlen\x1b[0m(arg1 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93mtrim_space\x1b[0m(arg1 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93mrand_int\x1b[0m(optionalMin, optionalMax \x1b[38;5;208muint\x1b[0m)\x1b[38;5;208m int\x1b[0m",
|
||||
"\x1b[93mremove_bad_chars\x1b[0m(arg1, arg2 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93mrand_char\x1b[0m(optionalCharSet \x1b[38;5;208mstring\x1b[0m)\x1b[38;5;208m string\x1b[0m",
|
||||
"\x1b[93mreverse\x1b[0m(arg1 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93mhtml_escape\x1b[0m(arg1 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93mbase64\x1b[0m(arg1 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93mbase64_decode\x1b[0m(arg1 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93mhex_decode\x1b[0m(arg1 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93mtrim_prefix\x1b[0m(arg1, arg2 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93murl_decode\x1b[0m(arg1 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93mreplace\x1b[0m(arg1, arg2, arg3 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93mtrim_suffix\x1b[0m(arg1, arg2 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93mrand_text_numeric\x1b[0m(length \x1b[38;5;208muint\x1b[0m, optionalBadNumbers \x1b[38;5;208mstring\x1b[0m)\x1b[38;5;208m string\x1b[0m",
|
||||
"\x1b[93mcontains\x1b[0m(arg1, arg2 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93mgenerate_java_gadget\x1b[0m(arg1, arg2, arg3 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93munix_time\x1b[0m(optionalSeconds \x1b[38;5;208muint\x1b[0m)\x1b[38;5;208m float64\x1b[0m",
|
||||
"\x1b[93mtrim_left\x1b[0m(arg1, arg2 \x1b[38;5;208minterface{}\x1b[0m)\x1b[38;5;208m interface{}\x1b[0m",
|
||||
"\x1b[93mrand_text_alphanumeric\x1b[0m(length \x1b[38;5;208muint\x1b[0m, optionalBadChars \x1b[38;5;208mstring\x1b[0m)\x1b[38;5;208m string\x1b[0m",
|
||||
}
|
||||
assert.ElementsMatch(t, expectedColorizedSignatures, colorizeDslFunctionSignatures())
|
||||
}
|
||||
|
|
|
@ -55,12 +55,12 @@ func (m *Matcher) CompileMatchers() error {
|
|||
}
|
||||
|
||||
// Compile the dsl expressions
|
||||
for _, expr := range m.DSL {
|
||||
compiled, err := govaluate.NewEvaluableExpressionWithFunctions(expr, dsl.HelperFunctions())
|
||||
for _, dslExpression := range m.DSL {
|
||||
compiledExpression, err := govaluate.NewEvaluableExpressionWithFunctions(dslExpression, dsl.HelperFunctions())
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not compile dsl: %s. %w", expr, err)
|
||||
return &DslCompilationError{DslSignature: dslExpression, WrappedError: err}
|
||||
}
|
||||
m.dslCompiled = append(m.dslCompiled, compiled)
|
||||
m.dslCompiled = append(m.dslCompiled, compiledExpression)
|
||||
}
|
||||
|
||||
// Set up the condition type, if any.
|
||||
|
@ -83,3 +83,16 @@ func (m *Matcher) CompileMatchers() error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type DslCompilationError struct {
|
||||
DslSignature string
|
||||
WrappedError error
|
||||
}
|
||||
|
||||
func (e *DslCompilationError) Error() string {
|
||||
return fmt.Sprintf("could not compile DSL expression: %s. %v", e.DslSignature, e.WrappedError)
|
||||
}
|
||||
|
||||
func (e *DslCompilationError) Unwrap() error {
|
||||
return e.WrappedError
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package matchers
|
|||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/expressions"
|
||||
)
|
||||
|
||||
|
@ -54,17 +55,19 @@ func (m *Matcher) MatchWords(corpus string, dynamicValues map[string]interface{}
|
|||
var err error
|
||||
word, err = expressions.Evaluate(word, dynamicValues)
|
||||
if err != nil {
|
||||
gologger.Warning().Msgf("Error while evaluating word matcher: %q", word)
|
||||
continue
|
||||
}
|
||||
// Continue if the word doesn't match
|
||||
if !strings.Contains(corpus, word) {
|
||||
// If we are in an AND request and a match failed,
|
||||
// return false as the AND condition fails on any single mismatch.
|
||||
if m.condition == ANDCondition {
|
||||
switch m.condition {
|
||||
case ANDCondition:
|
||||
return false, []string{}
|
||||
case ORCondition:
|
||||
continue
|
||||
}
|
||||
// Continue with the flow since it's an OR Condition.
|
||||
continue
|
||||
}
|
||||
|
||||
// If the condition was an OR, return on the first match.
|
||||
|
@ -91,11 +94,12 @@ func (m *Matcher) MatchRegex(corpus string) (bool, []string) {
|
|||
if !regex.MatchString(corpus) {
|
||||
// If we are in an AND request and a match failed,
|
||||
// return false as the AND condition fails on any single mismatch.
|
||||
if m.condition == ANDCondition {
|
||||
switch m.condition {
|
||||
case ANDCondition:
|
||||
return false, []string{}
|
||||
case ORCondition:
|
||||
continue
|
||||
}
|
||||
// Continue with the flow since it's an OR Condition.
|
||||
continue
|
||||
}
|
||||
|
||||
currentMatches := regex.FindAllString(corpus, -1)
|
||||
|
@ -122,11 +126,12 @@ func (m *Matcher) MatchBinary(corpus string) (bool, []string) {
|
|||
if !strings.Contains(corpus, binary) {
|
||||
// If we are in an AND request and a match failed,
|
||||
// return false as the AND condition fails on any single mismatch.
|
||||
if m.condition == ANDCondition {
|
||||
switch m.condition {
|
||||
case ANDCondition:
|
||||
return false, []string{}
|
||||
case ORCondition:
|
||||
continue
|
||||
}
|
||||
// Continue with the flow since it's an OR Condition.
|
||||
continue
|
||||
}
|
||||
|
||||
// If the condition was an OR, return on the first match.
|
||||
|
@ -150,21 +155,22 @@ func (m *Matcher) MatchDSL(data map[string]interface{}) bool {
|
|||
for i, expression := range m.dslCompiled {
|
||||
result, err := expression.Evaluate(data)
|
||||
if err != nil {
|
||||
gologger.Warning().Msgf(err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
var bResult bool
|
||||
bResult, ok := result.(bool)
|
||||
|
||||
// Continue if the regex doesn't match
|
||||
if !ok || !bResult {
|
||||
if boolResult, ok := result.(bool); !ok {
|
||||
gologger.Warning().Msgf("The return value of a DSL statement must return a boolean value.")
|
||||
continue
|
||||
} else if !boolResult {
|
||||
// If we are in an AND request and a match failed,
|
||||
// return false as the AND condition fails on any single mismatch.
|
||||
if m.condition == ANDCondition {
|
||||
switch m.condition {
|
||||
case ANDCondition:
|
||||
return false
|
||||
case ORCondition:
|
||||
continue
|
||||
}
|
||||
// Continue with the flow since it's an OR Condition.
|
||||
continue
|
||||
}
|
||||
|
||||
// If the condition was an OR, return on the first match.
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
package executer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/operators/common/dsl"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/operators/matchers"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/output"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/helpers/writer"
|
||||
|
@ -24,8 +29,20 @@ func NewExecuter(requests []protocols.Request, options *protocols.ExecuterOption
|
|||
|
||||
// Compile compiles the execution generators preparing any requests possible.
|
||||
func (e *Executer) Compile() error {
|
||||
cliOptions := e.options.Options
|
||||
|
||||
for _, request := range e.requests {
|
||||
if err := request.Compile(e.options); err != nil {
|
||||
var dslCompilationError *matchers.DslCompilationError
|
||||
if errors.As(err, &dslCompilationError) {
|
||||
if cliOptions.Verbose {
|
||||
rawErrorMessage := dslCompilationError.Error()
|
||||
formattedErrorMessage := strings.ToUpper(rawErrorMessage[:1]) + rawErrorMessage[1:] + "."
|
||||
gologger.Warning().Msgf(formattedErrorMessage)
|
||||
gologger.Info().Msgf("The available custom DSL functions are:")
|
||||
fmt.Println(dsl.GetPrintableDslFunctionSignatures(cliOptions.NoColor))
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,7 +90,7 @@ func (i *Exporter) Export(event *output.ResultEvent) error {
|
|||
WithMessage(sarif.NewMessage().WithText(event.Host)).
|
||||
WithLevel(sarifSeverity)
|
||||
|
||||
// Also write file match metadata to file
|
||||
// Also write file match metadata to file
|
||||
if event.Type == "file" && (event.FileToIndexPosition != nil && len(event.FileToIndexPosition) > 0) {
|
||||
for file, line := range event.FileToIndexPosition {
|
||||
result.WithLocation(sarif.NewLocation().WithMessage(sarif.NewMessage().WithText(ruleName)).WithPhysicalLocation(
|
||||
|
|
Loading…
Reference in New Issue