diff --git a/cmd/nuclei/main.go b/cmd/nuclei/main.go index 3caec21c..b6ecd1e4 100644 --- a/cmd/nuclei/main.go +++ b/cmd/nuclei/main.go @@ -15,6 +15,7 @@ import ( "github.com/projectdiscovery/utils/auth/pdcp" "github.com/projectdiscovery/utils/env" _ "github.com/projectdiscovery/utils/pprof" + stringsutil "github.com/projectdiscovery/utils/strings" "github.com/projectdiscovery/goflags" "github.com/projectdiscovery/gologger" @@ -329,7 +330,8 @@ on extensive configurability, massive extensibility and ease of use.`) flagSet.CreateGroup("rate-limit", "Rate-Limit", flagSet.IntVarP(&options.RateLimit, "rate-limit", "rl", 150, "maximum number of requests to send per second"), - flagSet.IntVarP(&options.RateLimitMinute, "rate-limit-minute", "rlm", 0, "maximum number of requests to send per minute"), + flagSet.DurationVarP(&options.RateLimitDuration, "rate-limit-duration", "rld", time.Second, "maximum number of requests to send per second"), + flagSet.IntVarP(&options.RateLimitMinute, "rate-limit-minute", "rlm", 0, "maximum number of requests to send per minute (DEPRECATED)"), flagSet.IntVarP(&options.BulkSize, "bulk-size", "bs", 25, "maximum number of hosts to be analyzed in parallel per template"), flagSet.IntVarP(&options.TemplateThreads, "concurrency", "c", 25, "maximum number of templates to be executed in parallel"), flagSet.IntVarP(&options.HeadlessBulkSize, "headless-bulk-size", "hbs", 10, "maximum number of headless hosts to be analyzed in parallel per template"), @@ -597,10 +599,10 @@ Note: Make sure you have backup of your custom nuclei-templates before proceedin gologger.Fatal().Msgf("could not read response: %s", err) } resp = strings.TrimSpace(resp) - if strings.EqualFold(resp, "y") || strings.EqualFold(resp, "yes") { + if stringsutil.EqualFoldAny(resp, "y", "yes") { break } - if strings.EqualFold(resp, "n") || strings.EqualFold(resp, "no") || resp == "" { + if stringsutil.EqualFoldAny(resp, "n", "no", "") { fmt.Println("Exiting...") os.Exit(0) } diff --git a/internal/runner/runner.go b/internal/runner/runner.go index 3c6aaf0b..23443647 100644 --- a/internal/runner/runner.go +++ b/internal/runner/runner.go @@ -314,11 +314,17 @@ func New(options *types.Options) (*Runner, error) { } if options.RateLimitMinute > 0 { - runner.rateLimiter = ratelimit.New(context.Background(), uint(options.RateLimitMinute), time.Minute) - } else if options.RateLimit > 0 { - runner.rateLimiter = ratelimit.New(context.Background(), uint(options.RateLimit), time.Second) - } else { + gologger.Warning().Msgf("rate limit per minute is deprecated - use rate-limit-duration") + options.RateLimit = options.RateLimitMinute + options.RateLimitDuration = time.Minute + } + if options.RateLimit > 0 && options.RateLimitDuration == 0 { + options.RateLimitDuration = time.Second + } + if options.RateLimit == 0 && options.RateLimitDuration == 0 { runner.rateLimiter = ratelimit.NewUnlimited(context.Background()) + } else { + runner.rateLimiter = ratelimit.New(context.Background(), uint(options.RateLimit), options.RateLimitDuration) } if tmpDir, err := os.MkdirTemp("", "nuclei-tmp-*"); err == nil { diff --git a/lib/multi.go b/lib/multi.go index 3a573b16..6fa791a2 100644 --- a/lib/multi.go +++ b/lib/multi.go @@ -42,11 +42,16 @@ func createEphemeralObjects(base *NucleiEngine, opts *types.Options) (*unsafeOpt Parser: base.parser, } if opts.RateLimitMinute > 0 { - u.executerOpts.RateLimiter = ratelimit.New(context.Background(), uint(opts.RateLimitMinute), time.Minute) - } else if opts.RateLimit > 0 { - u.executerOpts.RateLimiter = ratelimit.New(context.Background(), uint(opts.RateLimit), time.Second) - } else { + opts.RateLimit = opts.RateLimitMinute + opts.RateLimitDuration = time.Minute + } + if opts.RateLimit > 0 && opts.RateLimitDuration == 0 { + opts.RateLimitDuration = time.Second + } + if opts.RateLimit == 0 && opts.RateLimitDuration == 0 { u.executerOpts.RateLimiter = ratelimit.NewUnlimited(context.Background()) + } else { + u.executerOpts.RateLimiter = ratelimit.New(context.Background(), uint(opts.RateLimit), opts.RateLimitDuration) } u.engine = core.New(opts) u.engine.SetExecuterOptions(u.executerOpts) diff --git a/lib/sdk_private.go b/lib/sdk_private.go index bc66d53f..c76970c9 100644 --- a/lib/sdk_private.go +++ b/lib/sdk_private.go @@ -192,11 +192,16 @@ func (e *NucleiEngine) init() error { if e.executerOpts.RateLimiter == nil { if e.opts.RateLimitMinute > 0 { - e.executerOpts.RateLimiter = ratelimit.New(context.Background(), uint(e.opts.RateLimitMinute), time.Minute) - } else if e.opts.RateLimit > 0 { - e.executerOpts.RateLimiter = ratelimit.New(context.Background(), uint(e.opts.RateLimit), time.Second) - } else { + e.opts.RateLimit = e.opts.RateLimitMinute + e.opts.RateLimitDuration = time.Minute + } + if e.opts.RateLimit > 0 && e.opts.RateLimitDuration == 0 { + e.opts.RateLimitDuration = time.Second + } + if e.opts.RateLimit == 0 && e.opts.RateLimitDuration == 0 { e.executerOpts.RateLimiter = ratelimit.NewUnlimited(context.Background()) + } else { + e.executerOpts.RateLimiter = ratelimit.New(context.Background(), uint(e.opts.RateLimit), e.opts.RateLimitDuration) } } diff --git a/pkg/testutils/testutils.go b/pkg/testutils/testutils.go index 96d68e1d..e59aa015 100644 --- a/pkg/testutils/testutils.go +++ b/pkg/testutils/testutils.go @@ -54,6 +54,7 @@ var DefaultOptions = &types.Options{ Timeout: 5, Retries: 1, RateLimit: 150, + RateLimitDuration: time.Second, ProjectPath: "", Severities: severity.Severities{}, Targets: []string{}, diff --git a/pkg/types/types.go b/pkg/types/types.go index ce783809..34f3c3be 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -132,7 +132,10 @@ type Options struct { Retries int // Rate-Limit is the maximum number of requests per specified target RateLimit int + // Rate Limit Duration interval between burst resets + RateLimitDuration time.Duration // Rate-Limit is the maximum number of requests per minute for specified target + // Deprecated: Use RateLimitDuration - automatically set Rate Limit Duration to 60 seconds RateLimitMinute int // PageTimeout is the maximum time to wait for a page in seconds PageTimeout int @@ -410,6 +413,7 @@ func (options *Options) HasClientCertificates() bool { func DefaultOptions() *Options { return &Options{ RateLimit: 150, + RateLimitDuration: time.Second, BulkSize: 25, TemplateThreads: 25, HeadlessBulkSize: 10,