package workflows import ( "fmt" "github.com/projectdiscovery/nuclei/v3/pkg/model/types/stringslice" "github.com/projectdiscovery/nuclei/v3/pkg/operators" "github.com/projectdiscovery/nuclei/v3/pkg/protocols" templateTypes "github.com/projectdiscovery/nuclei/v3/pkg/templates/types" ) // Workflow is a workflow to execute with chained requests, etc. type Workflow struct { // description: | // Workflows is a list of workflows to execute for a template. Workflows []*WorkflowTemplate `yaml:"workflows,omitempty" json:"workflows,omitempty" jsonschema:"title=list of workflows to execute,description=List of workflows to execute for template"` Options *protocols.ExecutorOptions `yaml:"-" json:"-"` } // WorkflowTemplate is a template to be run as part of a workflow type WorkflowTemplate struct { // description: | // Template is a single template or directory to execute as part of workflow. // examples: // - name: A single template // value: "\"dns/worksites-detection.yaml\"" // - name: A template directory // value: "\"misconfigurations/aem\"" Template string `yaml:"template,omitempty" json:"template,omitempty" jsonschema:"title=template/directory to execute,description=Template or directory to execute as part of workflow"` // description: | // Tags to run templates based on. Tags stringslice.StringSlice `yaml:"tags,omitempty" json:"tags,omitempty" jsonschema:"title=tags to execute,description=Tags to run template based on"` // description: | // Matchers perform name based matching to run subtemplates for a workflow. Matchers []*Matcher `yaml:"matchers,omitempty" json:"matchers,omitempty" jsonschema:"title=name based template result matchers,description=Matchers perform name based matching to run subtemplates for a workflow"` // description: | // Subtemplates are run if the `template` field Template matches. Subtemplates []*WorkflowTemplate `yaml:"subtemplates,omitempty" json:"subtemplates,omitempty" jsonschema:"title=subtemplate based result matchers,description=Subtemplates are ran if the template field Template matches"` // Executers perform the actual execution for the workflow template Executers []*ProtocolExecuterPair `yaml:"-" json:"-"` } // ProtocolExecuterPair is a pair of protocol executer and its options type ProtocolExecuterPair struct { Executer protocols.Executer Options *protocols.ExecutorOptions TemplateType templateTypes.ProtocolType } // Matcher performs conditional matching on the workflow template results. type Matcher struct { // description: | // Name is the name of the items to match. Name stringslice.StringSlice `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"title=name of items to match,description=Name of items to match"` // description: | // Condition is the optional condition between names. By default, // the condition is assumed to be OR. // values: // - "and" // - "or" Condition string `yaml:"condition,omitempty" json:"condition,omitempty" jsonschema:"title=condition between names,description=Condition between the names,enum=and,enum=or"` // description: | // Subtemplates are run if the name of matcher matches. Subtemplates []*WorkflowTemplate `yaml:"subtemplates,omitempty" json:"subtemplates,omitempty" jsonschema:"title=templates to run after match,description=Templates to run after match"` condition ConditionType } // ConditionType is the type of condition for matcher type ConditionType int const ( // ANDCondition matches responses with AND condition in arguments. ANDCondition ConditionType = iota + 1 // ORCondition matches responses with AND condition in arguments. ORCondition ) // ConditionTypes is a table for conversion of condition type from string. var ConditionTypes = map[string]ConditionType{ "and": ANDCondition, "or": ORCondition, } // Compile compiles the matcher for workflow func (matcher *Matcher) Compile() error { var ok bool if matcher.Condition != "" { matcher.condition, ok = ConditionTypes[matcher.Condition] if !ok { return fmt.Errorf("unknown condition specified: %s", matcher.Condition) } } else { matcher.condition = ORCondition } return nil } // Match matches a name for matcher names or name func (matcher *Matcher) Match(result *operators.Result) bool { names := matcher.Name.ToSlice() if len(names) == 0 { return false } for i, name := range names { matchOK := result.HasMatch(name) extractOK := result.HasExtract(name) if !matchOK && !extractOK { if matcher.condition == ANDCondition { return false } continue } if matcher.condition == ORCondition { return true } else if len(names)-1 == i { return true } } return false }