mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2026-11-08 10:10:18 -10:00
Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd6657c332 | ||
|
|
7f9c303d83 | ||
|
|
426b847866 | ||
|
|
dcfc4aced0 | ||
|
|
fdf7f5fde3 | ||
|
|
f19e771955 | ||
|
|
9f5ab95cc6 | ||
|
|
13a505b3c7 |
No files matched your search
@@ -498,6 +498,7 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.StringVar(&memProfile, "profile-mem", "", "generate memory (heap) profile & trace files"),
|
||||
flagSet.BoolVar(&options.VerboseVerbose, "vv", false, "display templates loaded for scan"),
|
||||
flagSet.BoolVarP(&options.ShowVarDump, "show-var-dump", "svd", false, "show variables dump for debugging"),
|
||||
flagSet.BoolVarP(&options.LogUnresolved, "log-unresolved", "lu", false, "log per-request warnings for unresolved template variables (summary is always printed)"),
|
||||
flagSet.IntVarP(&options.VarDumpLimit, "var-dump-limit", "vdl", 255, "limit the number of characters displayed in var dump"),
|
||||
flagSet.BoolVarP(&options.EnablePprof, "enable-pprof", "ep", false, "enable pprof debugging server"),
|
||||
flagSet.CallbackVarP(printTemplateVersion, "templates-version", "tv", "shows the version of the installed nuclei-templates"),
|
||||
|
||||
@@ -910,6 +910,15 @@ func (r *Runner) RunEnumeration() error {
|
||||
r.progress.Stop()
|
||||
timeTaken := time.Since(now)
|
||||
|
||||
if counter, ok := r.progress.(progress.UnresolvedCounter); ok && counter.SkippedUnresolved() > 0 {
|
||||
skipped := counter.SkippedUnresolved()
|
||||
msg := fmt.Sprintf("Skipped %d request(s) due to unresolved variables", skipped)
|
||||
if !r.options.LogUnresolved {
|
||||
msg += " (use -log-unresolved to see details)"
|
||||
}
|
||||
r.Logger.Info().Msg(msg)
|
||||
}
|
||||
|
||||
// Print pool/tracker stats if available (single dialers lookup, reads under lock)
|
||||
if dialers := protocolstate.GetDialersWithId(r.options.ExecutionId); dialers != nil {
|
||||
dialers.Lock()
|
||||
|
||||
+28
-4
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/marker"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/render"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/unresolvedvars"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils/json"
|
||||
"github.com/projectdiscovery/retryablehttp-go"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
@@ -92,6 +93,16 @@ type GeneratedRequest struct {
|
||||
// Input is not thread safe and should not be shared between concurrent
|
||||
// goroutines.
|
||||
func (rule *Rule) Execute(input *ExecuteRuleInput) (err error) {
|
||||
// a rule with unresolved variables can never build a request, so it is
|
||||
// skipped and counted like any other request with unresolved variables
|
||||
if rule.unresolvedErr != nil {
|
||||
var target string
|
||||
if input.Input != nil && input.Input.MetaInput != nil {
|
||||
target = input.Input.MetaInput.Input
|
||||
}
|
||||
unresolvedvars.Skip(rule.options.Progress, rule.options.Options, rule.options.TemplateID, target, rule.unresolvedErr)
|
||||
return nil
|
||||
}
|
||||
if !rule.isInputURLValid(input.Input) {
|
||||
return errkit.Newf("rule not applicable: invalid input url: %v", input.Input.MetaInput.Input)
|
||||
}
|
||||
@@ -459,6 +470,8 @@ func (rule *Rule) executeRuleValues(input *ExecuteRuleInput, ruleComponent compo
|
||||
|
||||
// Compile compiles a fuzzing rule and initializes it for operation
|
||||
func (rule *Rule) Compile(generator *generators.PayloadGenerator, options *protocols.ExecutorOptions) error {
|
||||
// a recompiled rule must not keep the result of an earlier compile
|
||||
rule.unresolvedErr = nil
|
||||
// If a payload generator is specified from base request, use it
|
||||
// for payload values.
|
||||
if generator != nil {
|
||||
@@ -506,7 +519,7 @@ func (rule *Rule) Compile(generator *generators.PayloadGenerator, options *proto
|
||||
for _, key := range rule.Keys {
|
||||
evaluatedKey, err := rule.evaluateVars(key)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not evaluate key")
|
||||
return rule.compileVarsError(err, "could not evaluate key")
|
||||
}
|
||||
|
||||
rule.keysMap[strings.ToLower(evaluatedKey)] = struct{}{}
|
||||
@@ -516,7 +529,7 @@ func (rule *Rule) Compile(generator *generators.PayloadGenerator, options *proto
|
||||
for _, value := range rule.ValuesRegex {
|
||||
evaluatedValue, err := rule.evaluateVars(value)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not evaluate value regex")
|
||||
return rule.compileVarsError(err, "could not evaluate value regex")
|
||||
}
|
||||
|
||||
compiled, err := regexp.Compile(evaluatedValue)
|
||||
@@ -531,7 +544,7 @@ func (rule *Rule) Compile(generator *generators.PayloadGenerator, options *proto
|
||||
for _, value := range rule.KeysRegex {
|
||||
evaluatedValue, err := rule.evaluateVars(value)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not evaluate key regex")
|
||||
return rule.compileVarsError(err, "could not evaluate key regex")
|
||||
}
|
||||
|
||||
compiled, err := regexp.Compile(evaluatedValue)
|
||||
@@ -553,7 +566,7 @@ func (rule *Rule) Compile(generator *generators.PayloadGenerator, options *proto
|
||||
|
||||
evalReplaceRegex, err := rule.evaluateVars(rule.ReplaceRegex)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not evaluate replace regex")
|
||||
return rule.compileVarsError(err, "could not evaluate replace regex")
|
||||
}
|
||||
|
||||
compiled, err := regexp.Compile(evalReplaceRegex)
|
||||
@@ -566,3 +579,14 @@ func (rule *Rule) Compile(generator *generators.PayloadGenerator, options *proto
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// compileVarsError returns the error for a rule value that failed to evaluate
|
||||
// at compile time. Unresolved variables do not fail the template: the rule is
|
||||
// kept and every execution is skipped and counted at scan time instead.
|
||||
func (rule *Rule) compileVarsError(err error, message string) error {
|
||||
if unresolvedvars.Is(err) {
|
||||
rule.unresolvedErr = errors.Wrap(err, message)
|
||||
return nil
|
||||
}
|
||||
return errors.Wrap(err, message)
|
||||
}
|
||||
@@ -100,6 +100,9 @@ type Rule struct {
|
||||
replaceRegex *regexp.Regexp `yaml:"-" json:"-"`
|
||||
options *protocols.ExecutorOptions
|
||||
generator *generators.PayloadGenerator
|
||||
// unresolvedErr is set when a rule value has unresolved variables at
|
||||
// compile time; executions of the rule are then skipped and counted
|
||||
unresolvedErr error
|
||||
}
|
||||
|
||||
// ruleType is the type of rule enum declaration
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package fuzz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/progress"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/variables"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type unresolvedProgress struct {
|
||||
progress.Progress
|
||||
skipped atomic.Int64
|
||||
}
|
||||
|
||||
func (p *unresolvedProgress) IncrementSkippedUnresolved(count int64) { p.skipped.Add(count) }
|
||||
func (p *unresolvedProgress) SkippedUnresolved() uint64 { return uint64(p.skipped.Load()) }
|
||||
|
||||
func TestUnresolvedRuleVariablesAreSkipped(t *testing.T) {
|
||||
tests := map[string]*Rule{
|
||||
"keys": {Part: "query", Keys: []string{`{{to_lower("{{nosuchkey}}")}}`}},
|
||||
"keys-regex": {Part: "query", KeysRegex: []string{`{{to_lower("{{nosuchkey}}")}}`}},
|
||||
"values-regex": {Part: "query", ValuesRegex: []string{`{{to_lower("{{nosuchkey}}")}}`}},
|
||||
"replace-regex": {Part: "query", Type: "replace-regex", ReplaceRegex: `{{to_lower("{{nosuchkey}}")}}`},
|
||||
}
|
||||
for name, rule := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
prog := &unresolvedProgress{}
|
||||
executorOpts := &protocols.ExecutorOptions{
|
||||
TemplateID: "unresolved-fuzz",
|
||||
Variables: variables.Variable{InsertionOrderedStringMap: *utils.NewEmptyInsertionOrderedStringMap(0)},
|
||||
Constants: map[string]interface{}{},
|
||||
Options: &types.Options{},
|
||||
Progress: prog,
|
||||
}
|
||||
// the template still loads
|
||||
require.NoError(t, rule.Compile(nil, executorOpts))
|
||||
|
||||
input := &ExecuteRuleInput{Input: contextargs.NewWithInput(context.Background(), "http://127.0.0.1/?a=1")}
|
||||
require.NoError(t, rule.Execute(input))
|
||||
require.NoError(t, rule.Execute(input))
|
||||
require.Equal(t, uint64(2), prog.SkippedUnresolved())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecompiledRuleClearsUnresolvedError(t *testing.T) {
|
||||
rule := &Rule{Part: "query", Keys: []string{`{{to_lower("{{nosuchkey}}")}}`}}
|
||||
prog := &unresolvedProgress{}
|
||||
executorOpts := &protocols.ExecutorOptions{
|
||||
TemplateID: "unresolved-fuzz",
|
||||
Variables: variables.Variable{InsertionOrderedStringMap: *utils.NewEmptyInsertionOrderedStringMap(0)},
|
||||
Constants: map[string]interface{}{},
|
||||
Options: &types.Options{},
|
||||
Progress: prog,
|
||||
}
|
||||
require.NoError(t, rule.Compile(nil, executorOpts))
|
||||
require.Error(t, rule.unresolvedErr)
|
||||
|
||||
// the same rule now compiles with values that resolve
|
||||
rule.Keys = []string{"a"}
|
||||
require.NoError(t, rule.Compile(nil, executorOpts))
|
||||
require.NoError(t, rule.unresolvedErr)
|
||||
|
||||
input := &ExecuteRuleInput{Input: contextargs.NewWithInput(context.Background(), "http://127.0.0.1/?a=1")}
|
||||
_ = rule.Execute(input)
|
||||
require.Zero(t, prog.SkippedUnresolved())
|
||||
}
|
||||
@@ -34,6 +34,17 @@ type Progress interface {
|
||||
IncrementFailedRequestsBy(count int64)
|
||||
}
|
||||
|
||||
// UnresolvedCounter is implemented by a Progress that also counts requests
|
||||
// skipped because template variables could not be resolved. It is separate
|
||||
// from Progress so that custom Progress implementations keep compiling.
|
||||
type UnresolvedCounter interface {
|
||||
// IncrementSkippedUnresolved increments the count of requests skipped because
|
||||
// template variables could not be resolved.
|
||||
IncrementSkippedUnresolved(count int64)
|
||||
// SkippedUnresolved returns how many requests were skipped for unresolved variables.
|
||||
SkippedUnresolved() uint64
|
||||
}
|
||||
|
||||
// TaskTracker is implemented by a Progress that also counts template
|
||||
// executions. The engine counts one task for each template it runs on a
|
||||
// target, and one for each self-contained template. Unlike the request total,
|
||||
@@ -47,8 +58,9 @@ type TaskTracker interface {
|
||||
}
|
||||
|
||||
var (
|
||||
_ Progress = &StatsTicker{}
|
||||
_ TaskTracker = &StatsTicker{}
|
||||
_ Progress = &StatsTicker{}
|
||||
_ TaskTracker = &StatsTicker{}
|
||||
_ UnresolvedCounter = &StatsTicker{}
|
||||
)
|
||||
|
||||
// StatsTicker is a progress instance for showing program stats
|
||||
@@ -99,6 +111,7 @@ func (p *StatsTicker) Init(hostCount int64, rulesCount int, requestCount int64)
|
||||
p.stats.AddCounter("requests", uint64(0))
|
||||
p.stats.AddCounter("errors", uint64(0))
|
||||
p.stats.AddCounter("matched", uint64(0))
|
||||
p.stats.AddCounter("unresolved", uint64(0))
|
||||
p.stats.AddCounter("total", uint64(requestCount))
|
||||
p.stats.AddCounter("tasks", uint64(0))
|
||||
p.stats.AddCounter("totalTasks", uint64(0))
|
||||
@@ -174,6 +187,17 @@ func (p *StatsTicker) IncrementFailedRequestsBy(count int64) {
|
||||
p.stats.IncrementCounter("errors", int(count))
|
||||
}
|
||||
|
||||
// IncrementSkippedUnresolved increments requests skipped due to unresolved variables.
|
||||
func (p *StatsTicker) IncrementSkippedUnresolved(count int64) {
|
||||
p.stats.IncrementCounter("unresolved", int(count))
|
||||
}
|
||||
|
||||
// SkippedUnresolved returns the number of requests skipped due to unresolved variables.
|
||||
func (p *StatsTicker) SkippedUnresolved() uint64 {
|
||||
count, _ := p.stats.GetCounter("unresolved")
|
||||
return count
|
||||
}
|
||||
|
||||
func (p *StatsTicker) makePrintCallback() func(stats clistats.StatisticsClient) interface{} {
|
||||
return func(stats clistats.StatisticsClient) interface{} {
|
||||
builder := &strings.Builder{}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package progress
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSkippedUnresolvedCounter(t *testing.T) {
|
||||
p, err := NewStatsTicker(-1, false, false, false, 0)
|
||||
require.NoError(t, err)
|
||||
p.Init(1, 1, 10)
|
||||
counter, ok := p.(UnresolvedCounter)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, uint64(0), counter.SkippedUnresolved())
|
||||
|
||||
counter.IncrementSkippedUnresolved(1)
|
||||
counter.IncrementSkippedUnresolved(2)
|
||||
require.Equal(t, uint64(3), counter.SkippedUnresolved())
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package expressions
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
@@ -9,6 +10,10 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/operators/common/dsl"
|
||||
)
|
||||
|
||||
// ErrUnresolvedVariables is wrapped by every error that reports unresolved
|
||||
// template variables, so callers can classify it with errors.Is.
|
||||
var ErrUnresolvedVariables = errors.New("unresolved variables")
|
||||
|
||||
var (
|
||||
numericalExpressionRegex = regexp.MustCompile(`^[0-9+\-/\W]+$`)
|
||||
unresolvedVariablesRegex = regexp.MustCompile(`(?:%7[B|b]|\{){2}([^}]+)(?:%7[D|d]|\}){2}["'\)\}]*`)
|
||||
@@ -38,7 +43,7 @@ func ContainsUnresolvedVariables(items ...string) error {
|
||||
unresolvedVariables = append(unresolvedVariables, match[1])
|
||||
}
|
||||
if len(unresolvedVariables) > 0 {
|
||||
return errors.New("unresolved variables found: " + strings.Join(unresolvedVariables, ","))
|
||||
return fmt.Errorf("%w found: %s", ErrUnresolvedVariables, strings.Join(unresolvedVariables, ","))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +77,7 @@ func ContainsVariablesWithNames(names map[string]interface{}, items ...string) e
|
||||
}
|
||||
}
|
||||
if len(unresolvedVariables) > 0 {
|
||||
return errors.New("unresolved variables with values found: " + strings.Join(unresolvedVariables, ","))
|
||||
return fmt.Errorf("%w with values found: %s", ErrUnresolvedVariables, strings.Join(unresolvedVariables, ","))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +114,7 @@ func ContainsVariablesWithIgnoreList(skipNames map[string]interface{}, items ...
|
||||
}
|
||||
|
||||
if len(unresolvedVariables) > 0 {
|
||||
return errors.New("unresolved variables with values found: " + strings.Join(unresolvedVariables, ","))
|
||||
return fmt.Errorf("%w with values found: %s", ErrUnresolvedVariables, strings.Join(unresolvedVariables, ","))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -40,7 +40,12 @@ func TestUnresolvedVariablesCheck(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
err := ContainsUnresolvedVariables(test.data)
|
||||
require.Equal(t, test.err, err, "could not get unresolved variables")
|
||||
if test.err == nil {
|
||||
require.NoError(t, err, "could not get unresolved variables")
|
||||
continue
|
||||
}
|
||||
require.EqualError(t, err, test.err.Error(), "could not get unresolved variables")
|
||||
require.ErrorIs(t, err, ErrUnresolvedVariables)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package unresolvedvars
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/progress"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/expressions"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
)
|
||||
|
||||
// Is reports whether err is (or wraps) an unresolved-variables error.
|
||||
func Is(err error) bool {
|
||||
return errors.Is(err, expressions.ErrUnresolvedVariables)
|
||||
}
|
||||
|
||||
// Skip records a request skipped due to unresolved variables and optionally logs it.
|
||||
// Per-request warnings are muted unless options.LogUnresolved is set; use the end-of-scan
|
||||
// summary from progress.UnresolvedCounter instead.
|
||||
func Skip(prog progress.Progress, options *types.Options, templateID, target string, err error) {
|
||||
if counter, ok := prog.(progress.UnresolvedCounter); ok {
|
||||
counter.IncrementSkippedUnresolved(1)
|
||||
}
|
||||
// The user asked for these lines, so they are logged at info level: the
|
||||
// default log level hides warnings unless -v is set.
|
||||
if options == nil || !options.LogUnresolved {
|
||||
return
|
||||
}
|
||||
// self-contained requests have no input target
|
||||
if target == "" {
|
||||
gologger.Info().Msgf("[%s] Could not make request: %v\n", templateID, err)
|
||||
return
|
||||
}
|
||||
gologger.Info().Msgf("[%s] Could not make request for %s: %v\n", templateID, target, err)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package unresolvedvars
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/expressions"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestIs(t *testing.T) {
|
||||
require.True(t, Is(expressions.ContainsUnresolvedVariables("{{user}}")))
|
||||
require.True(t, Is(expressions.ContainsVariablesWithIgnoreList(nil, "{{user}}")))
|
||||
require.True(t, Is(fmt.Errorf("%w `%v` found in request", expressions.ErrUnresolvedVariables, "{{user}}")))
|
||||
require.True(t, Is(errkit.Wrap(expressions.ContainsUnresolvedVariables("{{user}}"), "could not build request")))
|
||||
// an unrelated error that only mentions the phrase is not a skip
|
||||
require.False(t, Is(errors.New("unresolved variables found: user")))
|
||||
require.False(t, Is(errors.New("connection refused")))
|
||||
require.False(t, Is(nil))
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/helpers/eventcreator"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/helpers/responsehighlighter"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/unresolvedvars"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/utils/vardump"
|
||||
protocolutils "github.com/projectdiscovery/nuclei/v3/pkg/protocols/utils"
|
||||
templateTypes "github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
|
||||
@@ -140,7 +141,11 @@ func (request *Request) execute(input *contextargs.Context, domain string, metad
|
||||
// to resolve. See https://github.com/projectdiscovery/nuclei/issues/7374.
|
||||
resolverVars := generators.MergeMaps(vars, metadata)
|
||||
if dnsClient, varErr = request.getDnsClient(request.options, resolverVars); varErr != nil {
|
||||
gologger.Warning().Msgf("[%s] Could not make dns request for %s: %v\n", request.options.TemplateID, domain, varErr)
|
||||
if unresolvedvars.Is(varErr) {
|
||||
unresolvedvars.Skip(request.options.Progress, request.options.Options, request.options.TemplateID, domain, varErr)
|
||||
} else {
|
||||
gologger.Warning().Msgf("[%s] Could not make dns request for %s: %v\n", request.options.TemplateID, domain, varErr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -154,7 +159,7 @@ func (request *Request) execute(input *contextargs.Context, domain string, metad
|
||||
|
||||
requestString := compiledRequest.String()
|
||||
if varErr := expressions.ContainsUnresolvedVariables(requestString); varErr != nil {
|
||||
gologger.Warning().Msgf("[%s] Could not make dns request for %s: %v\n", request.options.TemplateID, question, varErr)
|
||||
unresolvedvars.Skip(request.options.Progress, request.options.Options, request.options.TemplateID, question, varErr)
|
||||
return nil
|
||||
}
|
||||
if request.options.Options.Debug || request.options.Options.DebugRequests || request.options.Options.StoreResponse {
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/hostratelimit"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/interactsh"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/render"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/unresolvedvars"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/headless/engine"
|
||||
protocolutils "github.com/projectdiscovery/nuclei/v3/pkg/protocols/utils"
|
||||
templateTypes "github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
|
||||
@@ -185,6 +186,10 @@ func (request *Request) executeRequestWithPayloads(input *contextargs.Context, p
|
||||
timeStart := time.Now()
|
||||
out, createdPage, err := instance.Run(input, request.Steps, payloads, options)
|
||||
runDuration := time.Since(timeStart)
|
||||
if unresolvedvars.Is(err) {
|
||||
unresolvedvars.Skip(request.options.Progress, request.options.Options, request.options.TemplateID, input.MetaInput.Input, err)
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
request.options.Output.Request(request.options.TemplatePath, input.MetaInput.Input, request.Type().String(), err)
|
||||
request.options.Progress.IncrementFailedRequestsBy(1)
|
||||
|
||||
@@ -339,7 +339,7 @@ func (r *requestGenerator) makeSelfContainedRequest(ctx context.Context, input *
|
||||
}
|
||||
|
||||
if err := expressions.ContainsUnresolvedVariables(parts[1]); err != nil && !r.request.SkipVariablesCheck {
|
||||
return nil, errkit.Newf("unresolved variables `%v` found in request", parts[1])
|
||||
return nil, fmt.Errorf("%w `%v` found in request", expressions.ErrUnresolvedVariables, parts[1])
|
||||
}
|
||||
|
||||
parsed, err := urlutil.ParseURL(parts[1], true)
|
||||
@@ -367,7 +367,7 @@ func (r *requestGenerator) makeSelfContainedRequest(ctx context.Context, input *
|
||||
if err := expressions.ContainsUnresolvedVariables(data); err != nil && !r.request.SkipVariablesCheck {
|
||||
// early exit: if there are any unresolved variables in `path` after evaluation
|
||||
// then return early since this will definitely fail
|
||||
return nil, errkit.Newf("unresolved variables `%v` found in request", data)
|
||||
return nil, fmt.Errorf("%w `%v` found in request", expressions.ErrUnresolvedVariables, data)
|
||||
}
|
||||
|
||||
urlx, err := urlutil.ParseURL(data, true)
|
||||
|
||||
@@ -39,6 +39,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/interactsh"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/marker"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/unresolvedvars"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/utils/vardump"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/http/httpclientpool"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/http/httputils"
|
||||
@@ -683,6 +684,10 @@ executionLoop:
|
||||
if err == types.ErrNoMoreRequests {
|
||||
return false, true, nil, nil
|
||||
}
|
||||
if unresolvedvars.Is(err) {
|
||||
unresolvedvars.Skip(request.options.Progress, request.options.Options, request.options.TemplateID, input.MetaInput.Input, err)
|
||||
return false, true, nil, nil
|
||||
}
|
||||
return false, true, nil, err
|
||||
}
|
||||
// ideally if http template used a custom port or hostname
|
||||
@@ -1140,7 +1145,7 @@ func (request *Request) executeRequest(input *contextargs.Context, generatedRequ
|
||||
varErr = expressions.ContainsUnresolvedVariables(item)
|
||||
}
|
||||
if varErr != nil && !request.SkipVariablesCheck {
|
||||
gologger.Warning().Msgf("[%s] Could not make http request for %s: %v\n", request.options.TemplateID, input.MetaInput.Input, varErr)
|
||||
unresolvedvars.Skip(request.options.Progress, request.options.Options, request.options.TemplateID, input.MetaInput.Input, varErr)
|
||||
return ErrMissingVars
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/interactsh"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/render"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/replacer"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/unresolvedvars"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/utils/vardump"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/network/networkclientpool"
|
||||
protocolutils "github.com/projectdiscovery/nuclei/v3/pkg/protocols/utils"
|
||||
@@ -400,7 +401,7 @@ func (request *Request) executeRequestWithPayloads(variables map[string]interfac
|
||||
interactshURLs = result.InteractURLs
|
||||
|
||||
if err := expressions.ContainsUnresolvedVariables(data); err != nil {
|
||||
gologger.Warning().Msgf("[%s] Could not make network request for %s: %v\n", request.options.TemplateID, actualAddress, err)
|
||||
unresolvedvars.Skip(request.options.Progress, request.options.Options, request.options.TemplateID, actualAddress, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -254,6 +254,10 @@ type Options struct {
|
||||
VerboseVerbose bool
|
||||
// ShowVarDump displays variable dump
|
||||
ShowVarDump bool
|
||||
// LogUnresolved enables per-request warnings when a request is skipped because
|
||||
// template variables could not be resolved. By default those skips are counted
|
||||
// and summarized once at the end of the scan instead of flooding the log.
|
||||
LogUnresolved bool
|
||||
// VarDumpLimit limits the number of characters displayed in var dump
|
||||
VarDumpLimit int
|
||||
// No-Color disables the colored output.
|
||||
@@ -704,6 +708,7 @@ func (options *Options) Copy() *Options {
|
||||
Verbose: options.Verbose,
|
||||
VerboseVerbose: options.VerboseVerbose,
|
||||
ShowVarDump: options.ShowVarDump,
|
||||
LogUnresolved: options.LogUnresolved,
|
||||
VarDumpLimit: options.VarDumpLimit,
|
||||
NoColor: options.NoColor,
|
||||
UpdateTemplates: options.UpdateTemplates,
|
||||
|
||||
Reference in new issue
Block a user