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
forgedhallpass 2021-12-07 17:34:36 +02:00
parent c61ec5f673
commit 79aed22d46
6 changed files with 253 additions and 73 deletions

View File

@ -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 {

View File

@ -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())
}

View File

@ -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
}

View File

@ -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.

View File

@ -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
}
}

View File

@ -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(