diff --git a/v2/internal/runner/options.go b/v2/internal/runner/options.go index 4918f002..03632313 100644 --- a/v2/internal/runner/options.go +++ b/v2/internal/runner/options.go @@ -39,6 +39,7 @@ type Options struct { CustomHeaders requests.CustomHeaders // Custom global headers TemplatesDirectory string // TemplatesDirectory is the directory to use for storing templates RateLimit int // Rate-Limit of requests per specified target + StopAtFirstMatch bool // Stop processing template at first full match (this may break chained requests) } type multiStringFlag []string @@ -80,6 +81,7 @@ func ParseOptions() *Options { flag.BoolVar(&options.EnableProgressBar, "pbar", false, "Enable the progress bar") flag.BoolVar(&options.TemplateList, "tl", false, "List available templates") flag.IntVar(&options.RateLimit, "rl", 9999999, "Rate-Limit of requests per specified target") // 9999999 to avoid limiting + flag.BoolVar(&options.StopAtFirstMatch, "stop-at-first-match", false, "Stop processing http requests at first match (this may break template/workflow logic)") flag.Parse() diff --git a/v2/internal/runner/processor.go b/v2/internal/runner/processor.go index d62a4f01..2b16d46d 100644 --- a/v2/internal/runner/processor.go +++ b/v2/internal/runner/processor.go @@ -51,21 +51,22 @@ func (r *Runner) processTemplateWithList(ctx context.Context, p progress.IProgre }) case *requests.BulkHTTPRequest: httpExecuter, err = executer.NewHTTPExecuter(&executer.HTTPOptions{ - Debug: r.options.Debug, - Template: template, - BulkHTTPRequest: value, - Writer: r.output, - Timeout: r.options.Timeout, - Retries: r.options.Retries, - ProxyURL: r.options.ProxyURL, - ProxySocksURL: r.options.ProxySocksURL, - CustomHeaders: r.options.CustomHeaders, - JSON: r.options.JSON, - JSONRequests: r.options.JSONRequests, - CookieReuse: value.CookieReuse, - ColoredOutput: !r.options.NoColor, - Colorizer: &r.colorizer, - Decolorizer: r.decolorizer, + Debug: r.options.Debug, + Template: template, + BulkHTTPRequest: value, + Writer: r.output, + Timeout: r.options.Timeout, + Retries: r.options.Retries, + ProxyURL: r.options.ProxyURL, + ProxySocksURL: r.options.ProxySocksURL, + CustomHeaders: r.options.CustomHeaders, + JSON: r.options.JSON, + JSONRequests: r.options.JSONRequests, + CookieReuse: value.CookieReuse, + ColoredOutput: !r.options.NoColor, + Colorizer: &r.colorizer, + Decolorizer: r.decolorizer, + StopAtFirstMatch: r.options.StopAtFirstMatch, }) } diff --git a/v2/pkg/executer/executer_http.go b/v2/pkg/executer/executer_http.go index 3bd5f216..dedb931f 100644 --- a/v2/pkg/executer/executer_http.go +++ b/v2/pkg/executer/executer_http.go @@ -50,28 +50,30 @@ type HTTPExecuter struct { customHeaders requests.CustomHeaders CookieJar *cookiejar.Jar - colorizer colorizer.NucleiColorizer - decolorizer *regexp.Regexp + colorizer colorizer.NucleiColorizer + decolorizer *regexp.Regexp + stopAtFirstMatch bool } // HTTPOptions contains configuration options for the HTTP executer. type HTTPOptions struct { - Debug bool - JSON bool - JSONRequests bool - CookieReuse bool - ColoredOutput bool - Template *templates.Template - BulkHTTPRequest *requests.BulkHTTPRequest - Writer *bufwriter.Writer - Timeout int - Retries int - ProxyURL string - ProxySocksURL string - CustomHeaders requests.CustomHeaders - CookieJar *cookiejar.Jar - Colorizer *colorizer.NucleiColorizer - Decolorizer *regexp.Regexp + Debug bool + JSON bool + JSONRequests bool + CookieReuse bool + ColoredOutput bool + Template *templates.Template + BulkHTTPRequest *requests.BulkHTTPRequest + Writer *bufwriter.Writer + Timeout int + Retries int + ProxyURL string + ProxySocksURL string + CustomHeaders requests.CustomHeaders + CookieJar *cookiejar.Jar + Colorizer *colorizer.NucleiColorizer + Decolorizer *regexp.Regexp + StopAtFirstMatch bool } // NewHTTPExecuter creates a new HTTP executer from a template @@ -108,19 +110,20 @@ func NewHTTPExecuter(options *HTTPOptions) (*HTTPExecuter, error) { rawClient := rawhttp.NewClient(rawhttp.DefaultOptions) executer := &HTTPExecuter{ - debug: options.Debug, - jsonOutput: options.JSON, - jsonRequest: options.JSONRequests, - httpClient: client, - rawHttpClient: rawClient, - template: options.Template, - bulkHTTPRequest: options.BulkHTTPRequest, - writer: options.Writer, - customHeaders: options.CustomHeaders, - CookieJar: options.CookieJar, - coloredOutput: options.ColoredOutput, - colorizer: *options.Colorizer, - decolorizer: options.Decolorizer, + debug: options.Debug, + jsonOutput: options.JSON, + jsonRequest: options.JSONRequests, + httpClient: client, + rawHttpClient: rawClient, + template: options.Template, + bulkHTTPRequest: options.BulkHTTPRequest, + writer: options.Writer, + customHeaders: options.CustomHeaders, + CookieJar: options.CookieJar, + coloredOutput: options.ColoredOutput, + colorizer: *options.Colorizer, + decolorizer: options.Decolorizer, + stopAtFirstMatch: options.StopAtFirstMatch, } return executer, nil @@ -154,6 +157,12 @@ func (e *HTTPExecuter) ExecuteHTTP(ctx context.Context, p progress.IProgress, re } } + // Check if has to stop processing at first valid result + if e.stopAtFirstMatch && result.GotResults { + p.Drop(remaining) + break + } + // move always forward with requests e.bulkHTTPRequest.Increment(reqURL) p.Update()