introduce timeouts config in types.Options (#5228)

* introduce timeout variants

* update instances and add codeexectimeout

* fix test

* default to 10s

* minor

* make timeouts pluggable and rename

* remove residual code

---------

Co-authored-by: Tarun Koyalwar <tarun@projectdiscovery.io>
This commit is contained in:
Dogan Can Bakir 2024-07-15 13:27:15 +03:00 committed by GitHub
parent d4e81fd9e6
commit f080d614c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 153 additions and 123 deletions

View File

@ -297,7 +297,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"),
@ -306,7 +305,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", unitutils.Mega, "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"),

View File

@ -4,11 +4,11 @@ package compiler
import (
"context"
"fmt"
"time"
"github.com/dop251/goja"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
"github.com/projectdiscovery/nuclei/v3/pkg/types"
contextutil "github.com/projectdiscovery/utils/context"
"github.com/projectdiscovery/utils/errkit"
stringsutil "github.com/projectdiscovery/utils/strings"
@ -38,13 +38,13 @@ type ExecuteOptions struct {
// Cleanup is extra cleanup function to be called after execution
Cleanup func(runtime *goja.Runtime)
/// Timeout for this script execution
Timeout int
// Source is original source of the script
Source *string
Context context.Context
TimeoutVariants *types.Timeouts
// Manually exported objects
exports map[string]interface{}
}
@ -79,15 +79,6 @@ func (e ExecuteResult) GetSuccess() bool {
return val
}
// Execute executes a script with the default options.
func (c *Compiler) Execute(code string, args *ExecuteArgs) (ExecuteResult, error) {
p, err := WrapScriptNCompile(code, false)
if err != nil {
return nil, err
}
return c.ExecuteWithOptions(p, args, &ExecuteOptions{Context: context.Background()})
}
// ExecuteWithOptions executes a script with the provided options.
func (c *Compiler) ExecuteWithOptions(program *goja.Program, args *ExecuteArgs, opts *ExecuteOptions) (ExecuteResult, error) {
if opts == nil {
@ -106,14 +97,9 @@ func (c *Compiler) ExecuteWithOptions(program *goja.Program, args *ExecuteArgs,
// merge all args into templatectx
args.TemplateCtx = generators.MergeMaps(args.TemplateCtx, args.Args)
if opts.Timeout <= 0 || opts.Timeout > 180 {
// some js scripts can take longer time so allow configuring timeout
// from template but keep it within sane limits (180s)
opts.Timeout = JsProtocolTimeout
}
// execute with context and timeout
ctx, cancel := context.WithTimeoutCause(opts.Context, time.Duration(opts.Timeout)*time.Second, ErrJSExecDeadline)
ctx, cancel := context.WithTimeoutCause(opts.Context, opts.TimeoutVariants.JsCompilerExecutionTimeout, ErrJSExecDeadline)
defer cancel()
// execute the script
results, err := contextutil.ExecFuncWithTwoReturns(ctx, func() (val goja.Value, err error) {

View File

@ -1,11 +1,14 @@
package compiler
import (
"context"
"strings"
"testing"
"time"
"github.com/projectdiscovery/gologger"
"github.com/projectdiscovery/gologger/levels"
"github.com/projectdiscovery/nuclei/v3/pkg/types"
)
func TestNewCompilerConsoleDebug(t *testing.T) {
@ -18,7 +21,14 @@ func TestNewCompilerConsoleDebug(t *testing.T) {
})
compiler := New()
_, err := compiler.Execute("console.log('hello world');", NewExecuteArgs())
p, err := WrapScriptNCompile("console.log('hello world');", false)
if err != nil {
t.Fatal(err)
}
_, err = compiler.ExecuteWithOptions(p, NewExecuteArgs(), &ExecuteOptions{Context: context.Background(),
TimeoutVariants: &types.Timeouts{JsCompilerExecutionTimeout: time.Duration(20) * time.Second}},
)
if err != nil {
t.Fatal(err)
}
@ -27,17 +37,6 @@ func TestNewCompilerConsoleDebug(t *testing.T) {
}
}
func TestExecuteResultGetSuccess(t *testing.T) {
compiler := New()
result, err := compiler.Execute("1+1 == 2", NewExecuteArgs())
if err != nil {
t.Fatal(err)
}
if result.GetSuccess() != true {
t.Fatalf("expected true, got=%v", result.GetSuccess())
}
}
type noopWriter struct {
Callback func(data []byte, level levels.Level)
}

View File

@ -7,26 +7,17 @@ import (
// jsprotocolInit
var (
// Per Execution Javascript timeout in seconds
JsProtocolTimeout = 10
PoolingJsVmConcurrency = 100
NonPoolingVMConcurrency = 20
JsTimeoutMultiplier = 1.5
)
// Init initializes the javascript protocol
func Init(opts *types.Options) error {
if opts.Timeout < 10 {
// keep existing 10s timeout
return nil
}
if opts.JsConcurrency < 100 {
// 100 is reasonable default
opts.JsConcurrency = 100
}
// we have dialer timeout set to 10s so js needs to be at least
// 15s to return the actual error if not it will be a dialer timeout
JsProtocolTimeout = int(float64(opts.Timeout) * JsTimeoutMultiplier)
PoolingJsVmConcurrency = opts.JsConcurrency
PoolingJsVmConcurrency -= NonPoolingVMConcurrency
return nil

View File

@ -37,8 +37,7 @@ import (
)
const (
pythonEnvRegex = `os\.getenv\(['"]([^'"]+)['"]\)`
TimeoutMultiplier = 6 // timeout multiplier for code protocol
pythonEnvRegex = `os\.getenv\(['"]([^'"]+)['"]\)`
)
var (
@ -179,9 +178,6 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, dynamicVa
metaSrc.AddVariable(gozerotypes.Variable{Name: name, Value: v})
}
// set timeout using multiplier
timeout := TimeoutMultiplier * request.options.Options.Timeout
if request.PreCondition != "" {
if request.options.Options.Debug || request.options.Options.DebugRequests {
gologger.Debug().Msgf("[%s] Executing Precondition for Code request\n", request.TemplateID)
@ -199,11 +195,11 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, dynamicVa
result, err := request.options.JsCompiler.ExecuteWithOptions(request.preConditionCompiled, args,
&compiler.ExecuteOptions{
Timeout: timeout,
Source: &request.PreCondition,
Callback: registerPreConditionFunctions,
Cleanup: cleanUpPreConditionFunctions,
Context: input.Context(),
TimeoutVariants: request.options.Options.GetTimeouts(),
Source: &request.PreCondition,
Callback: registerPreConditionFunctions,
Cleanup: cleanUpPreConditionFunctions,
Context: input.Context(),
})
if err != nil {
return errorutil.NewWithTag(request.TemplateID, "could not execute pre-condition: %s", err)
@ -218,7 +214,7 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, dynamicVa
}
}
ctx, cancel := context.WithTimeoutCause(input.Context(), time.Duration(timeout)*time.Second, ErrCodeExecutionDeadline)
ctx, cancel := context.WithTimeoutCause(input.Context(), request.options.Options.GetTimeouts().CodeExecutionTimeout, ErrCodeExecutionDeadline)
defer cancel()
// Note: we use contextutil despite the fact that gozero accepts context as argument
gOutput, err := contextutil.ExecFuncWithTwoReturns(ctx, func() (*gozerotypes.Result, error) {

View File

@ -34,9 +34,7 @@ func Init(options *types.Options) error {
lfaAllowed = options.AllowLocalFileAccess
opts := fastdialer.DefaultOptions
if options.DialerTimeout > 0 {
opts.DialerTimeout = options.DialerTimeout
}
opts.DialerTimeout = options.GetTimeouts().DialTimeout
if options.DialerKeepAlive > 0 {
opts.DialerKeepAlive = options.DialerKeepAlive
}

View File

@ -336,7 +336,7 @@ func (request *Request) Compile(options *protocols.ExecutorOptions) error {
request.Raw[i] = strings.ReplaceAll(raw, "\n", "\r\n")
}
}
request.rawhttpClient = httpclientpool.GetRawHTTP(options.Options)
request.rawhttpClient = httpclientpool.GetRawHTTP(options)
}
if len(request.Matchers) > 0 || len(request.Extractors) > 0 {
compiled := &request.Operators

View File

@ -17,6 +17,7 @@ import (
"golang.org/x/net/publicsuffix"
"github.com/projectdiscovery/fastdialer/fastdialer/ja3/impersonate"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/utils"
"github.com/projectdiscovery/nuclei/v3/pkg/types"
@ -31,28 +32,14 @@ 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
}
// Init initializes the clientpool implementation
func Init(options *types.Options) error {
// Don't create clients if already created in the past.
if normalClient != nil {
return nil
}
if options.Timeout > 10 {
MaxResponseHeaderTimeout = time.Duration(options.Timeout) * time.Second
}
if options.ShouldFollowHTTPRedirects() {
forceMaxRedirects = options.MaxRedirects
}
@ -143,7 +130,7 @@ func (c *Configuration) HasStandardOptions() bool {
}
// GetRawHTTP returns the rawhttp request client
func GetRawHTTP(options *types.Options) *rawhttp.Client {
func GetRawHTTP(options *protocols.ExecutorOptions) *rawhttp.Client {
if rawHttpClient == nil {
rawHttpOptions := rawhttp.DefaultOptions
if types.ProxyURL != "" {
@ -153,7 +140,7 @@ func GetRawHTTP(options *types.Options) *rawhttp.Client {
} else if protocolstate.Dialer != nil {
rawHttpOptions.FastDialer = protocolstate.Dialer
}
rawHttpOptions.Timeout = GetHttpTimeout(options)
rawHttpOptions.Timeout = options.Options.GetTimeouts().HttpTimeout
rawHttpClient = rawhttp.NewClient(rawHttpOptions)
}
return rawHttpClient
@ -239,7 +226,7 @@ func wrappedGet(options *types.Options, configuration *Configuration) (*retryabl
}
// responseHeaderTimeout is max timeout for response headers to be read
responseHeaderTimeout := MaxResponseHeaderTimeout
responseHeaderTimeout := options.GetTimeouts().HttpResponseHeaderTimeout
if configuration.ResponseHeaderTimeout != 0 {
responseHeaderTimeout = configuration.ResponseHeaderTimeout
}
@ -308,7 +295,7 @@ func wrappedGet(options *types.Options, configuration *Configuration) (*retryabl
CheckRedirect: makeCheckRedirectFunc(redirectFlow, maxRedirects),
}
if !configuration.NoTimeout {
httpclient.Timeout = GetHttpTimeout(options)
httpclient.Timeout = options.GetTimeouts().HttpTimeout
}
client := retryablehttp.NewWithHTTPClient(httpclient, retryableHttpOptions)
if jar != nil {

View File

@ -483,7 +483,7 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, dynamicVa
request.options.RateLimitTake()
ctx := request.newContext(input)
ctxWithTimeout, cancel := context.WithTimeoutCause(ctx, httpclientpool.GetHttpTimeout(request.options.Options), ErrHttpEngineRequestDeadline)
ctxWithTimeout, cancel := context.WithTimeoutCause(ctx, request.options.Options.GetTimeouts().HttpTimeout, ErrHttpEngineRequestDeadline)
defer cancel()
generatedHttpRequest, err := generator.Make(ctxWithTimeout, input, data, payloads, dynamicValue)

View File

@ -130,6 +130,10 @@ func (r *Request) parseAnnotations(rawRequest string, request *retryablehttp.Req
if duration := reTimeoutAnnotation.FindStringSubmatch(rawRequest); len(duration) > 0 {
value := strings.TrimSpace(duration[1])
if parsed, err := time.ParseDuration(value); err == nil {
// to avoid dos via timeout request annotation in http template we set it to maximum of 2 minutes
if parsed > 2*time.Minute {
parsed = 2 * time.Minute
}
//nolint:govet // cancelled automatically by withTimeout
// global timeout is overridden by annotation by replacing context
ctx, overrides.cancelFunc = context.WithTimeoutCause(context.TODO(), parsed, ErrTimeoutAnnotationDeadline)
@ -140,7 +144,7 @@ func (r *Request) parseAnnotations(rawRequest string, request *retryablehttp.Req
} else {
//nolint:govet // cancelled automatically by withTimeout
// global timeout is overridden by annotation by replacing context
ctx, overrides.cancelFunc = context.WithTimeoutCause(context.TODO(), httpclientpool.GetHttpTimeout(r.options.Options), ErrRequestTimeoutDeadline)
ctx, overrides.cancelFunc = context.WithTimeoutCause(context.TODO(), r.options.Options.GetTimeouts().HttpTimeout, ErrRequestTimeoutDeadline)
request = request.Clone(ctx)
}
}

View File

@ -65,9 +65,6 @@ type Request struct {
// Code contains code to execute for the javascript request.
Code string `yaml:"code,omitempty" json:"code,omitempty" jsonschema:"title=code to execute in javascript,description=Executes inline javascript code for the request"`
// description: |
// Timeout in seconds is optional timeout for each javascript script execution (i.e init, pre-condition, code)
Timeout int `yaml:"timeout,omitempty" json:"timeout,omitempty" jsonschema:"title=timeout for javascript execution,description=Timeout in seconds is optional timeout for entire javascript script execution"`
// description: |
// StopAtFirstMatch stops processing the request at first match.
StopAtFirstMatch bool `yaml:"stop-at-first-match,omitempty" json:"stop-at-first-match,omitempty" jsonschema:"title=stop at first match,description=Stop the execution after a match is found"`
// description: |
@ -153,9 +150,9 @@ func (request *Request) Compile(options *protocols.ExecutorOptions) error {
}
opts := &compiler.ExecuteOptions{
Timeout: request.Timeout,
Source: &request.Init,
Context: context.Background(),
TimeoutVariants: request.options.Options.GetTimeouts(),
Source: &request.Init,
Context: context.Background(),
}
// register 'export' function to export variables from init code
// these are saved in args and are available in pre-condition and request code
@ -345,7 +342,10 @@ func (request *Request) ExecuteWithResults(target *contextargs.Context, dynamicV
argsCopy.TemplateCtx = templateCtx.GetAll()
result, err := request.options.JsCompiler.ExecuteWithOptions(request.preConditionCompiled, argsCopy,
&compiler.ExecuteOptions{Timeout: request.Timeout, Source: &request.PreCondition, Context: target.Context()})
&compiler.ExecuteOptions{
TimeoutVariants: requestOptions.Options.GetTimeouts(),
Source: &request.PreCondition, Context: target.Context(),
})
if err != nil {
return errorutil.NewWithTag(request.TemplateID, "could not execute pre-condition: %s", err)
}
@ -500,7 +500,11 @@ func (request *Request) executeRequestWithPayloads(hostPort string, input *conte
}
results, err := request.options.JsCompiler.ExecuteWithOptions(request.scriptCompiled, argsCopy,
&compiler.ExecuteOptions{Timeout: request.Timeout, Source: &request.Code, Context: input.Context()})
&compiler.ExecuteOptions{
TimeoutVariants: requestOptions.Options.GetTimeouts(),
Source: &request.Code,
Context: input.Context(),
})
if err != nil {
// shouldn't fail even if it returned error instead create a failure event
results = compiler.ExecuteResult{"success": false, "error": err.Error()}

View File

@ -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.Options.GetTimeouts().TcpReadTimeout)
if err != nil {
return errorutil.NewWithErr(err).Msgf("could not read response from connection")
}
@ -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.Options.GetTimeouts().TcpReadTimeout)
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)

View File

@ -39,15 +39,15 @@ 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(),
}
workflowLoader, err := workflow.NewLoader(&executerOpts)
if err != nil {

View File

@ -76,7 +76,6 @@ var DefaultOptions = &types.Options{
InteractionsPollDuration: 5,
GitHubTemplateRepo: []string{},
GitHubToken: "",
ResponseReadTimeout: time.Second * 5,
}
// TemplateInfo contains info for a mock executed template.
@ -90,17 +89,17 @@ 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),
}
executerOpts.CreateTemplateCtxStore()
return executerOpts

View File

@ -27,15 +27,15 @@ 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(),
}
workflowLoader, err := workflow.NewLoader(&executerOpts)
if err != nil {

View File

@ -278,8 +278,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
@ -292,8 +290,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
@ -406,6 +402,79 @@ type Options struct {
HttpApiEndpoint string
// ListTemplateProfiles lists all available template profiles
ListTemplateProfiles bool
// timeouts contains various types of timeouts used in nuclei
// these timeouts are derived from dial-timeout (-timeout) with known multipliers
// This is internally managed and does not need to be set by user by explicitly setting
// this overrides the default/derived one
timeouts *Timeouts
}
// SetTimeouts sets the timeout variants to use for the executor
func (opts *Options) SetTimeouts(t *Timeouts) {
opts.timeouts = t
}
// GetTimeouts returns the timeout variants to use for the executor
func (eo *Options) GetTimeouts() *Timeouts {
if eo.timeouts != nil {
// redundant but apply to avoid any potential issues
eo.timeouts.ApplyDefaults()
return eo.timeouts
}
// set timeout variant value
eo.timeouts = NewTimeoutVariant(eo.Timeout)
eo.timeouts.ApplyDefaults()
return eo.timeouts
}
// Timeouts is a struct that contains all the timeout variants for nuclei
// dialer timeout is used to derive other timeouts
type Timeouts struct {
// DialTimeout for fastdialer (default 10s)
DialTimeout time.Duration
// Tcp(Network Protocol) Read From Connection Timeout (default 5s)
TcpReadTimeout time.Duration
// Http Response Header Timeout (default 10s)
// this timeout prevents infinite hangs started by server if any
// this is temporarily overridden when using @timeout request annotation
HttpResponseHeaderTimeout time.Duration
// HttpTimeout for http client (default -> 3 x dial-timeout = 30s)
HttpTimeout time.Duration
// JsCompilerExec timeout/deadline (default -> 2 x dial-timeout = 20s)
JsCompilerExecutionTimeout time.Duration
// CodeExecutionTimeout for code execution (default -> 3 x dial-timeout = 30s)
CodeExecutionTimeout time.Duration
}
// NewTimeoutVariant creates a new timeout variant with the given dial timeout in seconds
func NewTimeoutVariant(dialTimeoutSec int) *Timeouts {
tv := &Timeouts{
DialTimeout: time.Duration(dialTimeoutSec) * time.Second,
}
tv.ApplyDefaults()
return tv
}
// ApplyDefaults applies default values to timeout variants when missing
func (tv *Timeouts) ApplyDefaults() {
if tv.DialTimeout == 0 {
tv.DialTimeout = 10 * time.Second
}
if tv.TcpReadTimeout == 0 {
tv.TcpReadTimeout = 5 * time.Second
}
if tv.HttpResponseHeaderTimeout == 0 {
tv.HttpResponseHeaderTimeout = 10 * time.Second
}
if tv.HttpTimeout == 0 {
tv.HttpTimeout = 3 * tv.DialTimeout
}
if tv.JsCompilerExecutionTimeout == 0 {
tv.JsCompilerExecutionTimeout = 2 * tv.DialTimeout
}
if tv.CodeExecutionTimeout == 0 {
tv.CodeExecutionTimeout = 3 * tv.DialTimeout
}
}
// ShouldLoadResume resume file
@ -444,7 +513,6 @@ func DefaultOptions() *Options {
MaxHostError: 30,
ResponseReadSize: 10 * unitutils.Mega,
ResponseSaveSize: unitutils.Mega,
ResponseReadTimeout: 5 * time.Second,
}
}