Skip to content

Commit

Permalink
introduce timeout variants
Browse files Browse the repository at this point in the history
  • Loading branch information
dogancanbakir committed May 27, 2024
1 parent 4ae0b39 commit b3eb134
Show file tree
Hide file tree
Showing 15 changed files with 95 additions and 70 deletions.
1 change: 1 addition & 0 deletions cmd/integration-test/library.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ func executeNucleiAsLibrary(templatePath, templateURL string) ([]string, error)
Colorizer: aurora.NewAurora(true),
ResumeCfg: types.NewResumeCfg(),
Parser: templates.NewParser(),
TimeoutVariants: defaultOpts.BuildTimeoutVariants(),
}
engine := core.New(defaultOpts)
engine.SetExecuterOptions(executerOpts)
Expand Down
2 changes: 0 additions & 2 deletions cmd/nuclei/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,6 @@ on extensive configurability, massive extensibility and ease of use.`)
flagSet.BoolVarP(&options.ShowMatchLine, "show-match-line", "sml", false, "show match lines for file templates, works with extractors only"),
flagSet.BoolVar(&options.ZTLS, "ztls", false, "use ztls library with autofallback to standard one for tls13 [Deprecated] autofallback to ztls is enabled by default"), //nolint:all
flagSet.StringVar(&options.SNI, "sni", "", "tls sni hostname to use (default: input domain name)"),
flagSet.DurationVarP(&options.DialerTimeout, "dialer-timeout", "dt", 0, "timeout for network requests."),
flagSet.DurationVarP(&options.DialerKeepAlive, "dialer-keep-alive", "dka", 0, "keep-alive duration for network requests."),
flagSet.BoolVarP(&options.AllowLocalFileAccess, "allow-local-file-access", "lfa", false, "allows file (payload) access anywhere on the system"),
flagSet.BoolVarP(&options.RestrictLocalNetworkAccess, "restrict-local-network-access", "lna", false, "blocks connections to the local / private network"),
Expand All @@ -305,7 +304,6 @@ on extensive configurability, massive extensibility and ease of use.`)
flagSet.StringVarP(&options.SourceIP, "source-ip", "sip", "", "source ip address to use for network scan"),
flagSet.IntVarP(&options.ResponseReadSize, "response-size-read", "rsr", 0, "max response size to read in bytes"),
flagSet.IntVarP(&options.ResponseSaveSize, "response-size-save", "rss", 1*1024*1024, "max response size to read in bytes"),
flagSet.DurationVarP(&options.ResponseReadTimeout, "response-read-timeout", "rrt", time.Duration(5*time.Second), "response read timeout in seconds"),
flagSet.CallbackVar(resetCallback, "reset", "reset removes all nuclei configuration and data files (including nuclei-templates)"),
flagSet.BoolVarP(&options.TlsImpersonate, "tls-impersonate", "tlsi", false, "enable experimental client hello (ja3) tls randomization"),
flagSet.StringVarP(&options.HttpApiEndpoint, "http-api-endpoint", "hae", "", "experimental http api endpoint"),
Expand Down
1 change: 1 addition & 0 deletions internal/runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,7 @@ func (r *Runner) RunEnumeration() error {
InputHelper: input.NewHelper(),
TemporaryDirectory: r.tmpDir,
Parser: r.parser,
TimeoutVariants: r.options.BuildTimeoutVariants(),
}

