github.com/projectdiscovery/nuclei/v2@v2.9.15/pkg/protocols/common/expressions/variables.go (about)

     1  package expressions
     2  
     3  import (
     4  	"errors"
     5  	"regexp"
     6  	"strings"
     7  
     8  	"github.com/Knetic/govaluate"
     9  	"github.com/projectdiscovery/nuclei/v2/pkg/operators/common/dsl"
    10  )
    11  
    12  var (
    13  	numericalExpressionRegex = regexp.MustCompile(`^[0-9+\-/\W]+$`)
    14  	unresolvedVariablesRegex = regexp.MustCompile(`(?:%7[B|b]|\{){2}([^}]+)(?:%7[D|d]|\}){2}["'\)\}]*`)
    15  )
    16  
    17  // ContainsUnresolvedVariables returns an error with variable names if the passed
    18  // input contains unresolved {{<pattern-here>}} variables.
    19  func ContainsUnresolvedVariables(items ...string) error {
    20  	for _, data := range items {
    21  		matches := unresolvedVariablesRegex.FindAllStringSubmatch(data, -1)
    22  		if len(matches) == 0 {
    23  			return nil
    24  		}
    25  		var unresolvedVariables []string
    26  		for _, match := range matches {
    27  			if len(match) < 2 {
    28  				continue
    29  			}
    30  			// Skip if the match is an expression
    31  			if numericalExpressionRegex.MatchString(match[1]) {
    32  				continue
    33  			}
    34  			// or if it contains only literals (can be solved from expression engine)
    35  			if hasLiteralsOnly(match[1]) {
    36  				continue
    37  			}
    38  			unresolvedVariables = append(unresolvedVariables, match[1])
    39  		}
    40  		if len(unresolvedVariables) > 0 {
    41  			return errors.New("unresolved variables found: " + strings.Join(unresolvedVariables, ","))
    42  		}
    43  	}
    44  
    45  	return nil
    46  }
    47  
    48  // ContainsVariablesWithNames returns an error with variable names if the passed
    49  // input contains unresolved {{<pattern-here>}} variables within the provided list
    50  func ContainsVariablesWithNames(names map[string]interface{}, items ...string) error {
    51  	for _, data := range items {
    52  		matches := unresolvedVariablesRegex.FindAllStringSubmatch(data, -1)
    53  		if len(matches) == 0 {
    54  			return nil
    55  		}
    56  		var unresolvedVariables []string
    57  		for _, match := range matches {
    58  			if len(match) < 2 {
    59  				continue
    60  			}
    61  			matchName := match[1]
    62  			// Skip if the match is an expression
    63  			if numericalExpressionRegex.MatchString(matchName) {
    64  				continue
    65  			}
    66  			// or if it contains only literals (can be solved from expression engine)
    67  			if hasLiteralsOnly(match[1]) {
    68  				continue
    69  			}
    70  			if _, ok := names[matchName]; !ok {
    71  				unresolvedVariables = append(unresolvedVariables, matchName)
    72  			}
    73  		}
    74  		if len(unresolvedVariables) > 0 {
    75  			return errors.New("unresolved variables with values found: " + strings.Join(unresolvedVariables, ","))
    76  		}
    77  	}
    78  
    79  	return nil
    80  }
    81  
    82  // ContainsVariablesWithIgnoreList returns an error with variable names if the passed
    83  // input contains unresolved {{<pattern-here>}} other than the ones listed in the ignore list
    84  func ContainsVariablesWithIgnoreList(skipNames map[string]interface{}, items ...string) error {
    85  	var unresolvedVariables []string
    86  	for _, data := range items {
    87  		matches := unresolvedVariablesRegex.FindAllStringSubmatch(data, -1)
    88  		if len(matches) == 0 {
    89  			return nil
    90  		}
    91  		for _, match := range matches {
    92  			if len(match) < 2 {
    93  				continue
    94  			}
    95  			matchName := match[1]
    96  			// Skip if the match is an expression
    97  			if numericalExpressionRegex.MatchString(matchName) {
    98  				continue
    99  			}
   100  			// or if it contains only literals (can be solved from expression engine)
   101  			if hasLiteralsOnly(match[1]) {
   102  				continue
   103  			}
   104  			if _, ok := skipNames[matchName]; ok {
   105  				continue
   106  			}
   107  			unresolvedVariables = append(unresolvedVariables, matchName)
   108  		}
   109  	}
   110  
   111  	if len(unresolvedVariables) > 0 {
   112  		return errors.New("unresolved variables with values found: " + strings.Join(unresolvedVariables, ","))
   113  	}
   114  
   115  	return nil
   116  }
   117  
   118  func hasLiteralsOnly(data string) bool {
   119  	expr, err := govaluate.NewEvaluableExpressionWithFunctions(data, dsl.HelperFunctions)
   120  	if err != nil {
   121  		return false
   122  	}
   123  	if expr != nil {
   124  		_, err = expr.Evaluate(nil)
   125  		return err == nil
   126  	}
   127  	return true
   128  }