diff --git a/pkg/core/executors.go b/pkg/core/executors.go index 262fdcefe7..81baa020dc 100644 --- a/pkg/core/executors.go +++ b/pkg/core/executors.go @@ -5,8 +5,8 @@ import ( "sync/atomic" "github.com/projectdiscovery/gologger" - "github.com/projectdiscovery/nuclei/v3/pkg/output" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" + "github.com/projectdiscovery/nuclei/v3/pkg/scan" "github.com/projectdiscovery/nuclei/v3/pkg/templates" "github.com/projectdiscovery/nuclei/v3/pkg/templates/types" generalTypes "github.com/projectdiscovery/nuclei/v3/pkg/types" @@ -23,15 +23,17 @@ func (e *Engine) executeAllSelfContained(alltemplates []*templates.Template, res defer sg.Done() var err error var match bool + ctx := scan.NewScanContext(contextargs.New()) if e.Callback != nil { - err = template.Executer.ExecuteWithResults(contextargs.New(), func(event *output.InternalWrappedEvent) { - for _, result := range event.Results { + if results, err := template.Executer.ExecuteWithResults(ctx); err != nil { + for _, result := range results { e.Callback(result) } - }) + } + match = true } else { - match, err = template.Executer.Execute(contextargs.New()) + match, err = template.Executer.Execute(ctx) } if err != nil { gologger.Warning().Msgf("[%s] Could not execute step: %s\n", e.executerOpts.Colorizer.BrightBlue(template.ID), err) @@ -111,21 +113,22 @@ func (e *Engine) executeTemplateWithTargets(template *templates.Template, target var match bool var err error + ctxArgs := contextargs.New() + ctxArgs.MetaInput = value + ctx := scan.NewScanContext(ctxArgs) switch template.Type() { case types.WorkflowProtocol: - match = e.executeWorkflow(value, template.CompiledWorkflow) + match = e.executeWorkflow(ctx, template.CompiledWorkflow) default: - ctxArgs := contextargs.New() - ctxArgs.MetaInput = value if e.Callback != nil { - err = template.Executer.ExecuteWithResults(ctxArgs, func(event *output.InternalWrappedEvent) { - for _, result := range event.Results { + if results, err := template.Executer.ExecuteWithResults(ctx); err != nil { + for _, result := range results { e.Callback(result) } - }) + } match = true } else { - match, err = template.Executer.Execute(ctxArgs) + match, err = template.Executer.Execute(ctx) } } if err != nil { @@ -166,21 +169,22 @@ func (e *Engine) executeTemplatesOnTarget(alltemplates []*templates.Template, ta var match bool var err error + ctxArgs := contextargs.New() + ctxArgs.MetaInput = value + ctx := scan.NewScanContext(ctxArgs) switch template.Type() { case types.WorkflowProtocol: - match = e.executeWorkflow(value, template.CompiledWorkflow) + match = e.executeWorkflow(ctx, template.CompiledWorkflow) default: - ctxArgs := contextargs.New() - ctxArgs.MetaInput = value if e.Callback != nil { - err = template.Executer.ExecuteWithResults(ctxArgs, func(event *output.InternalWrappedEvent) { - for _, result := range event.Results { + if results, err := template.Executer.ExecuteWithResults(ctx); err != nil { + for _, result := range results { e.Callback(result) } - }) + } match = true } else { - match, err = template.Executer.Execute(ctxArgs) + match, err = template.Executer.Execute(ctx) } } if err != nil { @@ -221,7 +225,8 @@ func (e *ChildExecuter) Execute(template *templates.Template, value *contextargs ctxArgs := contextargs.New() ctxArgs.MetaInput = value - match, err := template.Executer.Execute(ctxArgs) + ctx := scan.NewScanContext(ctxArgs) + match, err := template.Executer.Execute(ctx) if err != nil { gologger.Warning().Msgf("[%s] Could not execute step: %s\n", e.e.executerOpts.Colorizer.BrightBlue(template.ID), err) } diff --git a/pkg/core/workflow_execute.go b/pkg/core/workflow_execute.go index a0e210587b..cb877cc60e 100644 --- a/pkg/core/workflow_execute.go +++ b/pkg/core/workflow_execute.go @@ -10,19 +10,20 @@ import ( "github.com/projectdiscovery/gologger" "github.com/projectdiscovery/nuclei/v3/pkg/output" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" + "github.com/projectdiscovery/nuclei/v3/pkg/scan" "github.com/projectdiscovery/nuclei/v3/pkg/workflows" ) const workflowStepExecutionError = "[%s] Could not execute workflow step: %s\n" // executeWorkflow runs a workflow on an input and returns true or false -func (e *Engine) executeWorkflow(input *contextargs.MetaInput, w *workflows.Workflow) bool { +func (e *Engine) executeWorkflow(ctx *scan.ScanContext, w *workflows.Workflow) bool { results := &atomic.Bool{} // at this point we should be at the start root execution of a workflow tree, hence we create global shared instances workflowCookieJar, _ := cookiejar.New(nil) ctxArgs := contextargs.New() - ctxArgs.MetaInput = input + ctxArgs.MetaInput = ctx.Input.MetaInput ctxArgs.CookieJar = workflowCookieJar // we can know the nesting level only at runtime, so the best we can do here is increase template threads by one unit in case it's equal to 1 to allow @@ -39,7 +40,7 @@ func (e *Engine) executeWorkflow(input *contextargs.MetaInput, w *workflows.Work func(template *workflows.WorkflowTemplate) { defer swg.Done() - if err := e.runWorkflowStep(template, ctxArgs, results, &swg, w); err != nil { + if err := e.runWorkflowStep(template, ctx, results, &swg, w); err != nil { gologger.Warning().Msgf(workflowStepExecutionError, template.Template, err) } }(template) @@ -50,7 +51,7 @@ func (e *Engine) executeWorkflow(input *contextargs.MetaInput, w *workflows.Work // runWorkflowStep runs a workflow step for the workflow. It executes the workflow // in a recursive manner running all subtemplates and matchers. -func (e *Engine) runWorkflowStep(template *workflows.WorkflowTemplate, input *contextargs.Context, results *atomic.Bool, swg *sizedwaitgroup.SizedWaitGroup, w *workflows.Workflow) error { +func (e *Engine) runWorkflowStep(template *workflows.WorkflowTemplate, ctx *scan.ScanContext, results *atomic.Bool, swg *sizedwaitgroup.SizedWaitGroup, w *workflows.Workflow) error { var firstMatched bool var err error var mainErr error @@ -61,7 +62,7 @@ func (e *Engine) runWorkflowStep(template *workflows.WorkflowTemplate, input *co // Don't print results with subtemplates, only print results on template. if len(template.Subtemplates) > 0 { - err = executer.Executer.ExecuteWithResults(input, func(result *output.InternalWrappedEvent) { + ctx.OnResult = func(result *output.InternalWrappedEvent) { if result.OperatorsResult == nil { return } @@ -75,29 +76,30 @@ func (e *Engine) runWorkflowStep(template *workflows.WorkflowTemplate, input *co switch len(v) { case 0, 1: // - key:[item] => key: item - input.Set(k, v[0]) + ctx.Input.Set(k, v[0]) default: // - key:[item_0, ..., item_n] => key0:item_0, keyn:item_n for vIdx, vVal := range v { normalizedKIdx := fmt.Sprintf("%s%d", k, vIdx) - input.Set(normalizedKIdx, vVal) + ctx.Input.Set(normalizedKIdx, vVal) } // also add the original name with full slice - input.Set(k, v) + ctx.Input.Set(k, v) } } } - }) + } + _, err = executer.Executer.ExecuteWithResults(ctx) } else { var matched bool - matched, err = executer.Executer.Execute(input) + matched, err = executer.Executer.Execute(ctx) if matched { firstMatched = true } } if err != nil { if w.Options.HostErrorsCache != nil { - w.Options.HostErrorsCache.MarkFailed(input.MetaInput.ID(), err) + w.Options.HostErrorsCache.MarkFailed(ctx.Input.MetaInput.ID(), err) } if len(template.Executers) == 1 { mainErr = err @@ -115,14 +117,14 @@ func (e *Engine) runWorkflowStep(template *workflows.WorkflowTemplate, input *co for _, executer := range template.Executers { executer.Options.Progress.AddToTotal(int64(executer.Executer.Requests())) - err := executer.Executer.ExecuteWithResults(input, func(event *output.InternalWrappedEvent) { + ctx.OnResult = func(event *output.InternalWrappedEvent) { if event.OperatorsResult == nil { return } if event.OperatorsResult.Extracts != nil { for k, v := range event.OperatorsResult.Extracts { - input.Set(k, v) + ctx.Input.Set(k, v) } } @@ -137,13 +139,14 @@ func (e *Engine) runWorkflowStep(template *workflows.WorkflowTemplate, input *co go func(subtemplate *workflows.WorkflowTemplate) { defer swg.Done() - if err := e.runWorkflowStep(subtemplate, input, results, swg, w); err != nil { + if err := e.runWorkflowStep(subtemplate, ctx, results, swg, w); err != nil { gologger.Warning().Msgf(workflowStepExecutionError, subtemplate.Template, err) } }(subtemplate) } } - }) + } + _, err := executer.Executer.ExecuteWithResults(ctx) if err != nil { if len(template.Executers) == 1 { mainErr = err @@ -160,7 +163,7 @@ func (e *Engine) runWorkflowStep(template *workflows.WorkflowTemplate, input *co swg.Add() go func(template *workflows.WorkflowTemplate) { - if err := e.runWorkflowStep(template, input, results, swg, w); err != nil { + if err := e.runWorkflowStep(template, ctx, results, swg, w); err != nil { gologger.Warning().Msgf(workflowStepExecutionError, template.Template, err) } swg.Done() diff --git a/pkg/core/workflow_execute_test.go b/pkg/core/workflow_execute_test.go index fd9c09ed78..f3d6a7f231 100644 --- a/pkg/core/workflow_execute_test.go +++ b/pkg/core/workflow_execute_test.go @@ -9,6 +9,7 @@ import ( "github.com/projectdiscovery/nuclei/v3/pkg/progress" "github.com/projectdiscovery/nuclei/v3/pkg/protocols" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" + "github.com/projectdiscovery/nuclei/v3/pkg/scan" "github.com/projectdiscovery/nuclei/v3/pkg/types" "github.com/projectdiscovery/nuclei/v3/pkg/workflows" "github.com/stretchr/testify/require" @@ -24,7 +25,9 @@ func TestWorkflowsSimple(t *testing.T) { }} engine := &Engine{} - matched := engine.executeWorkflow(&contextargs.MetaInput{Input: "https://test.com"}, workflow) + input := contextargs.NewWithInput("https://test.com") + ctx := scan.NewScanContext(input) + matched := engine.executeWorkflow(ctx, workflow) require.True(t, matched, "could not get correct match value") } @@ -46,7 +49,9 @@ func TestWorkflowsSimpleMultiple(t *testing.T) { }} engine := &Engine{} - matched := engine.executeWorkflow(&contextargs.MetaInput{Input: "https://test.com"}, workflow) + input := contextargs.NewWithInput("https://test.com") + ctx := scan.NewScanContext(input) + matched := engine.executeWorkflow(ctx, workflow) require.True(t, matched, "could not get correct match value") require.Equal(t, "https://test.com", firstInput, "could not get correct first input") @@ -72,7 +77,9 @@ func TestWorkflowsSubtemplates(t *testing.T) { }} engine := &Engine{} - matched := engine.executeWorkflow(&contextargs.MetaInput{Input: "https://test.com"}, workflow) + input := contextargs.NewWithInput("https://test.com") + ctx := scan.NewScanContext(input) + matched := engine.executeWorkflow(ctx, workflow) require.True(t, matched, "could not get correct match value") require.Equal(t, "https://test.com", firstInput, "could not get correct first input") @@ -96,7 +103,9 @@ func TestWorkflowsSubtemplatesNoMatch(t *testing.T) { }} engine := &Engine{} - matched := engine.executeWorkflow(&contextargs.MetaInput{Input: "https://test.com"}, workflow) + input := contextargs.NewWithInput("https://test.com") + ctx := scan.NewScanContext(input) + matched := engine.executeWorkflow(ctx, workflow) require.False(t, matched, "could not get correct match value") require.Equal(t, "https://test.com", firstInput, "could not get correct first input") @@ -125,7 +134,9 @@ func TestWorkflowsSubtemplatesWithMatcher(t *testing.T) { }} engine := &Engine{} - matched := engine.executeWorkflow(&contextargs.MetaInput{Input: "https://test.com"}, workflow) + input := contextargs.NewWithInput("https://test.com") + ctx := scan.NewScanContext(input) + matched := engine.executeWorkflow(ctx, workflow) require.True(t, matched, "could not get correct match value") require.Equal(t, "https://test.com", firstInput, "could not get correct first input") @@ -154,7 +165,9 @@ func TestWorkflowsSubtemplatesWithMatcherNoMatch(t *testing.T) { }} engine := &Engine{} - matched := engine.executeWorkflow(&contextargs.MetaInput{Input: "https://test.com"}, workflow) + input := contextargs.NewWithInput("https://test.com") + ctx := scan.NewScanContext(input) + matched := engine.executeWorkflow(ctx, workflow) require.False(t, matched, "could not get correct match value") require.Equal(t, "https://test.com", firstInput, "could not get correct first input") @@ -178,20 +191,20 @@ func (m *mockExecuter) Requests() int { } // Execute executes the protocol group and returns true or false if results were found. -func (m *mockExecuter) Execute(input *contextargs.Context) (bool, error) { +func (m *mockExecuter) Execute(ctx *scan.ScanContext) (bool, error) { if m.executeHook != nil { - m.executeHook(input.MetaInput) + m.executeHook(ctx.Input.MetaInput) } return m.result, nil } // ExecuteWithResults executes the protocol requests and returns results instead of writing them. -func (m *mockExecuter) ExecuteWithResults(input *contextargs.Context, callback protocols.OutputEventCallback) error { +func (m *mockExecuter) ExecuteWithResults(ctx *scan.ScanContext) ([]*output.ResultEvent, error) { if m.executeHook != nil { - m.executeHook(input.MetaInput) + m.executeHook(ctx.Input.MetaInput) } for _, output := range m.outputs { - callback(output) + ctx.LogEvent(output) } - return nil + return ctx.GenerateResult(), nil } diff --git a/pkg/output/output.go b/pkg/output/output.go index 56352b20d8..f2e33a8ada 100644 --- a/pkg/output/output.go +++ b/pkg/output/output.go @@ -159,6 +159,7 @@ type ResultEvent struct { Lines []int `json:"matched-line,omitempty"` FileToIndexPosition map[string]int `json:"-"` + Error string `json:"error,omitempty"` } // NewStandardWriter creates a new output writer based on user configurations @@ -360,6 +361,7 @@ func (w *StandardWriter) WriteFailure(wrappedEvent *InternalWrappedEvent) error Timestamp: time.Now(), //FIXME: this is workaround to encode the template when no results were found TemplateEncoded: w.encodeTemplate(types.ToString(event["template-path"])), + Error: types.ToString(event["error"]), } return w.Write(data) } diff --git a/pkg/protocols/code/code.go b/pkg/protocols/code/code.go index 5977702e5e..bb8c06e549 100644 --- a/pkg/protocols/code/code.go +++ b/pkg/protocols/code/code.go @@ -255,6 +255,7 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent Timestamp: time.Now(), MatcherStatus: true, TemplateEncoded: request.options.EncodeTemplate(), + Error: types.ToString(wrapped.InternalEvent["error"]), } return data } diff --git a/pkg/protocols/dns/operators.go b/pkg/protocols/dns/operators.go index 7aeafe5837..7b8467646b 100644 --- a/pkg/protocols/dns/operators.go +++ b/pkg/protocols/dns/operators.go @@ -123,6 +123,7 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent Request: types.ToString(wrapped.InternalEvent["request"]), Response: types.ToString(wrapped.InternalEvent["raw"]), TemplateEncoded: request.options.EncodeTemplate(), + Error: types.ToString(wrapped.InternalEvent["error"]), } return data } diff --git a/pkg/protocols/file/operators.go b/pkg/protocols/file/operators.go index 347b5846c5..aee2abdfda 100644 --- a/pkg/protocols/file/operators.go +++ b/pkg/protocols/file/operators.go @@ -108,6 +108,7 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent Response: types.ToString(wrapped.InternalEvent["raw"]), Timestamp: time.Now(), TemplateEncoded: request.options.EncodeTemplate(), + Error: types.ToString(wrapped.InternalEvent["error"]), } return data } diff --git a/pkg/protocols/headless/operators.go b/pkg/protocols/headless/operators.go index c1bc0f2164..fd50ddaddb 100644 --- a/pkg/protocols/headless/operators.go +++ b/pkg/protocols/headless/operators.go @@ -139,6 +139,7 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent Request: types.ToString(wrapped.InternalEvent["request"]), Response: types.ToString(wrapped.InternalEvent["data"]), TemplateEncoded: request.options.EncodeTemplate(), + Error: types.ToString(wrapped.InternalEvent["error"]), } return data } diff --git a/pkg/protocols/http/operators.go b/pkg/protocols/http/operators.go index 475bdf81b4..1e0f680399 100644 --- a/pkg/protocols/http/operators.go +++ b/pkg/protocols/http/operators.go @@ -165,6 +165,7 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent Response: request.truncateResponse(wrapped.InternalEvent["response"]), CURLCommand: types.ToString(wrapped.InternalEvent["curl-command"]), TemplateEncoded: request.options.EncodeTemplate(), + Error: types.ToString(wrapped.InternalEvent["error"]), } return data } diff --git a/pkg/protocols/http/request.go b/pkg/protocols/http/request.go index aac2bad71d..2328c8f30c 100644 --- a/pkg/protocols/http/request.go +++ b/pkg/protocols/http/request.go @@ -655,26 +655,25 @@ func (request *Request) executeRequest(input *contextargs.Context, generatedRequ request.options.Output.Request(request.options.TemplatePath, formedURL, request.Type().String(), err) request.options.Progress.IncrementErrorsBy(1) - // If we have interactsh markers and request times out, still send + // In case of interactsh markers and request times out, still send // a callback event so in case we receive an interaction, correlation is possible. - if hasInteractMatchers { - outputEvent := request.responseToDSLMap(&http.Response{}, input.MetaInput.Input, formedURL, tostring.UnsafeToString(dumpedRequest), "", "", "", 0, generatedRequest.meta) - if i := strings.LastIndex(hostname, ":"); i != -1 { - hostname = hostname[:i] - } + // Also, to log failed use-cases. + outputEvent := request.responseToDSLMap(&http.Response{}, input.MetaInput.Input, formedURL, tostring.UnsafeToString(dumpedRequest), "", "", "", 0, generatedRequest.meta) + if i := strings.LastIndex(hostname, ":"); i != -1 { + hostname = hostname[:i] + } - if input.MetaInput.CustomIP != "" { - outputEvent["ip"] = input.MetaInput.CustomIP - } else { - outputEvent["ip"] = httpclientpool.Dialer.GetDialedIP(hostname) - } + if input.MetaInput.CustomIP != "" { + outputEvent["ip"] = input.MetaInput.CustomIP + } else { + outputEvent["ip"] = httpclientpool.Dialer.GetDialedIP(hostname) + } - event := &output.InternalWrappedEvent{InternalEvent: outputEvent} - if request.CompiledOperators != nil { - event.InternalEvent = outputEvent - } - callback(event) + event := &output.InternalWrappedEvent{InternalEvent: outputEvent} + if request.CompiledOperators != nil { + event.InternalEvent = outputEvent } + callback(event) return err } defer func() { diff --git a/pkg/protocols/javascript/js.go b/pkg/protocols/javascript/js.go index 3736bf034c..d6c8917970 100644 --- a/pkg/protocols/javascript/js.go +++ b/pkg/protocols/javascript/js.go @@ -635,6 +635,7 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent Response: types.ToString(wrapped.InternalEvent["response"]), IP: types.ToString(wrapped.InternalEvent["ip"]), TemplateEncoded: request.options.EncodeTemplate(), + Error: types.ToString(wrapped.InternalEvent["error"]), } return data } diff --git a/pkg/protocols/network/operators.go b/pkg/protocols/network/operators.go index 74bee27980..8988496408 100644 --- a/pkg/protocols/network/operators.go +++ b/pkg/protocols/network/operators.go @@ -109,6 +109,7 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent Request: types.ToString(wrapped.InternalEvent["request"]), Response: types.ToString(wrapped.InternalEvent["data"]), TemplateEncoded: request.options.EncodeTemplate(), + Error: types.ToString(wrapped.InternalEvent["error"]), } return data } diff --git a/pkg/protocols/offlinehttp/operators.go b/pkg/protocols/offlinehttp/operators.go index 0bd7a30ff9..0d906ff514 100644 --- a/pkg/protocols/offlinehttp/operators.go +++ b/pkg/protocols/offlinehttp/operators.go @@ -152,6 +152,7 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent Request: types.ToString(wrapped.InternalEvent["request"]), Response: types.ToString(wrapped.InternalEvent["raw"]), TemplateEncoded: request.options.EncodeTemplate(), + Error: types.ToString(wrapped.InternalEvent["error"]), } return data } diff --git a/pkg/protocols/protocols.go b/pkg/protocols/protocols.go index c818b70685..1e05080cab 100644 --- a/pkg/protocols/protocols.go +++ b/pkg/protocols/protocols.go @@ -27,6 +27,7 @@ import ( "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/variables" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/headless/engine" "github.com/projectdiscovery/nuclei/v3/pkg/reporting" + "github.com/projectdiscovery/nuclei/v3/pkg/scan" templateTypes "github.com/projectdiscovery/nuclei/v3/pkg/templates/types" "github.com/projectdiscovery/nuclei/v3/pkg/types" ) @@ -40,9 +41,9 @@ type Executer interface { // Requests returns the total number of requests the rule will perform Requests() int // Execute executes the protocol group and returns true or false if results were found. - Execute(input *contextargs.Context) (bool, error) + Execute(ctx *scan.ScanContext) (bool, error) // ExecuteWithResults executes the protocol requests and returns results instead of writing them. - ExecuteWithResults(input *contextargs.Context, callback OutputEventCallback) error + ExecuteWithResults(ctx *scan.ScanContext) ([]*output.ResultEvent, error) } // ExecutorOptions contains the configuration options for executer clients diff --git a/pkg/protocols/ssl/ssl.go b/pkg/protocols/ssl/ssl.go index aa05b012ae..2415a47ebc 100644 --- a/pkg/protocols/ssl/ssl.go +++ b/pkg/protocols/ssl/ssl.go @@ -403,6 +403,7 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent MatcherStatus: true, IP: types.ToString(wrapped.InternalEvent["ip"]), TemplateEncoded: request.options.EncodeTemplate(), + Error: types.ToString(wrapped.InternalEvent["error"]), } return data } diff --git a/pkg/protocols/websocket/websocket.go b/pkg/protocols/websocket/websocket.go index fdaebad356..4cedbc765e 100644 --- a/pkg/protocols/websocket/websocket.go +++ b/pkg/protocols/websocket/websocket.go @@ -410,6 +410,7 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent Request: types.ToString(wrapped.InternalEvent["request"]), Response: types.ToString(wrapped.InternalEvent["response"]), TemplateEncoded: request.options.EncodeTemplate(), + Error: types.ToString(wrapped.InternalEvent["error"]), } return data } diff --git a/pkg/protocols/whois/whois.go b/pkg/protocols/whois/whois.go index a7c02a9740..ce0bafe56c 100644 --- a/pkg/protocols/whois/whois.go +++ b/pkg/protocols/whois/whois.go @@ -186,6 +186,7 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent Request: types.ToString(wrapped.InternalEvent["request"]), Response: types.ToString(wrapped.InternalEvent["response"]), TemplateEncoded: request.options.EncodeTemplate(), + Error: types.ToString(wrapped.InternalEvent["error"]), } return data } diff --git a/pkg/scan/scan_context.go b/pkg/scan/scan_context.go new file mode 100644 index 0000000000..f1f73804b8 --- /dev/null +++ b/pkg/scan/scan_context.go @@ -0,0 +1,70 @@ +package scan + +import ( + "context" + "strings" + + "github.com/projectdiscovery/nuclei/v3/pkg/output" + "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" +) + +type ScanContext struct { + context.Context + Input *contextargs.Context + errors []error + events []*output.InternalWrappedEvent + + OnError func(error) + OnResult func(e *output.InternalWrappedEvent) +} + +func NewScanContext(input *contextargs.Context) *ScanContext { + return &ScanContext{Input: input} +} + +func (s *ScanContext) GenerateResult() []*output.ResultEvent { + return aggregateResults(s.events) +} + +func aggregateResults(events []*output.InternalWrappedEvent) []*output.ResultEvent { + var results []*output.ResultEvent + for _, e := range events { + results = append(results, e.Results...) + } + return results +} + +func joinErrors(errors []error) string { + var errorMessages []string + for _, e := range errors { + errorMessages = append(errorMessages, e.Error()) + } + return strings.Join(errorMessages, "; ") +} + +func (s *ScanContext) LogEvent(e *output.InternalWrappedEvent) { + if s.OnResult != nil { + s.OnResult(e) + } + s.events = append(s.events, e) +} + +func (s *ScanContext) LogError(err error) { + if err == nil { + return + } + + if s.OnError != nil { + s.OnError(err) + } + s.errors = append(s.errors, err) + + errorMessage := joinErrors(s.errors) + results := aggregateResults(s.events) + for _, result := range results { + result.Error = errorMessage + } + for _, e := range s.events { + e.InternalEvent["error"] = errorMessage + } +} diff --git a/pkg/templates/cluster.go b/pkg/templates/cluster.go index 219262e1cb..80507563bf 100644 --- a/pkg/templates/cluster.go +++ b/pkg/templates/cluster.go @@ -10,8 +10,8 @@ import ( "github.com/projectdiscovery/nuclei/v3/pkg/operators" "github.com/projectdiscovery/nuclei/v3/pkg/output" "github.com/projectdiscovery/nuclei/v3/pkg/protocols" - "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/helpers/writer" + "github.com/projectdiscovery/nuclei/v3/pkg/scan" "github.com/projectdiscovery/nuclei/v3/pkg/templates/types" cryptoutil "github.com/projectdiscovery/utils/crypto" mapsutil "github.com/projectdiscovery/utils/maps" @@ -240,12 +240,12 @@ func (e *ClusterExecuter) Requests() int { } // Execute executes the protocol group and returns true or false if results were found. -func (e *ClusterExecuter) Execute(input *contextargs.Context) (bool, error) { +func (e *ClusterExecuter) Execute(ctx *scan.ScanContext) (bool, error) { var results bool - inputItem := input.Clone() - if e.options.InputHelper != nil && input.MetaInput.Input != "" { - if inputItem.MetaInput.Input = e.options.InputHelper.Transform(input.MetaInput.Input, e.templateType); input.MetaInput.Input == "" { + inputItem := ctx.Input.Clone() + if e.options.InputHelper != nil && ctx.Input.MetaInput.Input != "" { + if inputItem.MetaInput.Input = e.options.InputHelper.Transform(ctx.Input.MetaInput.Input, e.templateType); ctx.Input.MetaInput.Input == "" { return false, nil } } @@ -274,19 +274,20 @@ func (e *ClusterExecuter) Execute(input *contextargs.Context) (bool, error) { } }) if err != nil && e.options.HostErrorsCache != nil { - e.options.HostErrorsCache.MarkFailed(input.MetaInput.Input, err) + e.options.HostErrorsCache.MarkFailed(ctx.Input.MetaInput.Input, err) } return results, err } // ExecuteWithResults executes the protocol requests and returns results instead of writing them. -func (e *ClusterExecuter) ExecuteWithResults(input *contextargs.Context, callback protocols.OutputEventCallback) error { +func (e *ClusterExecuter) ExecuteWithResults(ctx *scan.ScanContext) ([]*output.ResultEvent, error) { + scanCtx := scan.NewScanContext(ctx.Input) dynamicValues := make(map[string]interface{}) - inputItem := input.Clone() - if e.options.InputHelper != nil && input.MetaInput.Input != "" { - if inputItem.MetaInput.Input = e.options.InputHelper.Transform(input.MetaInput.Input, e.templateType); input.MetaInput.Input == "" { - return nil + inputItem := ctx.Input.Clone() + if e.options.InputHelper != nil && ctx.Input.MetaInput.Input != "" { + if inputItem.MetaInput.Input = e.options.InputHelper.Transform(ctx.Input.MetaInput.Input, e.templateType); ctx.Input.MetaInput.Input == "" { + return nil, nil } } err := e.requests.ExecuteWithResults(inputItem, dynamicValues, nil, func(event *output.InternalWrappedEvent) { @@ -298,12 +299,16 @@ func (e *ClusterExecuter) ExecuteWithResults(input *contextargs.Context, callbac event.InternalEvent["template-path"] = operator.templatePath event.InternalEvent["template-info"] = operator.templateInfo event.Results = e.requests.MakeResultEvent(event) - callback(event) + scanCtx.LogEvent(event) } } }) + if err != nil { + ctx.LogError(err) + } + if err != nil && e.options.HostErrorsCache != nil { - e.options.HostErrorsCache.MarkFailed(input.MetaInput.Input, err) + e.options.HostErrorsCache.MarkFailed(ctx.Input.MetaInput.Input, err) } - return err + return scanCtx.GenerateResult(), err } diff --git a/pkg/testutils/testutils.go b/pkg/testutils/testutils.go index 3b916e5166..f212f935f7 100644 --- a/pkg/testutils/testutils.go +++ b/pkg/testutils/testutils.go @@ -180,6 +180,7 @@ func (m *MockOutputWriter) WriteFailure(wrappedEvent *output.InternalWrappedEven Timestamp: time.Now(), //FIXME: this is workaround to encode the template when no results were found TemplateEncoded: m.encodeTemplate(types.ToString(event["template-path"])), + Error: types.ToString(event["error"]), } return m.Write(data) } diff --git a/pkg/tmplexec/exec.go b/pkg/tmplexec/exec.go index 642906275b..45f5058779 100644 --- a/pkg/tmplexec/exec.go +++ b/pkg/tmplexec/exec.go @@ -12,6 +12,7 @@ import ( "github.com/projectdiscovery/nuclei/v3/pkg/protocols" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/helpers/writer" + "github.com/projectdiscovery/nuclei/v3/pkg/scan" "github.com/projectdiscovery/nuclei/v3/pkg/tmplexec/flow" "github.com/projectdiscovery/nuclei/v3/pkg/tmplexec/generic" "github.com/projectdiscovery/nuclei/v3/pkg/tmplexec/multiproto" @@ -93,12 +94,12 @@ func (e *TemplateExecuter) Requests() int { } // Execute executes the protocol group and returns true or false if results were found. -func (e *TemplateExecuter) Execute(input *contextargs.Context) (bool, error) { +func (e *TemplateExecuter) Execute(ctx *scan.ScanContext) (bool, error) { results := &atomic.Bool{} defer func() { // it is essential to remove template context of `Scan i.e template x input pair` // since it is of no use after scan is completed (regardless of success or failure) - e.options.RemoveTemplateCtx(input.MetaInput) + e.options.RemoveTemplateCtx(ctx.Input.MetaInput) }() var lastMatcherEvent *output.InternalWrappedEvent @@ -111,7 +112,7 @@ func (e *TemplateExecuter) Execute(input *contextargs.Context) (bool, error) { } } - cliExecutorCallback := func(event *output.InternalWrappedEvent) { + ctx.OnResult = func(event *output.InternalWrappedEvent) { if event == nil { // something went wrong return @@ -138,13 +139,13 @@ func (e *TemplateExecuter) Execute(input *contextargs.Context) (bool, error) { // so in compile step earlier we compile it to validate javascript syntax and other things // and while executing we create new instance of flow executor everytime if e.options.Flow != "" { - flowexec := flow.NewFlowExecutor(e.requests, input, e.options, results) + flowexec := flow.NewFlowExecutor(e.requests, ctx.Input, e.options, results) if err := flowexec.Compile(); err != nil { return false, err } - err = flowexec.ExecuteWithResults(input, cliExecutorCallback) + err = flowexec.ExecuteWithResults(ctx) } else { - err = e.engine.ExecuteWithResults(input, cliExecutorCallback) + err = e.engine.ExecuteWithResults(ctx) } if lastMatcherEvent != nil { @@ -154,11 +155,8 @@ func (e *TemplateExecuter) Execute(input *contextargs.Context) (bool, error) { } // ExecuteWithResults executes the protocol requests and returns results instead of writing them. -func (e *TemplateExecuter) ExecuteWithResults(input *contextargs.Context, callback protocols.OutputEventCallback) error { - userCallback := func(event *output.InternalWrappedEvent) { - if event != nil { - callback(event) - } - } - return e.engine.ExecuteWithResults(input, userCallback) +func (e *TemplateExecuter) ExecuteWithResults(ctx *scan.ScanContext) ([]*output.ResultEvent, error) { + err := e.engine.ExecuteWithResults(ctx) + ctx.LogError(err) + return ctx.GenerateResult(), err } diff --git a/pkg/tmplexec/flow/flow_executor.go b/pkg/tmplexec/flow/flow_executor.go index 04394744c0..690a4be991 100644 --- a/pkg/tmplexec/flow/flow_executor.go +++ b/pkg/tmplexec/flow/flow_executor.go @@ -14,6 +14,7 @@ import ( "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate" + "github.com/projectdiscovery/nuclei/v3/pkg/scan" templateTypes "github.com/projectdiscovery/nuclei/v3/pkg/templates/types" "github.com/projectdiscovery/nuclei/v3/pkg/types" @@ -161,15 +162,15 @@ func (f *FlowExecutor) Compile() error { } // ExecuteWithResults executes the flow and returns results -func (f *FlowExecutor) ExecuteWithResults(input *contextargs.Context, callback protocols.OutputEventCallback) error { +func (f *FlowExecutor) ExecuteWithResults(ctx *scan.ScanContext) error { defer func() { if e := recover(); e != nil { - gologger.Error().Label(f.options.TemplateID).Msgf("panic occurred while executing target %v with flow: %v", input.MetaInput.Input, e) + gologger.Error().Label(f.options.TemplateID).Msgf("panic occurred while executing target %v with flow: %v", ctx.Input.MetaInput.Input, e) panic(e) } }() - f.input = input + f.input = ctx.Input // -----Load all types of variables----- // add all input args to template context if f.input != nil && f.input.HasArgs() { @@ -177,20 +178,22 @@ func (f *FlowExecutor) ExecuteWithResults(input *contextargs.Context, callback p f.options.GetTemplateCtx(f.input.MetaInput).Set(key, value) }) } - if callback == nil { + if ctx.OnResult == nil { return fmt.Errorf("output callback cannot be nil") } // pass flow and execute the js vm and handle errors value, err := f.jsVM.RunProgram(f.program) if err != nil { + ctx.LogError(err) return errorutil.NewWithErr(err).Msgf("failed to execute flow\n%v\n", f.options.Flow) } runtimeErr := f.GetRuntimeErrors() if runtimeErr != nil { + ctx.LogError(runtimeErr) return errorutil.NewWithErr(runtimeErr).Msgf("got following errors while executing flow") } // this is where final result is generated/created - callback(f.lastEvent) + ctx.LogEvent(f.lastEvent) if value.Export() != nil { f.results.Store(value.ToBoolean()) } else { diff --git a/pkg/tmplexec/flow/flow_executor_test.go b/pkg/tmplexec/flow/flow_executor_test.go index cb67c59fc2..e2053b423d 100644 --- a/pkg/tmplexec/flow/flow_executor_test.go +++ b/pkg/tmplexec/flow/flow_executor_test.go @@ -12,6 +12,7 @@ import ( "github.com/projectdiscovery/nuclei/v3/pkg/progress" "github.com/projectdiscovery/nuclei/v3/pkg/protocols" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" + "github.com/projectdiscovery/nuclei/v3/pkg/scan" "github.com/projectdiscovery/nuclei/v3/pkg/templates" "github.com/projectdiscovery/nuclei/v3/pkg/testutils" "github.com/projectdiscovery/ratelimit" @@ -54,7 +55,8 @@ func TestFlowTemplateWithIndex(t *testing.T) { require.Nil(t, err, "could not compile template") input := contextargs.NewWithInput("hackerone.com") - gotresults, err := Template.Executer.Execute(input) + ctx := scan.NewScanContext(input) + gotresults, err := Template.Executer.Execute(ctx) require.Nil(t, err, "could not execute template") require.True(t, gotresults) } @@ -72,7 +74,8 @@ func TestFlowTemplateWithID(t *testing.T) { require.Nil(t, err, "could not compile template") target := contextargs.NewWithInput("hackerone.com") - gotresults, err := Template.Executer.Execute(target) + ctx := scan.NewScanContext(target) + gotresults, err := Template.Executer.Execute(ctx) require.Nil(t, err, "could not execute template") require.True(t, gotresults) } @@ -93,7 +96,8 @@ func TestFlowWithProtoPrefix(t *testing.T) { require.Nil(t, err, "could not compile template") input := contextargs.NewWithInput("hackerone.com") - gotresults, err := Template.Executer.Execute(input) + ctx := scan.NewScanContext(input) + gotresults, err := Template.Executer.Execute(ctx) require.Nil(t, err, "could not execute template") require.True(t, gotresults) } @@ -112,8 +116,9 @@ func TestFlowWithConditionNegative(t *testing.T) { require.Nil(t, err, "could not compile template") input := contextargs.NewWithInput("scanme.sh") + ctx := scan.NewScanContext(input) // expect no results and verify thant dns request is executed and http is not - gotresults, err := Template.Executer.Execute(input) + gotresults, err := Template.Executer.Execute(ctx) require.Nil(t, err, "could not execute template") require.False(t, gotresults) } @@ -132,8 +137,9 @@ func TestFlowWithConditionPositive(t *testing.T) { require.Nil(t, err, "could not compile template") input := contextargs.NewWithInput("blog.projectdiscovery.io") + ctx := scan.NewScanContext(input) // positive match . expect results also verify that both dns() and http() were executed - gotresults, err := Template.Executer.Execute(input) + gotresults, err := Template.Executer.Execute(ctx) require.Nil(t, err, "could not execute template") require.True(t, gotresults) } @@ -151,8 +157,10 @@ func TestFlowWithNoMatchers(t *testing.T) { err = Template.Executer.Compile() require.Nil(t, err, "could not compile template") + input := contextargs.NewWithInput("blog.projectdiscovery.io") + ctx := scan.NewScanContext(input) // positive match . expect results also verify that both dns() and http() were executed - gotresults, err := Template.Executer.Execute(contextargs.NewWithInput("blog.projectdiscovery.io")) + gotresults, err := Template.Executer.Execute(ctx) require.Nil(t, err, "could not execute template") require.True(t, gotresults) @@ -165,8 +173,10 @@ func TestFlowWithNoMatchers(t *testing.T) { err = Template.Executer.Compile() require.Nil(t, err, "could not compile template") + anotherInput := contextargs.NewWithInput("blog.projectdiscovery.io") + anotherCtx := scan.NewScanContext(anotherInput) // positive match . expect results also verify that both dns() and http() were executed - gotresults, err = Template.Executer.Execute(contextargs.NewWithInput("blog.projectdiscovery.io")) + gotresults, err = Template.Executer.Execute(anotherCtx) require.Nil(t, err, "could not execute template") require.True(t, gotresults) diff --git a/pkg/tmplexec/generic/exec.go b/pkg/tmplexec/generic/exec.go index 022661cef1..c5d25795cf 100644 --- a/pkg/tmplexec/generic/exec.go +++ b/pkg/tmplexec/generic/exec.go @@ -7,7 +7,7 @@ import ( "github.com/projectdiscovery/gologger" "github.com/projectdiscovery/nuclei/v3/pkg/output" "github.com/projectdiscovery/nuclei/v3/pkg/protocols" - "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" + "github.com/projectdiscovery/nuclei/v3/pkg/scan" ) // generic engine as name suggests is a generic template @@ -34,18 +34,18 @@ func (g *Generic) Compile() error { } // ExecuteWithResults executes the template and returns results -func (g *Generic) ExecuteWithResults(input *contextargs.Context, callback protocols.OutputEventCallback) error { +func (g *Generic) ExecuteWithResults(ctx *scan.ScanContext) error { dynamicValues := make(map[string]interface{}) - if input.HasArgs() { - input.ForEach(func(key string, value interface{}) { + if ctx.Input.HasArgs() { + ctx.Input.ForEach(func(key string, value interface{}) { dynamicValues[key] = value }) } previous := make(map[string]interface{}) for _, req := range g.requests { - inputItem := input.Clone() - if g.options.InputHelper != nil && input.MetaInput.Input != "" { + inputItem := ctx.Input.Clone() + if g.options.InputHelper != nil && ctx.Input.MetaInput.Input != "" { if inputItem.MetaInput.Input = g.options.InputHelper.Transform(inputItem.MetaInput.Input, req.Type()); inputItem.MetaInput.Input == "" { return nil } @@ -72,13 +72,14 @@ func (g *Generic) ExecuteWithResults(input *contextargs.Context, callback protoc } // for ExecuteWithResults : this callback will execute user defined callback and some error handling // for Execute : this callback will print the result to output - callback(event) + ctx.LogEvent(event) }) if err != nil { + ctx.LogError(err) if g.options.HostErrorsCache != nil { - g.options.HostErrorsCache.MarkFailed(input.MetaInput.ID(), err) + g.options.HostErrorsCache.MarkFailed(ctx.Input.MetaInput.ID(), err) } - gologger.Warning().Msgf("[%s] Could not execute request for %s: %s\n", g.options.TemplateID, input.MetaInput.PrettyPrint(), err) + gologger.Warning().Msgf("[%s] Could not execute request for %s: %s\n", g.options.TemplateID, ctx.Input.MetaInput.PrettyPrint(), err) } // If a match was found and stop at first match is set, break out of the loop and return if g.results.Load() && (g.options.StopAtFirstMatch || g.options.Options.StopAtFirstMatch) { diff --git a/pkg/tmplexec/interface.go b/pkg/tmplexec/interface.go index d813e5bd2d..67f9621116 100644 --- a/pkg/tmplexec/interface.go +++ b/pkg/tmplexec/interface.go @@ -1,8 +1,7 @@ package tmplexec import ( - "github.com/projectdiscovery/nuclei/v3/pkg/protocols" - "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" + "github.com/projectdiscovery/nuclei/v3/pkg/scan" "github.com/projectdiscovery/nuclei/v3/pkg/tmplexec/flow" "github.com/projectdiscovery/nuclei/v3/pkg/tmplexec/generic" "github.com/projectdiscovery/nuclei/v3/pkg/tmplexec/multiproto" @@ -26,7 +25,7 @@ type TemplateEngine interface { Compile() error // ExecuteWithResults executes the template and returns results - ExecuteWithResults(input *contextargs.Context, callback protocols.OutputEventCallback) error + ExecuteWithResults(ctx *scan.ScanContext) error // Name returns name of template engine Name() string diff --git a/pkg/tmplexec/multiproto/multi.go b/pkg/tmplexec/multiproto/multi.go index e9db400277..021c83534d 100644 --- a/pkg/tmplexec/multiproto/multi.go +++ b/pkg/tmplexec/multiproto/multi.go @@ -6,8 +6,8 @@ import ( "github.com/projectdiscovery/nuclei/v3/pkg/output" "github.com/projectdiscovery/nuclei/v3/pkg/protocols" - "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators" + "github.com/projectdiscovery/nuclei/v3/pkg/scan" ) // Mutliprotocol is a template executer engine that executes multiple protocols @@ -43,9 +43,9 @@ func (m *MultiProtocol) Compile() error { } // ExecuteWithResults executes the template and returns results -func (m *MultiProtocol) ExecuteWithResults(input *contextargs.Context, callback protocols.OutputEventCallback) error { +func (m *MultiProtocol) ExecuteWithResults(ctx *scan.ScanContext) error { // put all readonly args into template context - m.options.GetTemplateCtx(input.MetaInput).Merge(m.readOnlyArgs) + m.options.GetTemplateCtx(ctx.Input.MetaInput).Merge(m.readOnlyArgs) var finalProtoEvent *output.InternalWrappedEvent // callback to process results from all protocols multiProtoCallback := func(event *output.InternalWrappedEvent) { @@ -59,7 +59,7 @@ func (m *MultiProtocol) ExecuteWithResults(input *contextargs.Context, callback // we either need to add support for iterate-all in other protocols or implement a different logic (specific to template context) // currently if dynamic value array only contains one value we replace it with the value if len(v) == 1 { - m.options.GetTemplateCtx(input.MetaInput).Set(k, v[0]) + m.options.GetTemplateCtx(ctx.Input.MetaInput).Set(k, v[0]) } else { // Note: if extracted value contains multiple values then they can be accessed by indexing // ex: if values are dynamic = []string{"a","b","c"} then they are available as @@ -67,9 +67,9 @@ func (m *MultiProtocol) ExecuteWithResults(input *contextargs.Context, callback // we intentionally omit first index for unknown situations (where no of extracted values are not known) for i, val := range v { if i == 0 { - m.options.GetTemplateCtx(input.MetaInput).Set(k, val) + m.options.GetTemplateCtx(ctx.Input.MetaInput).Set(k, val) } else { - m.options.GetTemplateCtx(input.MetaInput).Set(k+strconv.Itoa(i), val) + m.options.GetTemplateCtx(ctx.Input.MetaInput).Set(k+strconv.Itoa(i), val) } } } @@ -77,8 +77,8 @@ func (m *MultiProtocol) ExecuteWithResults(input *contextargs.Context, callback } // evaluate all variables after execution of each protocol - variableMap := m.options.Variables.Evaluate(m.options.GetTemplateCtx(input.MetaInput).GetAll()) - m.options.GetTemplateCtx(input.MetaInput).Merge(variableMap) // merge all variables into template context + variableMap := m.options.Variables.Evaluate(m.options.GetTemplateCtx(ctx.Input.MetaInput).GetAll()) + m.options.GetTemplateCtx(ctx.Input.MetaInput).Merge(variableMap) // merge all variables into template context } // template context: contains values extracted using `internal` extractor from previous protocols @@ -89,10 +89,11 @@ func (m *MultiProtocol) ExecuteWithResults(input *contextargs.Context, callback // execute all protocols in the queue for _, req := range m.requests { - values := m.options.GetTemplateCtx(input.MetaInput).GetAll() - err := req.ExecuteWithResults(input, output.InternalEvent(values), nil, multiProtoCallback) + values := m.options.GetTemplateCtx(ctx.Input.MetaInput).GetAll() + err := req.ExecuteWithResults(ctx.Input, output.InternalEvent(values), nil, multiProtoCallback) // if error skip execution of next protocols if err != nil { + ctx.LogError(err) return err } } @@ -100,7 +101,7 @@ func (m *MultiProtocol) ExecuteWithResults(input *contextargs.Context, callback // currently the outer callback is only executed once (for the last protocol in queue) // due to workflow logic at https://github.com/projectdiscovery/nuclei/blob/main/pkg/protocols/common/executer/executem.go#L150 // this causes addition of duplicated / unncessary variables with prefix template_id_all_variables - callback(finalProtoEvent) + ctx.LogEvent(finalProtoEvent) return nil } diff --git a/pkg/tmplexec/multiproto/multi_test.go b/pkg/tmplexec/multiproto/multi_test.go index 5dd1d65d42..cd260a049e 100644 --- a/pkg/tmplexec/multiproto/multi_test.go +++ b/pkg/tmplexec/multiproto/multi_test.go @@ -12,6 +12,7 @@ import ( "github.com/projectdiscovery/nuclei/v3/pkg/progress" "github.com/projectdiscovery/nuclei/v3/pkg/protocols" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs" + "github.com/projectdiscovery/nuclei/v3/pkg/scan" "github.com/projectdiscovery/nuclei/v3/pkg/templates" "github.com/projectdiscovery/nuclei/v3/pkg/testutils" "github.com/projectdiscovery/ratelimit" @@ -52,7 +53,9 @@ func TestMultiProtoWithDynamicExtractor(t *testing.T) { err = Template.Executer.Compile() require.Nil(t, err, "could not compile template") - gotresults, err := Template.Executer.Execute(contextargs.NewWithInput("blog.projectdiscovery.io")) + input := contextargs.NewWithInput("blog.projectdiscovery.io") + ctx := scan.NewScanContext(input) + gotresults, err := Template.Executer.Execute(ctx) require.Nil(t, err, "could not execute template") require.True(t, gotresults) } @@ -67,7 +70,9 @@ func TestMultiProtoWithProtoPrefix(t *testing.T) { err = Template.Executer.Compile() require.Nil(t, err, "could not compile template") - gotresults, err := Template.Executer.Execute(contextargs.NewWithInput("blog.projectdiscovery.io")) + input := contextargs.NewWithInput("blog.projectdiscovery.io") + ctx := scan.NewScanContext(input) + gotresults, err := Template.Executer.Execute(ctx) require.Nil(t, err, "could not execute template") require.True(t, gotresults) }