if config.DefaultConfig.IsDebugArgEnabled(config.DebugExportURLPattern) {
Expand Down
1 change: 1 addition & 0 deletions lib/multi.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func createEphemeralObjects(base *NucleiEngine, opts *types.Options) (*unsafeOpt
Colorizer: aurora.NewAurora(true),
ResumeCfg: types.NewResumeCfg(),
Parser: base.parser,
TimeoutVariants: opts.BuildTimeoutVariants(),
}
if opts.RateLimitMinute > 0 {
opts.RateLimit = opts.RateLimitMinute
Expand Down
1 change: 1 addition & 0 deletions lib/sdk_private.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ func (e *NucleiEngine) init() error {
ResumeCfg: types.NewResumeCfg(),
Browser: e.browserInstance,
Parser: e.parser,
TimeoutVariants: e.opts.BuildTimeoutVariants(),
}
if len(e.opts.SecretsFile) > 0 {
authTmplStore, err := runner.GetAuthTmplStore(*e.opts, e.catalog, e.executerOpts)
Expand Down
5 changes: 3 additions & 2 deletions pkg/protocols/common/protocolstate/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ func Init(options *types.Options) error {

lfaAllowed = options.AllowLocalFileAccess
opts := fastdialer.DefaultOptions
if options.DialerTimeout > 0 {
opts.DialerTimeout = options.DialerTimeout
timeoutVariants := options.BuildTimeoutVariants()
if timeoutVariants.DialTimeout > 0 {
opts.DialerTimeout = timeoutVariants.DialTimeout
}
if options.DialerKeepAlive > 0 {
opts.DialerKeepAlive = options.DialerKeepAlive
Expand Down
14 changes: 3 additions & 11 deletions pkg/protocols/http/httpclientpool/clientpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,11 @@ var (
forceMaxRedirects int
normalClient *retryablehttp.Client
clientPool *mapsutil.SyncLockMap[string, *retryablehttp.Client]
// MaxResponseHeaderTimeout is the timeout for response headers
// to be read from the server (this prevents infinite hang started by server if any)
// Note: this will be overridden temporarily when using @timeout request annotation
MaxResponseHeaderTimeout = time.Duration(10) * time.Second
// HttpTimeoutMultiplier is the multiplier for the http timeout
HttpTimeoutMultiplier = 3
)

// GetHttpTimeout returns the http timeout for the client
func GetHttpTimeout(opts *types.Options) time.Duration {
return time.Duration(opts.Timeout*HttpTimeoutMultiplier) * time.Second
return opts.BuildTimeoutVariants().HttpTimeout
}

// Init initializes the clientpool implementation
Expand All @@ -54,9 +48,6 @@ func Init(options *types.Options) error {
if normalClient != nil {
return nil
}
if options.Timeout > 10 {
MaxResponseHeaderTimeout = time.Duration(options.Timeout) * time.Second
}
if options.ShouldFollowHTTPRedirects() {
forceMaxRedirects = options.MaxRedirects
}
Expand Down Expand Up @@ -247,7 +238,8 @@ func wrappedGet(options *types.Options, configuration *Configuration) (*retryabl
}

// responseHeaderTimeout is max timeout for response headers to be read
responseHeaderTimeout := MaxResponseHeaderTimeout
timeoutVariants := options.BuildTimeoutVariants()
responseHeaderTimeout := timeoutVariants.MaxResponseHeaderTimeout
if configuration.ResponseHeaderTimeout != 0 {
responseHeaderTimeout = configuration.ResponseHeaderTimeout
}
Expand Down
19 changes: 10 additions & 9 deletions pkg/protocols/javascript/js_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,16 @@ func setup() {
progressImpl, _ := progress.NewStatsTicker(0, false, false, false, 0)

executerOpts = protocols.ExecutorOptions{
Output: testutils.NewMockOutputWriter(options.OmitTemplate),
Options: options,
Progress: progressImpl,
ProjectFile: nil,
IssuesClient: nil,
Browser: nil,
Catalog: disk.NewCatalog(config.DefaultConfig.TemplatesDirectory),
RateLimiter: ratelimit.New(context.Background(), uint(options.RateLimit), time.Second),
Parser: templates.NewParser(),
Output: testutils.NewMockOutputWriter(options.OmitTemplate),
Options: options,
Progress: progressImpl,
ProjectFile: nil,
IssuesClient: nil,
Browser: nil,
Catalog: disk.NewCatalog(config.DefaultConfig.TemplatesDirectory),
RateLimiter: ratelimit.New(context.Background(), uint(options.RateLimit), time.Second),
Parser: templates.NewParser(),
TimeoutVariants: options.BuildTimeoutVariants(),
}
workflowLoader, err := workflow.NewLoader(&executerOpts)
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions pkg/protocols/network/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ func (request *Request) executeRequestWithPayloads(variables map[string]interfac
}

if input.Read > 0 {
buffer, err := ConnReadNWithTimeout(conn, int64(input.Read), request.options.Options.ResponseReadTimeout)
buffer, err := ConnReadNWithTimeout(conn, int64(input.Read), request.options.TimeoutVariants.ResponseReadTimeout)
if err != nil {
return errorutil.NewWithErr(err).Msgf("could not read response from connection")
}
Expand Down Expand Up @@ -377,7 +377,7 @@ func (request *Request) executeRequestWithPayloads(variables map[string]interfac
bufferSize = -1
}

final, err := ConnReadNWithTimeout(conn, int64(bufferSize), request.options.Options.ResponseReadTimeout)
final, err := ConnReadNWithTimeout(conn, int64(bufferSize), request.options.TimeoutVariants.ResponseReadTimeout)
if err != nil {
request.options.Output.Request(request.options.TemplatePath, address, request.Type().String(), err)
gologger.Verbose().Msgf("could not read more data from %s: %s", actualAddress, err)
Expand Down
1 change: 1 addition & 0 deletions pkg/protocols/protocols.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ type ExecutorOptions struct {
// ExportReqURLPattern exports the request URL pattern
// in ResultEvent it contains the exact url pattern (ex: {{BaseURL}}/{{randstr}}/xyz) used in the request
ExportReqURLPattern bool
TimeoutVariants types.TimeoutVariants
}

// todo: centralizing components is not feasible with current clogged architecture
Expand Down
19 changes: 10 additions & 9 deletions pkg/templates/compile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,16 @@ func setup() {
progressImpl, _ := progress.NewStatsTicker(0, false, false, false, 0)

executerOpts = protocols.ExecutorOptions{
Output: testutils.NewMockOutputWriter(options.OmitTemplate),
Options: options,
Progress: progressImpl,
ProjectFile: nil,
IssuesClient: nil,
Browser: nil,
Catalog: disk.NewCatalog(config.DefaultConfig.TemplatesDirectory),
RateLimiter: ratelimit.New(context.Background(), uint(options.RateLimit), time.Second),
Parser: templates.NewParser(),
Output: testutils.NewMockOutputWriter(options.OmitTemplate),
Options: options,
Progress: progressImpl,
ProjectFile: nil,
IssuesClient: nil,
Browser: nil,
Catalog: disk.NewCatalog(config.DefaultConfig.TemplatesDirectory),
RateLimiter: ratelimit.New(context.Background(), uint(options.RateLimit), time.Second),
Parser: templates.NewParser(),
TimeoutVariants: options.BuildTimeoutVariants(),
}
workflowLoader, err := workflow.NewLoader(&executerOpts)
if err != nil {
Expand Down
24 changes: 12 additions & 12 deletions pkg/testutils/testutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ var DefaultOptions = &types.Options{
InteractionsPollDuration: 5,
GitHubTemplateRepo: []string{},
GitHubToken: "",
ResponseReadTimeout: time.Second * 5,
}

// TemplateInfo contains info for a mock executed template.
Expand All @@ -89,17 +88,18 @@ type TemplateInfo struct {
func NewMockExecuterOptions(options *types.Options, info *TemplateInfo) *protocols.ExecutorOptions {
progressImpl, _ := progress.NewStatsTicker(0, false, false, false, 0)
executerOpts := &protocols.ExecutorOptions{
TemplateID: info.ID,
TemplateInfo: info.Info,
TemplatePath: info.Path,
Output: NewMockOutputWriter(options.OmitTemplate),
Options: options,
Progress: progressImpl,
ProjectFile: nil,
IssuesClient: nil,
Browser: nil,
Catalog: disk.NewCatalog(config.DefaultConfig.TemplatesDirectory),
RateLimiter: ratelimit.New(context.Background(), uint(options.RateLimit), time.Second),
TemplateID: info.ID,
TemplateInfo: info.Info,
TemplatePath: info.Path,
Output: NewMockOutputWriter(options.OmitTemplate),
Options: options,
Progress: progressImpl,
ProjectFile: nil,
IssuesClient: nil,
Browser: nil,
Catalog: disk.NewCatalog(config.DefaultConfig.TemplatesDirectory),
RateLimiter: ratelimit.New(context.Background(), uint(options.RateLimit), time.Second),
TimeoutVariants: options.BuildTimeoutVariants(),
}
executerOpts.CreateTemplateCtxStore()
return executerOpts
Expand Down
19 changes: 10 additions & 9 deletions pkg/tmplexec/flow/flow_executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,16 @@ func setup() {
progressImpl, _ := progress.NewStatsTicker(0, false, false, false, 0)

executerOpts = protocols.ExecutorOptions{
Output: testutils.NewMockOutputWriter(options.OmitTemplate),
Options: options,
Progress: progressImpl,
ProjectFile: nil,
IssuesClient: nil,
Browser: nil,
Catalog: disk.NewCatalog(config.DefaultConfig.TemplatesDirectory),
RateLimiter: ratelimit.New(context.Background(), uint(options.RateLimit), time.Second),
Parser: templates.NewParser(),
Output: testutils.NewMockOutputWriter(options.OmitTemplate),
Options: options,
Progress: progressImpl,
ProjectFile: nil,
IssuesClient: nil,
Browser: nil,
Catalog: disk.NewCatalog(config.DefaultConfig.TemplatesDirectory),
RateLimiter: ratelimit.New(context.Background(), uint(options.RateLimit), time.Second),
Parser: templates.NewParser(),
TimeoutVariants: options.BuildTimeoutVariants(),
}
workflowLoader, err := workflow.NewLoader(&executerOpts)
if err != nil {
Expand Down
19 changes: 10 additions & 9 deletions pkg/tmplexec/multiproto/multi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,16 @@ func setup() {
progressImpl, _ := progress.NewStatsTicker(0, false, false, false, 0)

executerOpts = protocols.ExecutorOptions{
Output: testutils.NewMockOutputWriter(options.OmitTemplate),
Options: options,
Progress: progressImpl,
ProjectFile: nil,
IssuesClient: nil,
Browser: nil,
Catalog: disk.NewCatalog(config.DefaultConfig.TemplatesDirectory),
RateLimiter: ratelimit.New(context.Background(), uint(options.RateLimit), time.Second),
Parser: templates.NewParser(),
Output: testutils.NewMockOutputWriter(options.OmitTemplate),
Options: options,
Progress: progressImpl,
ProjectFile: nil,
IssuesClient: nil,
Browser: nil,
Catalog: disk.NewCatalog(config.DefaultConfig.TemplatesDirectory),
RateLimiter: ratelimit.New(context.Background(), uint(options.RateLimit), time.Second),
Parser: templates.NewParser(),
TimeoutVariants: options.BuildTimeoutVariants(),
}
workflowLoader, err := workflow.NewLoader(&executerOpts)
if err != nil {
Expand Down
35 changes: 30 additions & 5 deletions pkg/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,8 +277,6 @@ type Options struct {
SNI string
// InputFileMode specifies the mode of input file (jsonl, burp, openapi, swagger, etc)
InputFileMode string
// DialerTimeout sets the timeout for network requests.
DialerTimeout time.Duration
// DialerKeepAlive sets the keep alive duration for network requests.
DialerKeepAlive time.Duration
// Interface to use for network scan
Expand All @@ -291,8 +289,6 @@ type Options struct {
ResponseReadSize int
// ResponseSaveSize is the maximum size of response to save
ResponseSaveSize int
// ResponseReadTimeout is response read timeout in seconds
ResponseReadTimeout time.Duration
// Health Check
HealthCheck bool
// Time to wait between each input read operation before closing the stream
Expand Down Expand Up @@ -401,6 +397,36 @@ type Options struct {
ListTemplateProfiles bool
}

type TimeoutVariants struct {
MaxResponseHeaderTimeout time.Duration
ResponseReadTimeout time.Duration
JsCompilerExecutionTimeout time.Duration
HttpTimeout time.Duration
DialTimeout time.Duration
}

func (options *Options) BuildTimeoutVariants() TimeoutVariants {
timeoutVariants := TimeoutVariants{
// MaxResponseHeaderTimeout is the timeout for response headers
// to be read from the server (this prevents infinite hang started by server if any)
// Note: this will be overridden temporarily when using @timeout request annotation
MaxResponseHeaderTimeout: time.Second * 10,
//response read timeout in seconds
ResponseReadTimeout: time.Second * 5,
JsCompilerExecutionTimeout: time.Second * time.Duration(int(float64(options.Timeout)*1.5)),
//http timeout for the client
HttpTimeout: time.Second * time.Duration(options.Timeout*3),
//timeout for network requests
DialTimeout: time.Second * time.Duration(options.Timeout),
}

if options.Timeout > 10 {
timeoutVariants.MaxResponseHeaderTimeout = time.Second * time.Duration(options.Timeout)
}

return timeoutVariants
}

// ShouldLoadResume resume file
func (options *Options) ShouldLoadResume() bool {
return options.Resume != "" && fileutil.FileExists(options.Resume)
Expand Down Expand Up @@ -437,7 +463,6 @@ func DefaultOptions() *Options {
MaxHostError: 30,
ResponseReadSize: 10 * 1024 * 1024,
ResponseSaveSize: 1024 * 1024,
ResponseReadTimeout: 5 * time.Second,
}
}

Expand Down

0 comments on commit b3eb134

Please sign in to comment.