Path: blob/dev/pkg/operators/matchers/compile.go
2851 views
package matchers12import (3"encoding/hex"4"fmt"5"regexp"6"strings"78"github.com/Knetic/govaluate"9"github.com/projectdiscovery/nuclei/v3/pkg/operators/cache"10"github.com/projectdiscovery/nuclei/v3/pkg/operators/common/dsl"11)1213// CompileMatchers performs the initial setup operation on a matcher14func (matcher *Matcher) CompileMatchers() error {15var ok bool1617// Support hexadecimal encoding for matchers too.18if matcher.Encoding == "hex" {19for i, word := range matcher.Words {20if decoded, err := hex.DecodeString(word); err == nil && len(decoded) > 0 {21matcher.Words[i] = string(decoded)22}23}24}2526// Set up the matcher type27computedType, err := toMatcherTypes(matcher.GetType().String())28if err != nil {29return fmt.Errorf("unknown matcher type specified: %s", matcher.Type)30}3132matcher.matcherType = computedType3334// Validate the matcher structure35if err := matcher.Validate(); err != nil {36return err37}3839// By default, match on body if user hasn't provided any specific items40if matcher.Part == "" && matcher.GetType() != DSLMatcher {41matcher.Part = "body"42}4344// Compile the regexes (with shared cache)45for _, regex := range matcher.Regex {46if cached, err := cache.Regex().GetIFPresent(regex); err == nil && cached != nil {47matcher.regexCompiled = append(matcher.regexCompiled, cached)48continue49}50compiled, err := regexp.Compile(regex)51if err != nil {52return fmt.Errorf("could not compile regex: %s", regex)53}54_ = cache.Regex().Set(regex, compiled)55matcher.regexCompiled = append(matcher.regexCompiled, compiled)56}5758// Compile and validate binary Values in matcher59for _, value := range matcher.Binary {60if decoded, err := hex.DecodeString(value); err != nil {61return fmt.Errorf("could not hex decode binary: %s", value)62} else {63matcher.binaryDecoded = append(matcher.binaryDecoded, string(decoded))64}65}6667// Compile the dsl expressions (with shared cache)68for _, dslExpression := range matcher.DSL {69if cached, err := cache.DSL().GetIFPresent(dslExpression); err == nil && cached != nil {70matcher.dslCompiled = append(matcher.dslCompiled, cached)71continue72}73compiledExpression, err := govaluate.NewEvaluableExpressionWithFunctions(dslExpression, dsl.HelperFunctions)74if err != nil {75return &dsl.CompilationError{DslSignature: dslExpression, WrappedError: err}76}77_ = cache.DSL().Set(dslExpression, compiledExpression)78matcher.dslCompiled = append(matcher.dslCompiled, compiledExpression)79}8081// Set up the condition type, if any.82if matcher.Condition != "" {83matcher.condition, ok = ConditionTypes[matcher.Condition]84if !ok {85return fmt.Errorf("unknown condition specified: %s", matcher.Condition)86}87} else {88matcher.condition = ORCondition89}9091if matcher.CaseInsensitive {92if matcher.GetType() != WordsMatcher {93return fmt.Errorf("case-insensitive flag is supported only for 'word' matchers (not '%s')", matcher.Type)94}95for i := range matcher.Words {96matcher.Words[i] = strings.ToLower(matcher.Words[i])97}98}99return nil100}101102// GetType returns the condition type of the matcher103// todo: the field should be exposed natively104func (matcher *Matcher) GetCondition() ConditionType {105return matcher.condition106}107108109