From 92326047b7309a7738f454023057f25377a43add Mon Sep 17 00:00:00 2001 From: Tarun Koyalwar Date: Thu, 16 May 2024 02:26:11 +0530 Subject: [PATCH] migrate to new utils/errkit --- go.mod | 6 +- go.sum | 10 +- pkg/js/compiler/compiler.go | 4 +- pkg/output/output.go | 25 +- pkg/protocols/code/code.go | 4 +- .../common/hosterrorscache/hosterrorscache.go | 22 +- pkg/protocols/common/protocolstate/state.go | 2 +- pkg/protocols/headless/engine/page_actions.go | 4 +- pkg/protocols/http/request.go | 7 +- pkg/protocols/http/request_annotations.go | 7 +- pkg/types/errkit/errors.go | 249 ------------------ pkg/types/errkit/errors_test.go | 80 ------ pkg/types/errkit/helpers.go | 108 -------- pkg/types/errkit/interfaces.go | 32 --- pkg/types/nucleierr/kinds.go | 28 ++ 15 files changed, 85 insertions(+), 503 deletions(-) delete mode 100644 pkg/types/errkit/errors.go delete mode 100644 pkg/types/errkit/errors_test.go delete mode 100644 pkg/types/errkit/helpers.go delete mode 100644 pkg/types/errkit/interfaces.go create mode 100644 pkg/types/nucleierr/kinds.go diff --git a/go.mod b/go.mod index 1002e41efd..5ab5481067 100644 --- a/go.mod +++ b/go.mod @@ -94,12 +94,12 @@ require ( github.com/projectdiscovery/tlsx v1.1.6 github.com/projectdiscovery/uncover v1.0.7 github.com/projectdiscovery/useragent v0.0.49 - github.com/projectdiscovery/utils v0.0.92 + github.com/projectdiscovery/utils v0.0.93-0.20240515205400-37bc84ef0c3f github.com/projectdiscovery/wappalyzergo v0.0.122 github.com/redis/go-redis/v9 v9.1.0 github.com/seh-msft/burpxml v1.0.1 github.com/stretchr/testify v1.9.0 - github.com/tarunKoyalwar/goleak v0.0.0-20240426214851-746d64600adc + github.com/tarunKoyalwar/goleak v0.0.0-20240429141123-0efa90dbdcf9 github.com/zmap/zgrab2 v0.1.8-0.20230806160807-97ba87c0e706 golang.org/x/term v0.19.0 gopkg.in/yaml.v3 v3.0.1 @@ -350,3 +350,5 @@ require ( // https://go.dev/ref/mod#go-mod-file-retract retract v3.2.0 // retract due to broken js protocol issue + +replace github.com/projectdiscovery/fastdialer => /Users/tarun/Codebase/fastdialer diff --git a/go.sum b/go.sum index dbc2f68aeb..18c2171514 100644 --- a/go.sum +++ b/go.sum @@ -837,8 +837,6 @@ github.com/projectdiscovery/clistats v0.0.20 h1:5jO5SLiRJ7f0nDV0ndBNmBeesbROouPo github.com/projectdiscovery/clistats v0.0.20/go.mod h1:GJ2av0KnOvK0AISQnP8hyDclYIji1LVkx2l0pwnzAu4= github.com/projectdiscovery/dsl v0.0.52 h1:jvIvF+qN8+MbI1MHtWJJKfWqAZQlCExL3ob7SddQbZE= github.com/projectdiscovery/dsl v0.0.52/go.mod h1:xfcHwhy2HSaeGgh+1wqzOoCGm2XTdh5JzjBRBVHEMvI= -github.com/projectdiscovery/fastdialer v0.0.71 h1:96j6Y65hDPZ9AzlYpp95hvIH5Yx/0OE2UTx+frWfnm4= -github.com/projectdiscovery/fastdialer v0.0.71/go.mod h1:b/oPPVSoLLD2N4W2/HrXbhQbyJVXqRw8CK1lenCUk64= github.com/projectdiscovery/fasttemplate v0.0.2 h1:h2cISk5xDhlJEinlBQS6RRx0vOlOirB2y3Yu4PJzpiA= github.com/projectdiscovery/fasttemplate v0.0.2/go.mod h1:XYWWVMxnItd+r0GbjA1GCsUopMw1/XusuQxdyAIHMCw= github.com/projectdiscovery/freeport v0.0.5 h1:jnd3Oqsl4S8n0KuFkE5Hm8WGDP24ITBvmyw5pFTHS8Q= @@ -889,8 +887,8 @@ github.com/projectdiscovery/uncover v1.0.7 h1:ut+2lTuvmftmveqF5RTjMWAgyLj8ltPQC7 github.com/projectdiscovery/uncover v1.0.7/go.mod h1:HFXgm1sRPuoN0D4oATljPIdmbo/EEh1wVuxQqo/dwFE= github.com/projectdiscovery/useragent v0.0.49 h1:wQc9i+Xy+mUMJ45Ralv1JsQImRWqEOEvpYUe6MchScg= github.com/projectdiscovery/useragent v0.0.49/go.mod h1:jQz6X/usiXrPYE6B/1uVKuzIrBJXgw9hLC9eeNy38+0= -github.com/projectdiscovery/utils v0.0.92 h1:lGCmjUJhzoNX4FQZWpp80058pRlD0/dYxLJOSs07EqY= -github.com/projectdiscovery/utils v0.0.92/go.mod h1:d5uvD5qcRiK3qxZbBy9eatCqrCSuj9SObL04w/WgXSg= +github.com/projectdiscovery/utils v0.0.93-0.20240515205400-37bc84ef0c3f h1:6fk5y6FG4SfB80aXOEQfz+MJFJ/1F3sAF17nUcstXOc= +github.com/projectdiscovery/utils v0.0.93-0.20240515205400-37bc84ef0c3f/go.mod h1:2+mWzk5FeYdK9imo5eLk6oVeih0G0wsTff1pzBAh9tk= github.com/projectdiscovery/wappalyzergo v0.0.122 h1:xfNJ7VNzU/OGlgYtsyB5ppuOHdfWzU2B8cYATwTz54c= github.com/projectdiscovery/wappalyzergo v0.0.122/go.mod h1:qW0PP+UBMcdQBBnwk+X6YYFs6huKNvn2BOVs4vQPru0= github.com/projectdiscovery/yamldoc-go v1.0.4 h1:eZoESapnMw6WAHiVgRwNqvbJEfNHEH148uthhFbG5jE= @@ -1016,8 +1014,8 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= -github.com/tarunKoyalwar/goleak v0.0.0-20240426214851-746d64600adc h1:/5P5I7oDqdLee8W9Moof0xSD8tT1qEVzhObSI9CqHkg= -github.com/tarunKoyalwar/goleak v0.0.0-20240426214851-746d64600adc/go.mod h1:uQdBQGrE1fZ2EyOs0pLcCDd1bBV4rSThieuIIGhXZ50= +github.com/tarunKoyalwar/goleak v0.0.0-20240429141123-0efa90dbdcf9 h1:GXIyLuIJ5Qk46lI8WJ83qHBZKUI3zhmMmuoY9HICUIQ= +github.com/tarunKoyalwar/goleak v0.0.0-20240429141123-0efa90dbdcf9/go.mod h1:uQdBQGrE1fZ2EyOs0pLcCDd1bBV4rSThieuIIGhXZ50= github.com/tidwall/assert v0.1.0 h1:aWcKyRBUAdLoVebxo95N7+YZVTFF/ASTr7BN4sLP6XI= github.com/tidwall/assert v0.1.0/go.mod h1:QLYtGyeqse53vuELQheYl9dngGCJQ+mTtlxcktb+Kj8= github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= diff --git a/pkg/js/compiler/compiler.go b/pkg/js/compiler/compiler.go index 570370f33a..26f4705615 100644 --- a/pkg/js/compiler/compiler.go +++ b/pkg/js/compiler/compiler.go @@ -9,14 +9,14 @@ import ( "github.com/dop251/goja" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators" - "github.com/projectdiscovery/nuclei/v3/pkg/types/errkit" contextutil "github.com/projectdiscovery/utils/context" + "github.com/projectdiscovery/utils/errkit" stringsutil "github.com/projectdiscovery/utils/strings" ) var ( // ErrJSExecDeadline is the error returned when alloted time for script execution exceeds - ErrJSExecDeadline = errkit.New("js engine execution deadline exceeded").SetClass(errkit.ErrClassDeadline).Build() + ErrJSExecDeadline = errkit.New("js engine execution deadline exceeded").SetKind(errkit.ErrKindDeadline).Build() ) // Compiler provides a runtime to execute goja runtime diff --git a/pkg/output/output.go b/pkg/output/output.go index 044d164f1f..a78792ba8a 100644 --- a/pkg/output/output.go +++ b/pkg/output/output.go @@ -4,6 +4,7 @@ import ( "encoding/base64" "fmt" "io" + "log/slog" "os" "path/filepath" "regexp" @@ -27,7 +28,9 @@ import ( "github.com/projectdiscovery/nuclei/v3/pkg/operators" protocolUtils "github.com/projectdiscovery/nuclei/v3/pkg/protocols/utils" "github.com/projectdiscovery/nuclei/v3/pkg/types" + "github.com/projectdiscovery/nuclei/v3/pkg/types/nucleierr" "github.com/projectdiscovery/nuclei/v3/pkg/utils" + "github.com/projectdiscovery/utils/errkit" fileutil "github.com/projectdiscovery/utils/file" osutils "github.com/projectdiscovery/utils/os" ) @@ -299,10 +302,12 @@ func (w *StandardWriter) Write(event *ResultEvent) error { // JSONLogRequest is a trace/error log request written to file type JSONLogRequest struct { - Template string `json:"template"` - Input string `json:"input"` - Error string `json:"error"` - Type string `json:"type"` + Template string `json:"template"` + Type string `json:"type"` + Input string `json:"input"` + Error string `json:"error"` + Kind string `json:"kind"` + Attrs interface{} `json:"attrs,omitempty"` } // Request writes a log the requests trace log @@ -314,11 +319,17 @@ func (w *StandardWriter) Request(templatePath, input, requestType string, reques Template: templatePath, Input: input, Type: requestType, + Kind: errkit.ErrKindUnknown.String(), } - if unwrappedErr := utils.UnwrapError(requestErr); unwrappedErr != nil { - request.Error = unwrappedErr.Error() - } else { + errX := errkit.FromError(requestErr) + if errX == nil { request.Error = "none" + } else { + request.Error = errX.Cause().Error() + request.Kind = errkit.GetErrorKind(requestErr, nucleierr.ErrTemplateLogic).String() + if len(errX.Attrs()) > 0 { + request.Attrs = slog.GroupValue(errX.Attrs()...) + } } data, err := jsoniter.Marshal(request) diff --git a/pkg/protocols/code/code.go b/pkg/protocols/code/code.go index 7c0ba297d5..5cecadde43 100644 --- a/pkg/protocols/code/code.go +++ b/pkg/protocols/code/code.go @@ -31,8 +31,8 @@ import ( protocolutils "github.com/projectdiscovery/nuclei/v3/pkg/protocols/utils" templateTypes "github.com/projectdiscovery/nuclei/v3/pkg/templates/types" "github.com/projectdiscovery/nuclei/v3/pkg/types" - "github.com/projectdiscovery/nuclei/v3/pkg/types/errkit" contextutil "github.com/projectdiscovery/utils/context" + "github.com/projectdiscovery/utils/errkit" errorutil "github.com/projectdiscovery/utils/errors" ) @@ -45,7 +45,7 @@ var ( // pythonEnvRegexCompiled is the compiled regex for python environment variables pythonEnvRegexCompiled = regexp.MustCompile(pythonEnvRegex) // ErrCodeExecutionDeadline is the error returned when alloted time for script execution exceeds - ErrCodeExecutionDeadline = errkit.New("code execution deadline exceeded").SetClass(errkit.ErrClassDeadline).Build() + ErrCodeExecutionDeadline = errkit.New("code execution deadline exceeded").SetKind(errkit.ErrKindDeadline).Build() ) // Request is a request for the SSL protocol diff --git a/pkg/protocols/common/hosterrorscache/hosterrorscache.go b/pkg/protocols/common/hosterrorscache/hosterrorscache.go index 021a25b396..07a60e2ca1 100644 --- a/pkg/protocols/common/hosterrorscache/hosterrorscache.go +++ b/pkg/protocols/common/hosterrorscache/hosterrorscache.go @@ -10,7 +10,8 @@ import ( "github.com/bluele/gcache" "github.com/projectdiscovery/gologger" - "github.com/projectdiscovery/nuclei/v3/pkg/types/errkit" + "github.com/projectdiscovery/nuclei/v3/pkg/types/nucleierr" + "github.com/projectdiscovery/utils/errkit" ) // CacheInterface defines the signature of the hosterrorscache so that @@ -135,15 +136,24 @@ func (c *Cache) checkError(err error) bool { if err == nil { return false } - // parse error for furthur processing - errX := errkit.FromError(err) - switch errX.Class() { - case errkit.ErrClassTemplateLogic: + kind := errkit.GetErrorKind(err, nucleierr.ErrTemplateLogic) + switch kind { + case nucleierr.ErrTemplateLogic: // these are errors that are not related to the target // and are due to template logic return false - + case errkit.ErrKindNetworkTemporary: + // these should not be counted as host errors + return false + case errkit.ErrKindNetworkPermanent: + // these should be counted as host errors + return true + case errkit.ErrKindDeadline: + // these should not be counted as host errors + return false default: + // parse error for furthur processing + errX := errkit.FromError(err) tmp := errX.Cause() cause := tmp.Error() if strings.Contains(cause, "ReadStatusLine:") && strings.Contains(cause, "read: connection reset by peer") { diff --git a/pkg/protocols/common/protocolstate/state.go b/pkg/protocols/common/protocolstate/state.go index a763f1abf9..4cc9e66a48 100644 --- a/pkg/protocols/common/protocolstate/state.go +++ b/pkg/protocols/common/protocolstate/state.go @@ -118,7 +118,7 @@ func Init(options *types.Options) error { if err != nil { return err } - opts.ProxyDialer = &dialer + opts.ProxyDialer = dialer } if options.SystemResolvers { diff --git a/pkg/protocols/headless/engine/page_actions.go b/pkg/protocols/headless/engine/page_actions.go index 681bec246f..57cc371093 100644 --- a/pkg/protocols/headless/engine/page_actions.go +++ b/pkg/protocols/headless/engine/page_actions.go @@ -23,8 +23,8 @@ import ( "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/utils/vardump" protocolutils "github.com/projectdiscovery/nuclei/v3/pkg/protocols/utils" httputil "github.com/projectdiscovery/nuclei/v3/pkg/protocols/utils/http" - "github.com/projectdiscovery/nuclei/v3/pkg/types/errkit" contextutil "github.com/projectdiscovery/utils/context" + "github.com/projectdiscovery/utils/errkit" errorutil "github.com/projectdiscovery/utils/errors" fileutil "github.com/projectdiscovery/utils/file" folderutil "github.com/projectdiscovery/utils/folder" @@ -37,7 +37,7 @@ var ( errinvalidArguments = errorutil.New("invalid arguments provided") ErrLFAccessDenied = errorutil.New("Use -allow-local-file-access flag to enable local file access") // ErrActionExecDealine is the error returned when alloted time for action execution exceeds - ErrActionExecDealine = errkit.New("headless action execution deadline exceeded").SetClass(errkit.ErrClassDeadline).Build() + ErrActionExecDealine = errkit.New("headless action execution deadline exceeded").SetKind(errkit.ErrKindDeadline).Build() ) const ( diff --git a/pkg/protocols/http/request.go b/pkg/protocols/http/request.go index 8e24a575fe..6f34b61f4b 100644 --- a/pkg/protocols/http/request.go +++ b/pkg/protocols/http/request.go @@ -35,9 +35,10 @@ import ( "github.com/projectdiscovery/nuclei/v3/pkg/protocols/http/signerpool" templateTypes "github.com/projectdiscovery/nuclei/v3/pkg/templates/types" "github.com/projectdiscovery/nuclei/v3/pkg/types" - "github.com/projectdiscovery/nuclei/v3/pkg/types/errkit" + "github.com/projectdiscovery/nuclei/v3/pkg/types/nucleierr" "github.com/projectdiscovery/rawhttp" convUtil "github.com/projectdiscovery/utils/conversion" + "github.com/projectdiscovery/utils/errkit" errorutil "github.com/projectdiscovery/utils/errors" httpUtils "github.com/projectdiscovery/utils/http" "github.com/projectdiscovery/utils/reader" @@ -56,9 +57,9 @@ const ( var ( MaxBodyRead = int64(10 * 1024 * 1024) // 10MB // ErrMissingVars is error occured when variables are missing - ErrMissingVars = errkit.New("stop execution due to unresolved variables").SetClass(errkit.ErrClassTemplateLogic).Build() + ErrMissingVars = errkit.New("stop execution due to unresolved variables").SetKind(nucleierr.ErrTemplateLogic).Build() // ErrHttpEngineRequestDeadline is error occured when request deadline set by http request engine is exceeded - ErrHttpEngineRequestDeadline = errkit.New("http request engine deadline exceeded").SetClass(errkit.ErrClassDeadline).Build() + ErrHttpEngineRequestDeadline = errkit.New("http request engine deadline exceeded").SetKind(errkit.ErrKindDeadline).Build() ) // Type returns the type of the protocol request diff --git a/pkg/protocols/http/request_annotations.go b/pkg/protocols/http/request_annotations.go index 1271359154..dd58674242 100644 --- a/pkg/protocols/http/request_annotations.go +++ b/pkg/protocols/http/request_annotations.go @@ -10,8 +10,9 @@ import ( "github.com/projectdiscovery/fastdialer/fastdialer" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/http/httpclientpool" - "github.com/projectdiscovery/nuclei/v3/pkg/types/errkit" + "github.com/projectdiscovery/nuclei/v3/pkg/types/nucleierr" "github.com/projectdiscovery/retryablehttp-go" + "github.com/projectdiscovery/utils/errkit" iputil "github.com/projectdiscovery/utils/ip" stringsutil "github.com/projectdiscovery/utils/strings" ) @@ -33,10 +34,10 @@ var ( // which was alloted using @timeout annotation this usually means that vulnerability was not found // in rare case it could also happen due to network congestion // the assigned class is TemplateLogic since this in almost every case means that server is not vulnerable - ErrTimeoutAnnotationDeadline = errkit.New("timeout annotation deadline exceeded").SetClass(errkit.ErrClassTemplateLogic).Build() + ErrTimeoutAnnotationDeadline = errkit.New("timeout annotation deadline exceeded").SetKind(nucleierr.ErrTemplateLogic).Build() // ErrRequestTimeoutDeadline is the error returned when a specific amount of time was exceeded for a request // this happens when the request execution exceeds alloted time - ErrRequestTimeoutDeadline = errkit.New("request timeout deadline exceeded when notimeout is set").SetClass(errkit.ErrClassDeadline).Build() + ErrRequestTimeoutDeadline = errkit.New("request timeout deadline exceeded when notimeout is set").SetKind(errkit.ErrKindDeadline).Build() ) type flowMark int diff --git a/pkg/types/errkit/errors.go b/pkg/types/errkit/errors.go deleted file mode 100644 index 3d44782394..0000000000 --- a/pkg/types/errkit/errors.go +++ /dev/null @@ -1,249 +0,0 @@ -// errkit implements all errors generated by nuclei and includes error definations -// specific to nuclei , error classification (like network,logic) etc -package errkit - -import ( - "encoding/json" - "errors" - "fmt" - "strings" - - "github.com/projectdiscovery/utils/env" -) - -const ( - // DelimArrow is delim used by projectdiscovery/utils to join errors - DelimArrow = "<-" - // DelimArrowSerialized - DelimArrowSerialized = "\u003c-" - // DelimSemiColon is standard delim popularly used to join errors - DelimSemiColon = "; " - // DelimMultiLine is delim used to join errors in multiline format - DelimMultiLine = "\n - " - // MultiLinePrefix is the prefix used for multiline errors - MultiLineErrPrefix = "the following errors occurred:" -) - -const ( - // ErrClassNetwork indicates an error related to network operations - // these may be resolved by retrying the operation with exponential backoff - // ex: Timeout awaiting headers, connection reset by peer etc - ErrClassNetworkTemporary = "network-temporary-error" - // ErrClassNetworkPermanent indicates a permanent error related to network operations - // these may not be resolved by retrying and need manual intervention - // ex: no address found for host - ErrClassNetworkPermanent = "network-permanent-error" - // ErrClassDeadline indicates a timeout error in logical operations - // these are custom deadlines set by nuclei itself to prevent infinite hangs - // and in most cases are server side issues (ex: server connects but does not respond at all) - // a manual intervention is required - ErrClassDeadline = "deadline-error" - // ErrClassLogic indicates an error in logical operations or decision-making - // these are caused if a dependency is not met ex: next operation requires csrf token - // but csrf token is not present (these can be safely ignored and does not actually represent an error) - ErrClassTemplateLogic = "template-logic-error" - // ErrClassDataMissing indicates an error due to missing required data - // ex: a template that requires username , password or something else but none were provided - ErrClassDataMissing = "data-missing" - // ErrClassUnknown indicates an unknown error class - ErrClassUnknown = "unknown-class" -) - -var ( - // MaxErrorDepth is the maximum depth of errors to be unwrapped or maintained - // all errors beyond this depth will be ignored - MaxErrorDepth = env.GetEnvOrDefault("MAX_ERROR_DEPTH", 3) - // ErrorSeperator is the seperator used to join errors - ErrorSeperator = env.GetEnvOrDefault("ERROR_SEPERATOR", "; ") -) - -// ErrorX is a custom error type that can handle all known types of errors -// wrapping and joining strategies including custom ones and it supports error class -// which can be shown to client/users in more meaningful way -type ErrorX struct { - class string - errs []error -} - -// Build returns the object as error interface -func (e *ErrorX) Build() error { - return e -} - -// Unwrap returns the underlying error -func (e *ErrorX) Unwrap() []error { - return e.errs -} - -// Is checks if current error contains given error -func (e *ErrorX) Is(err error) bool { - x := &ErrorX{} - parseError(x, err) - // even one submatch is enough - for _, orig := range e.errs { - for _, match := range x.errs { - if errors.Is(orig, match) { - return true - } - } - } - - return false -} - -// MarshalJSON returns the json representation of the error -func (e *ErrorX) MarshalJSON() ([]byte, error) { - m := map[string]interface{}{ - "class": e.class, - "errors": e.errs, - } - return json.Marshal(m) -} - -// Error returns the error string -func (e *ErrorX) Error() string { - var sb strings.Builder - if e.class != "" { - sb.WriteString("class=") - sb.WriteString(e.getOriginClass()) - sb.WriteString(" ") - } - for _, err := range e.errs { - sb.WriteString(err.Error()) - sb.WriteString(ErrorSeperator) - } - return strings.TrimSuffix(sb.String(), ErrorSeperator) -} - -// Cause return the original error that caused this without any wrapping -func (e *ErrorX) Cause() error { - if len(e.errs) > 0 { - return e.errs[0] - } - return nil -} - -// getOriginClass returns the class that was first set -func (e *ErrorX) getOriginClass() string { - index := strings.LastIndex(e.class, ",") - if index != -1 { - return e.class[:index] - } else { - return e.class - } -} - -// Class returns the class of the error -// if multiple classes are present, it returns the first one -func (e *ErrorX) Class() string { - return e.getOriginClass() -} - -// FromError parses a given error to understand the error class -// and optionally adds given message for more info -func FromError(err error) *ErrorX { - if err == nil { - return nil - } - nucleiErr := &ErrorX{} - parseError(nucleiErr, err) - return nucleiErr -} - -// New creates a new error with the given message -func New(format string, args ...interface{}) *ErrorX { - return &ErrorX{errs: []error{fmt.Errorf(format, args...)}} -} - -// Msgf adds a message to the error -func (e *ErrorX) Msgf(format string, args ...interface{}) { - if e == nil { - return - } - e.errs = append(e.errs, fmt.Errorf(format, args...)) -} - -// SetClass sets the class of the error -// if underlying error class was already set, then it is given preference -// when generating final error msg -func (e *ErrorX) SetClass(class string) *ErrorX { - if e.class != "" { - e.class = class + "," + e.class - } else { - e.class = class - } - return e -} - -// parseError recursively parses all known types of errors -func parseError(to *ErrorX, err error) { - if err == nil { - return - } - if to == nil { - to = &ErrorX{} - } - if len(to.errs) >= MaxErrorDepth { - return - } - - switch v := err.(type) { - case *ErrorX: - to.errs = append(to.errs, v.errs...) - if to.class == "" { - to.class = v.class - } else { - to.class += "," + v.class - } - case JoinedError: - to.errs = append(to.errs, v.Unwrap()...) - case WrappedError: - to.errs = append(to.errs, v.Unwrap()) - case CauseError: - to.errs = append(to.errs, v.Cause()) - remaining := strings.Replace(err.Error(), v.Cause().Error(), "", -1) - parseError(to, errors.New(remaining)) - default: - errString := err.Error() - // try assigning to enriched error - if strings.Contains(errString, DelimArrow) { - // Split the error by arrow delim - parts := strings.Split(errString, DelimArrow) - for i := len(parts) - 1; i >= 0; i-- { - part := strings.TrimSpace(parts[i]) - parseError(to, errors.New(part)) - } - } else if strings.Contains(errString, DelimArrowSerialized) { - // Split the error by arrow delim - parts := strings.Split(errString, DelimArrowSerialized) - for i := len(parts) - 1; i >= 0; i-- { - part := strings.TrimSpace(parts[i]) - parseError(to, errors.New(part)) - } - } else if strings.Contains(errString, DelimSemiColon) { - // Split the error by semi-colon delim - parts := strings.Split(errString, DelimSemiColon) - for _, part := range parts { - part = strings.TrimSpace(part) - parseError(to, errors.New(part)) - } - } else if strings.Contains(errString, MultiLineErrPrefix) { - // remove prefix - msg := strings.ReplaceAll(errString, MultiLineErrPrefix, "") - parts := strings.Split(msg, DelimMultiLine) - for _, part := range parts { - part = strings.TrimSpace(part) - parseError(to, errors.New(part)) - } - } else { - // this cannot be furthur unwrapped - to.errs = append(to.errs, err) - } - } -} - -// WrappedError is implemented by errors that are wrapped -type WrappedError interface { - // Unwrap returns the underlying error - Unwrap() error -} diff --git a/pkg/types/errkit/errors_test.go b/pkg/types/errkit/errors_test.go deleted file mode 100644 index 62207db761..0000000000 --- a/pkg/types/errkit/errors_test.go +++ /dev/null @@ -1,80 +0,0 @@ -package errkit - -import ( - "testing" - - "github.com/pkg/errors" - errorutil "github.com/projectdiscovery/utils/errors" - "go.uber.org/multierr" - - stderrors "errors" -) - -// what are these tests ? -// Below tests check for interoperability of this package with other error packages -// like pkg/errors and go.uber.org/multierr and std errors as well - -func TestErrorAs(t *testing.T) { - // Create a new error with a specific class and wrap it - x := New("this is a nuclei error").SetClass(ErrClassNetworkPermanent).Build() - y := errors.Wrap(x, "this is a wrap error") - - // Attempt to unwrap the error to a specific type - ne := &ErrorX{} - if !errors.As(y, &ne) { - t.Fatal("expected to be able to unwrap") - } - - // Wrap the specific error type into another error and try unwrapping again - wrapped := Wrap(ne, "this is a wrapped error") - if !errors.As(wrapped, &ne) { - t.Fatal("expected to be able to unwrap") - } - - // Combine multiple errors into a multierror and attempt to unwrap to the specific type - errs := []error{ - stderrors.New("this is a std error"), - x, - errors.New("this is a pkg error"), - } - multi := multierr.Combine(errs...) - if !errors.As(multi, &ne) { - t.Fatal("expected to be able to unwrap") - } -} - -func TestErrorIs(t *testing.T) { - // Create a new error, wrap it, and check if the original error can be found - x := New("this is a nuclei error").SetClass(ErrClassNetworkPermanent).Build() - y := errors.Wrap(x, "this is a wrap error") - if !errors.Is(y, x) { - t.Fatal("expected to be able to find the original error") - } - - // Wrap the original error with a custom wrapper and check again - wrapped := Wrap(x, "this is a wrapped error") - if !stderrors.Is(wrapped, x) { - t.Fatal("expected to be able to find the original error") - } - - // Combine multiple errors into a multierror and check if the original error can be found - errs := []error{ - stderrors.New("this is a std error"), - x, - errors.New("this is a pkg error"), - } - multi := multierr.Combine(errs...) - if !errors.Is(multi, x) { - t.Fatal("expected to be able to find the original error") - } -} - -func TestErrorUtil(t *testing.T) { - utilErr := errorutil.New("got err while executing http://206.189.19.240:8000/wp-content/plugins/wp-automatic/inc/csv.php <- POST http://206.189.19.240:8000/wp-content/plugins/wp-automatic/inc/csv.php giving up after 2 attempts: Post \"http://206.189.19.240:8000/wp-content/plugins/wp-automatic/inc/csv.php\": [:RUNTIME] ztls fallback failed <- dial tcp 206.189.19.240:8000: connect: connection refused") - x := ErrorX{} - parseError(&x, utilErr) - if len(x.errs) != 3 { - t.Fatal("expected 3 errors") - } - t.Log(x.errs) -} diff --git a/pkg/types/errkit/helpers.go b/pkg/types/errkit/helpers.go deleted file mode 100644 index f210ac14fb..0000000000 --- a/pkg/types/errkit/helpers.go +++ /dev/null @@ -1,108 +0,0 @@ -package errkit - -import "errors" - -// Proxy to StdLib errors.Is -func Is(err error, target error) bool { - return errors.Is(err, target) -} - -// Proxy to StdLib errors.As -func As(err error, target interface{}) bool { - return errors.As(err, target) -} - -// Combine combines multiple errors into a single error -func Combine(errs ...error) error { - if len(errs) == 0 { - return nil - } - x := &ErrorX{} - for _, err := range errs { - if err == nil { - continue - } - parseError(x, err) - } - return x -} - -// Wrap wraps the given error with the message -func Wrap(err error, message string) error { - if err == nil { - return nil - } - x := &ErrorX{} - parseError(x, err) - x.Msgf(message) - return x -} - -// Wrapf wraps the given error with the message -func Wrapf(err error, format string, args ...interface{}) error { - if err == nil { - return nil - } - x := &ErrorX{} - parseError(x, err) - x.Msgf(format, args...) - return x -} - -// Errors returns all underlying errors there were appended or joined -func Errors(err error) []error { - if err == nil { - return nil - } - x := &ErrorX{} - parseError(x, err) - return x.errs -} - -// Append appends given errors and returns a new error -// it ignores all nil errors -func Append(errs ...error) error { - if len(errs) == 0 { - return nil - } - x := &ErrorX{} - for _, err := range errs { - if err == nil { - continue - } - parseError(x, err) - } - return x -} - -// Cause returns the original error that caused this error -func Cause(err error) error { - if err == nil { - return nil - } - x := &ErrorX{} - parseError(x, err) - return x.Cause() -} - -// WithMessage -func WithMessage(err error, message string) error { - if err == nil { - return nil - } - x := &ErrorX{} - parseError(x, err) - x.Msgf(message) - return x -} - -// WithMessagef -func WithMessagef(err error, format string, args ...interface{}) error { - if err == nil { - return nil - } - x := &ErrorX{} - parseError(x, err) - x.Msgf(format, args...) - return x -} diff --git a/pkg/types/errkit/interfaces.go b/pkg/types/errkit/interfaces.go deleted file mode 100644 index 950efb7bbb..0000000000 --- a/pkg/types/errkit/interfaces.go +++ /dev/null @@ -1,32 +0,0 @@ -package errkit - -import "encoding/json" - -var ( - _ json.Marshaler = &ErrorX{} - _ JoinedError = &ErrorX{} - _ CauseError = &ErrorX{} - _ ComparableError = &ErrorX{} - _ error = &ErrorX{} -) - -// below contains all interfaces that are implemented by ErrorX which -// makes it compatible with other error packages - -// JoinedError is implemented by errors that are joined by Join -type JoinedError interface { - // Unwrap returns the underlying error - Unwrap() []error -} - -// CauseError is implemented by errors that have a cause -type CauseError interface { - // Cause return the original error that caused this without any wrapping - Cause() error -} - -// ComparableError is implemented by errors that can be compared -type ComparableError interface { - // Is checks if current error contains given error - Is(err error) bool -} diff --git a/pkg/types/nucleierr/kinds.go b/pkg/types/nucleierr/kinds.go new file mode 100644 index 0000000000..9042beaade --- /dev/null +++ b/pkg/types/nucleierr/kinds.go @@ -0,0 +1,28 @@ +package nucleierr + +import ( + "strings" + + "github.com/projectdiscovery/utils/errkit" +) + +var ( + // ErrTemplateLogic are errors that occured due to missing variable or something similar in template logic + // so this is more of a virtual error that is expected due to template logic + ErrTemplateLogic = errkit.NewPrimitiveErrKind("TemplateLogic", "Error expected due to template logic", isTemplateLogicKind) +) + +// isTemplateLogicKind checks if an error is of template logic kind +func isTemplateLogicKind(err *errkit.ErrorX) bool { + if err == nil || err.Cause() == nil { + return false + } + v := err.Cause().Error() + switch { + case strings.Contains(v, "timeout annotation deadline exceeded"): + return true + case strings.Contains(v, "stop execution due to unresolved variables"): + return true + } + return false +}