Path: blob/dev/pkg/protocols/common/variables/variables.go
2073 views
package variables12import (3"strings"45"github.com/invopop/jsonschema"6"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/expressions"7"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"8"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/interactsh"9protocolutils "github.com/projectdiscovery/nuclei/v3/pkg/protocols/utils"10"github.com/projectdiscovery/nuclei/v3/pkg/types"11"github.com/projectdiscovery/nuclei/v3/pkg/utils"12"github.com/projectdiscovery/nuclei/v3/pkg/utils/json"13stringsutil "github.com/projectdiscovery/utils/strings"14)1516// Variable is a key-value pair of strings that can be used17// throughout template.18type Variable struct {19LazyEval bool `yaml:"-" json:"-"` // LazyEval is used to evaluate variables lazily if it using any expression or global variables20utils.InsertionOrderedStringMap `yaml:"-" json:"-"`21}2223func (variables Variable) JSONSchema() *jsonschema.Schema {24gotType := &jsonschema.Schema{25Type: "object",26Title: "variables for the request",27Description: "Additional variables for the request",28AdditionalProperties: &jsonschema.Schema{},29}30return gotType31}3233func (variables *Variable) UnmarshalYAML(unmarshal func(interface{}) error) error {34variables.InsertionOrderedStringMap = utils.InsertionOrderedStringMap{}35if err := unmarshal(&variables.InsertionOrderedStringMap); err != nil {36return err37}3839if variables.LazyEval || variables.checkForLazyEval() {40return nil41}4243evaluated := variables.Evaluate(map[string]interface{}{})4445for k, v := range evaluated {46variables.Set(k, v)47}48return nil49}5051func (variables *Variable) UnmarshalJSON(data []byte) error {52variables.InsertionOrderedStringMap = utils.InsertionOrderedStringMap{}53if err := json.Unmarshal(data, &variables.InsertionOrderedStringMap); err != nil {54return err55}56evaluated := variables.Evaluate(map[string]interface{}{})5758for k, v := range evaluated {59variables.Set(k, v)60}61return nil62}6364// Evaluate returns a finished map of variables based on set values65func (variables *Variable) Evaluate(values map[string]interface{}) map[string]interface{} {66result := make(map[string]interface{}, variables.Len())67variables.ForEach(func(key string, value interface{}) {68if sliceValue, ok := value.([]interface{}); ok {69// slices cannot be evaluated70result[key] = sliceValue71return72}73valueString := types.ToString(value)74combined := generators.MergeMaps(values, result)75if value, ok := combined[key]; ok {76valueString = types.ToString(value)77}78result[key] = evaluateVariableValue(valueString, combined, result)79})80return result81}8283// GetAll returns all variables as a map84func (variables *Variable) GetAll() map[string]interface{} {85result := make(map[string]interface{}, variables.Len())86variables.ForEach(func(key string, value interface{}) {87result[key] = value88})89return result90}9192// EvaluateWithInteractsh returns evaluation results of variables with interactsh93func (variables *Variable) EvaluateWithInteractsh(values map[string]interface{}, interact *interactsh.Client) (map[string]interface{}, []string) {94result := make(map[string]interface{}, variables.Len())9596var interactURLs []string97variables.ForEach(func(key string, value interface{}) {98if sliceValue, ok := value.([]interface{}); ok {99// slices cannot be evaluated100result[key] = sliceValue101return102}103valueString := types.ToString(value)104if strings.Contains(valueString, "interactsh-url") {105valueString, interactURLs = interact.Replace(valueString, interactURLs)106}107combined := generators.MergeMaps(values, result)108if value, ok := combined[key]; ok {109valueString = types.ToString(value)110}111result[key] = evaluateVariableValue(valueString, combined, result)112})113return result, interactURLs114}115116// evaluateVariableValue expression and returns final value117func evaluateVariableValue(expression string, values, processing map[string]interface{}) string {118finalMap := generators.MergeMaps(values, processing)119result, err := expressions.Evaluate(expression, finalMap)120if err != nil {121return expression122}123124return result125}126127// checkForLazyEval checks if the variables have any lazy evaluation i.e any dsl function128// and sets the flag accordingly.129func (variables *Variable) checkForLazyEval() bool {130variables.ForEach(func(key string, value interface{}) {131for _, v := range protocolutils.KnownVariables {132if stringsutil.ContainsAny(types.ToString(value), v) {133variables.LazyEval = true134return135}136}137// this is a hotfix and not the best way to do it138// will be refactored once we move scan state to scanContext (see: https://github.com/projectdiscovery/nuclei/issues/4631)139if strings.Contains(types.ToString(value), "interactsh-url") {140variables.LazyEval = true141return142}143})144return variables.LazyEval145}146147148