Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion cmd/integration-test/library.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,9 @@ func executeNucleiAsLibrary(templatePath, templateURL string) ([]string, error)
if err != nil {
return nil, errors.Wrap(err, "could not create loader")
}
store.Load()
if err := store.Load(); err != nil {
return nil, errors.Wrap(err, "could not load templates")
}

_ = engine.Execute(context.Background(), store.Templates(), provider.NewSimpleInputProviderWithUrls(defaultOpts.ExecutionId, templateURL))
engine.WorkPool().Wait() // Wait for the scan to finish
Expand Down
11 changes: 7 additions & 4 deletions internal/runner/lazy.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,10 @@ func GetAuthTmplStore(opts *types.Options, catalog catalog.Catalog, execOpts *pr
// GetLazyAuthFetchCallback returns a lazy fetch callback for auth secrets
func GetLazyAuthFetchCallback(opts *AuthLazyFetchOptions) authx.LazyFetchSecret {
return func(d *authx.Dynamic) error {
tmpls := opts.TemplateStore.LoadTemplates([]string{d.TemplatePath})
tmpls, err := opts.TemplateStore.LoadTemplates([]string{d.TemplatePath})
if err != nil {
return fmt.Errorf("failed to load templates: %w", err)
}
if len(tmpls) == 0 {
return fmt.Errorf("%w for path: %s", disk.ErrNoTemplatesFound, d.TemplatePath)
}
Expand Down Expand Up @@ -140,9 +143,9 @@ func GetLazyAuthFetchCallback(opts *AuthLazyFetchOptions) authx.LazyFetchSecret
// log result of template in result file/screen
_ = writer.WriteResult(e, opts.ExecOpts.Output, opts.ExecOpts.Progress, opts.ExecOpts.IssuesClient)
}
_, err := tmpl.Executer.ExecuteWithResults(ctx)
if err != nil {
finalErr = err
_, execErr := tmpl.Executer.ExecuteWithResults(ctx)
if execErr != nil {
finalErr = execErr
}
// store extracted result in auth context
d.Extracted = data
Expand Down
4 changes: 3 additions & 1 deletion internal/runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -660,7 +660,9 @@ func (r *Runner) RunEnumeration() error {
}
return nil // exit
}
store.Load()
if err := store.Load(); err != nil {
return err
}
// TODO: remove below functions after v3 or update warning messages
templates.PrintDeprecatedProtocolNameMsgIfApplicable(r.options.Silent, r.options.Verbose)

Expand Down
4 changes: 3 additions & 1 deletion internal/server/nuclei_sdk.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,9 @@ func newNucleiExecutor(opts *NucleiExecutorOptions) (*nucleiExecutor, error) {
if err != nil {
return nil, errors.Wrap(err, "Could not create loader options.")
}
store.Load()
if err := store.Load(); err != nil {
return nil, errors.Wrap(err, "Could not load templates.")
}

return &nucleiExecutor{
engine: executorEngine,
Expand Down
4 changes: 3 additions & 1 deletion lib/multi.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,9 @@ func (e *ThreadSafeNucleiEngine) ExecuteNucleiWithOptsCtx(ctx context.Context, t
if err != nil {
return errkit.Wrapf(err, "Could not create loader client: %s", err)
}
store.Load()
if err := store.Load(); err != nil {
return errkit.Wrapf(err, "Could not load templates: %s", err)
}

inputProvider := provider.NewSimpleInputProviderWithUrls(e.eng.opts.ExecutionId, targets...)

Expand Down
4 changes: 3 additions & 1 deletion lib/sdk.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,9 @@ func (e *NucleiEngine) LoadAllTemplates() error {
if err != nil {
return errkit.Wrapf(err, "Could not create loader client: %s", err)
}
e.store.Load()
if err := e.store.Load(); err != nil {
return errkit.Wrapf(err, "Could not load templates: %s", err)
}
Comment on lines +113 to +115
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Propagate the new loader error; internal callers still drop it and can still crash.

Great change at Line 113-Line 115, but the new error is still ignored at Line 123, Line 131, and Line 258 (_ = e.LoadAllTemplates()). If loading fails before e.store is ready, subsequent e.store.Templates() / e.store.Workflows() dereferences can panic, and ExecuteCallbackWithCtx may mask the real failure.

Suggested fix (propagate/guard load failures)
 func (e *NucleiEngine) GetTemplates() []*templates.Template {
 	if !e.templatesLoaded {
-		_ = e.LoadAllTemplates()
+		if err := e.LoadAllTemplates(); err != nil {
+			return nil
+		}
 	}
+	if e.store == nil {
+		return nil
+	}
 	return e.store.Templates()
 }

 func (e *NucleiEngine) GetWorkflows() []*templates.Template {
 	if !e.templatesLoaded {
-		_ = e.LoadAllTemplates()
+		if err := e.LoadAllTemplates(); err != nil {
+			return nil
+		}
 	}
+	if e.store == nil {
+		return nil
+	}
 	return e.store.Workflows()
 }

 func (e *NucleiEngine) ExecuteCallbackWithCtx(ctx context.Context, callback ...func(event *output.ResultEvent)) error {
 	if !e.templatesLoaded {
-		_ = e.LoadAllTemplates()
+		if err := e.LoadAllTemplates(); err != nil {
+			return err
+		}
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/sdk.go` around lines 113 - 115, The new loader error returned by
e.store.Load() must be propagated and not ignored: update callers that currently
ignore e.LoadAllTemplates() (notably the places using `_ =
e.LoadAllTemplates()`) to check and return or handle its error, and add guards
before dereferencing e.store (calls to e.store.Templates() and
e.store.Workflows()) so they only run when LoadAllTemplates succeeded; also
ensure ExecuteCallbackWithCtx surfaces the underlying load error instead of
masking it by returning early or wrapping the real error. Target the
functions/methods that call e.LoadAllTemplates(), any uses of
e.store.Templates()/e.store.Workflows(), and ExecuteCallbackWithCtx to propagate
or guard against load failures.

e.templatesLoaded = true
return nil
}
Expand Down
20 changes: 13 additions & 7 deletions pkg/catalog/loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,9 +333,14 @@ func (store *Store) RegisterPreprocessor(preprocessor templates.Preprocessor) {

// Load loads all the templates from a store, performs filtering and returns
// the complete compiled templates for a nuclei execution configuration.
func (store *Store) Load() {
store.templates = store.LoadTemplates(store.finalTemplates)
func (store *Store) Load() error {
templates, err := store.LoadTemplates(store.finalTemplates)
if err != nil {
return err
}
store.templates = templates
store.workflows = store.LoadWorkflows(store.finalWorkflows)
return nil
}

var templateIDPathMap map[string]string
Expand Down Expand Up @@ -637,7 +642,7 @@ func isParsingError(store *Store, message string, template string, err error) bo
}

// LoadTemplates takes a list of templates and returns paths for them
func (store *Store) LoadTemplates(templatesList []string) []*templates.Template {
func (store *Store) LoadTemplates(templatesList []string) ([]*templates.Template, error) {
return store.LoadTemplatesWithTags(templatesList, nil)
}

Expand Down Expand Up @@ -668,7 +673,8 @@ func (store *Store) LoadWorkflows(workflowsList []string) []*templates.Template

// LoadTemplatesWithTags takes a list of templates and extra tags
// returning templates that match.
func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) []*templates.Template {
// Returns an error if dialers are not initialized for the given execution ID.
func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) ([]*templates.Template, error) {
defer store.saveMetadataIndexOnce()

indexFilter := store.indexFilter
Expand Down Expand Up @@ -708,7 +714,7 @@ func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) []*templ

wgLoadTemplates, errWg := syncutil.New(syncutil.WithSize(concurrency))
if errWg != nil {
panic("could not create wait group")
return nil, fmt.Errorf("could not create wait group: %w", errWg)
}

if typesOpts.ExecutionId == "" {
Expand All @@ -717,7 +723,7 @@ func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) []*templ

dialers := protocolstate.GetDialersWithId(typesOpts.ExecutionId)
if dialers == nil {
panic("dialers with executionId " + typesOpts.ExecutionId + " not found")
return nil, fmt.Errorf("dialers with executionId %s not found", typesOpts.ExecutionId)
}

for _, templatePath := range includedTemplates {
Expand Down Expand Up @@ -852,7 +858,7 @@ func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) []*templ
return loadedTemplates.Slice[i].Path < loadedTemplates.Slice[j].Path
})

return loadedTemplates.Slice
return loadedTemplates.Slice, nil
}

// IsHTTPBasedProtocolUsed returns true if http/headless protocol is being used for
Expand Down
14 changes: 7 additions & 7 deletions pkg/catalog/loader/loader_bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func BenchmarkLoadTemplates(b *testing.B) {
b.ReportAllocs()

for b.Loop() {
_ = store.LoadTemplates([]string{config.DefaultConfig.TemplatesDirectory})
_, _ = store.LoadTemplates([]string{config.DefaultConfig.TemplatesDirectory})
}
})

Expand All @@ -89,7 +89,7 @@ func BenchmarkLoadTemplates(b *testing.B) {
b.ReportAllocs()

for b.Loop() {
_ = store.LoadTemplates([]string{config.DefaultConfig.TemplatesDirectory})
_, _ = store.LoadTemplates([]string{config.DefaultConfig.TemplatesDirectory})
}
})

Expand All @@ -107,7 +107,7 @@ func BenchmarkLoadTemplates(b *testing.B) {
b.ReportAllocs()

for b.Loop() {
_ = store.LoadTemplates([]string{config.DefaultConfig.TemplatesDirectory})
_, _ = store.LoadTemplates([]string{config.DefaultConfig.TemplatesDirectory})
}
})

Expand All @@ -125,7 +125,7 @@ func BenchmarkLoadTemplates(b *testing.B) {
b.ReportAllocs()

for b.Loop() {
_ = store.LoadTemplates([]string{config.DefaultConfig.TemplatesDirectory})
_, _ = store.LoadTemplates([]string{config.DefaultConfig.TemplatesDirectory})
}
})

Expand All @@ -143,7 +143,7 @@ func BenchmarkLoadTemplates(b *testing.B) {
b.ReportAllocs()

for b.Loop() {
_ = store.LoadTemplates([]string{config.DefaultConfig.TemplatesDirectory})
_, _ = store.LoadTemplates([]string{config.DefaultConfig.TemplatesDirectory})
}
})

Expand All @@ -161,7 +161,7 @@ func BenchmarkLoadTemplates(b *testing.B) {
b.ReportAllocs()

for b.Loop() {
_ = store.LoadTemplates([]string{config.DefaultConfig.TemplatesDirectory})
_, _ = store.LoadTemplates([]string{config.DefaultConfig.TemplatesDirectory})
}
})

Expand All @@ -181,7 +181,7 @@ func BenchmarkLoadTemplates(b *testing.B) {
b.ReportAllocs()

for b.Loop() {
_ = store.LoadTemplates([]string{config.DefaultConfig.TemplatesDirectory})
_, _ = store.LoadTemplates([]string{config.DefaultConfig.TemplatesDirectory})
}
})
}
Expand Down
5 changes: 4 additions & 1 deletion pkg/protocols/common/automaticscan/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ func getTemplateDirs(opts Options) ([]string, error) {

// LoadTemplatesWithTags loads and returns templates with given tags
func LoadTemplatesWithTags(opts Options, templateDirs []string, tags []string, logInfo bool) ([]*templates.Template, error) {
finalTemplates := opts.Store.LoadTemplatesWithTags(templateDirs, tags)
finalTemplates, err := opts.Store.LoadTemplatesWithTags(templateDirs, tags)
if err != nil {
return nil, errors.Wrap(err, "could not load templates")
}
if len(finalTemplates) == 0 {
return nil, errors.New("could not find any templates with tech tag")
}
Expand Down
Loading