From 4331929bd06bb62c2ec649100f7a0df0558685a5 Mon Sep 17 00:00:00 2001 From: vmfunc <59031302+vmfunc@users.noreply.github.com> Date: Wed, 7 Jan 2026 13:07:35 +0100 Subject: [PATCH 1/6] feat(modules): legacy nuclei scan Converted nuclei scan to be able to run as module. --- internal/scan/builtin/nuclei.go | 141 ++++++++++++++++++++++++++++++ internal/scan/builtin/register.go | 4 +- 2 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 internal/scan/builtin/nuclei.go diff --git a/internal/scan/builtin/nuclei.go b/internal/scan/builtin/nuclei.go new file mode 100644 index 0000000..ecff539 --- /dev/null +++ b/internal/scan/builtin/nuclei.go @@ -0,0 +1,141 @@ +/* +·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━· +: : +: █▀ █ █▀▀ · Blazing-fast pentesting suite : +: ▄█ █ █▀ · BSD 3-Clause License : +: : +: (c) 2022-2025 vmfunc (vmfunc), xyzeva, : +: lunchcat alumni & contributors : +: : +·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━· +*/ + +package builtin + +import ( + "context" + "fmt" + "github.com/dropalldatabases/sif/internal/modules" + "github.com/dropalldatabases/sif/internal/scan" +) + +type NucleiModule struct{} + +func (m *NucleiModule) Info() modules.Info { + return modules.Info{ + ID: "nuclei-scan", + Name: "Nuclei Vulnerability Scanner", + Author: "sif", + Severity: "high", + Description: "Runs Nuclei vulnerability scanning templates against target", + Tags: []string{"vuln", "nuclei", "cve"}, + } +} + +func (m *NucleiModule) Type() modules.ModuleType { + return modules.TypeScript +} + +func (m *NucleiModule) Execute(ctx context.Context, target string, opts modules.Options) (*modules.Result, error) { + // Call existing legacy scan.Nuclei function + nucleiResults, err := scan.Nuclei(target, opts.Timeout, opts.Threads, opts.LogDir) + + if err != nil { + return nil, err + } + + result := &modules.Result{ + ModuleID: m.Info().ID, + Target: target, + Findings: make([]modules.Finding, 0, len(nucleiResults)), + } + + // Process nuclei results into module findings + for _, event := range nucleiResults { + severity := "info" + + switch event.Info.SeverityHolder.Severity.String() { + case "critical": + severity = "critical" + case "high": + severity = "high" + case "medium": + severity = "medium" + case "low": + severity = "low" + } + + evidence := fmt.Sprintf("[%s] %s", event.TemplateID, event.Info.Name) + if event.Matched != "" { + evidence = fmt.Sprintf("[%s] %s - matched: %s", event.TemplateID, event.Info.Name, event.Matched) + } + + finding := modules.Finding{ + URL: event.Host, + Severity: severity, + Evidence: evidence, + Extracted: map[string]string{ + "template_id": event.TemplateID, + "template_name": event.Info.Name, + "severity": event.Info.SeverityHolder.Severity.String(), + }, + } + + // Template info + if event.Type != "" { + finding.Extracted["type"] = event.Type + } + + // Matcher name + if event.MatcherName != "" { + finding.Extracted["matcher_name"] = event.MatcherName + } + + // Extractor name + if event.ExtractorName != "" { + finding.Extracted["extractor_name"] = event.ExtractorName + } + + // Matched line/data + if event.Matched != "" { + finding.Extracted["matched"] = event.Matched + } + + // Metadata + if len(event.Info.Metadata) > 0 { + for key, value := range event.Info.Metadata { + finding.Extracted[fmt.Sprintf("metadata_%s", key)] = fmt.Sprintf("%v", value) + } + } + + // Tags + if len(event.Info.Tags.ToSlice()) > 0 { + tagStr := "" + for _, tag := range event.Info.Tags.ToSlice() { + if tagStr != "" { + tagStr += ", " + } + tagStr += tag + } + + finding.Extracted["tags"] = tagStr + } + + // Reference + if len(event.Info.Reference.ToSlice()) > 0 { + refStr := "" + for _, ref := range event.Info.Reference.ToSlice() { + if refStr != "" { + refStr += "; " + } + refStr += ref + } + + finding.Extracted["references"] = refStr + } + + result.Findings = append(result.Findings, finding) + } + + return result, nil +} diff --git a/internal/scan/builtin/register.go b/internal/scan/builtin/register.go index 006cd09..27749c9 100644 --- a/internal/scan/builtin/register.go +++ b/internal/scan/builtin/register.go @@ -12,8 +12,10 @@ package builtin +import "github.com/dropalldatabases/sif/internal/modules" + // Register registers all Go-based built-in scans as modules. // Allows complex Go scans to participate in the module system func Register() { - // Built-in modules will be registered here + modules.Register(&NucleiModule{}) } From 53687e4bd43e5b43663069a3b829da9e961f3754 Mon Sep 17 00:00:00 2001 From: vmfunc <59031302+vmfunc@users.noreply.github.com> Date: Wed, 7 Jan 2026 15:09:49 +0100 Subject: [PATCH 2/6] fix: nuclei scan nil pointer dereference Fixed nil pointer dereference issues in the nuclei scan running as a module --- internal/scan/builtin/nuclei.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/scan/builtin/nuclei.go b/internal/scan/builtin/nuclei.go index ecff539..d584047 100644 --- a/internal/scan/builtin/nuclei.go +++ b/internal/scan/builtin/nuclei.go @@ -109,7 +109,7 @@ func (m *NucleiModule) Execute(ctx context.Context, target string, opts modules. } // Tags - if len(event.Info.Tags.ToSlice()) > 0 { + if !event.Info.Tags.IsEmpty() { tagStr := "" for _, tag := range event.Info.Tags.ToSlice() { if tagStr != "" { @@ -122,7 +122,7 @@ func (m *NucleiModule) Execute(ctx context.Context, target string, opts modules. } // Reference - if len(event.Info.Reference.ToSlice()) > 0 { + if event.Info.Reference != nil && !event.Info.Reference.IsEmpty() { refStr := "" for _, ref := range event.Info.Reference.ToSlice() { if refStr != "" { From 6d6a57a0e0cb512dc7b3872875491785079cd843 Mon Sep 17 00:00:00 2001 From: vmfunc <59031302+vmfunc@users.noreply.github.com> Date: Wed, 7 Jan 2026 17:01:22 +0100 Subject: [PATCH 3/6] fix(nuclei): logdir, headless option and hosterrorscache Set the HostErrorsCache executor option, cache is created but not passed as option. Headless initialization is required even without browser templates. Nuclei expects project file to be set --- internal/scan/nuclei.go | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/internal/scan/nuclei.go b/internal/scan/nuclei.go index 3e97000..5d95f49 100644 --- a/internal/scan/nuclei.go +++ b/internal/scan/nuclei.go @@ -59,6 +59,12 @@ func Nuclei(url string, timeout time.Duration, threads int, logdir string) ([]ou options.TemplateThreads = threads options.Timeout = int(timeout.Seconds()) + if logdir != "" { + options.ProjectPath = logdir + } + + options.Headless = false + // Get templates templates.Install(nucleilog) pwd, err := os.Getwd() @@ -101,14 +107,15 @@ func Nuclei(url string, timeout time.Duration, threads int, logdir string) ([]ou protocolinit.Init(options) executorOpts := protocols.ExecutorOptions{ - Output: outputWriter, - Progress: progressClient, - Catalog: catalog, - Options: options, - IssuesClient: reportingClient, - RateLimiter: ratelimit.New(context.Background(), 150, time.Second), - Interactsh: interactClient, - ResumeCfg: types.NewResumeCfg(), + Output: outputWriter, + Progress: progressClient, + Catalog: catalog, + Options: options, + IssuesClient: reportingClient, + RateLimiter: ratelimit.New(context.Background(), 150, time.Second), + Interactsh: interactClient, + HostErrorsCache: cache, + ResumeCfg: types.NewResumeCfg(), } engine := core.New(options) engine.SetExecuterOptions(executorOpts) From ad9a98b132a7b68ee99fc069d46ba7ca369208a6 Mon Sep 17 00:00:00 2001 From: vmfunc <59031302+vmfunc@users.noreply.github.com> Date: Wed, 7 Jan 2026 19:06:51 +0100 Subject: [PATCH 4/6] fix: colorizer exception Fixed Nuclei giving off exception for missing Colorizer on the executor options. --- internal/scan/nuclei.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/scan/nuclei.go b/internal/scan/nuclei.go index 5d95f49..41d459a 100644 --- a/internal/scan/nuclei.go +++ b/internal/scan/nuclei.go @@ -23,6 +23,7 @@ import ( "github.com/dropalldatabases/sif/internal/nuclei/format" "github.com/dropalldatabases/sif/internal/nuclei/templates" sifoutput "github.com/dropalldatabases/sif/internal/output" + "github.com/logrusorgru/aurora" "github.com/projectdiscovery/nuclei/v2/pkg/catalog/config" "github.com/projectdiscovery/nuclei/v2/pkg/catalog/disk" "github.com/projectdiscovery/nuclei/v2/pkg/catalog/loader" @@ -107,6 +108,7 @@ func Nuclei(url string, timeout time.Duration, threads int, logdir string) ([]ou protocolinit.Init(options) executorOpts := protocols.ExecutorOptions{ + Colorizer: aurora.NewAurora(false), Output: outputWriter, Progress: progressClient, Catalog: catalog, From 748f320e59faace1f9a90752f979c85c5a21c0d7 Mon Sep 17 00:00:00 2001 From: vmfunc <59031302+vmfunc@users.noreply.github.com> Date: Wed, 7 Jan 2026 22:33:49 +0100 Subject: [PATCH 5/6] fix: renamed nuclei module file Renamed the nuclei module file to differentiate from the nuclei legacy scan file. --- internal/scan/builtin/{nuclei.go => nuclei-module.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename internal/scan/builtin/{nuclei.go => nuclei-module.go} (100%) diff --git a/internal/scan/builtin/nuclei.go b/internal/scan/builtin/nuclei-module.go similarity index 100% rename from internal/scan/builtin/nuclei.go rename to internal/scan/builtin/nuclei-module.go From 280e6ad8b0080fb22f8d5729b74a2aca88fd644e Mon Sep 17 00:00:00 2001 From: vmfunc <59031302+vmfunc@users.noreply.github.com> Date: Wed, 7 Jan 2026 22:39:19 +0100 Subject: [PATCH 6/6] fix: rename to snakecase --- internal/scan/builtin/{nuclei-module.go => nuclei_module.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename internal/scan/builtin/{nuclei-module.go => nuclei_module.go} (100%) diff --git a/internal/scan/builtin/nuclei-module.go b/internal/scan/builtin/nuclei_module.go similarity index 100% rename from internal/scan/builtin/nuclei-module.go rename to internal/scan/builtin/nuclei_module.go