Skip to content

Commit 7ed215a

Browse files
committed
refactor: migrate Nuclei global template execution from os/exec to Nuclei SDK for improved integration
1 parent 54fb9d6 commit 7ed215a

3 files changed

Lines changed: 78 additions & 30 deletions

File tree

internal/api/ui_api.go

Lines changed: 10 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ import (
3333
"github.com/h0tak88r/AutoAR/internal/utils"
3434
"github.com/h0tak88r/AutoAR/internal/version"
3535
"github.com/projectdiscovery/dnsx/libs/dnsx"
36+
"github.com/projectdiscovery/nuclei/v3/pkg/output"
37+
"github.com/h0tak88r/AutoAR/internal/scanner/nuclei"
3638
)
3739

3840
// ─────────────────────────────────────────────────────────────────────────────
@@ -602,41 +604,20 @@ func apiRunGlobalNuclei(c *gin.Context) {
602604
os.MkdirAll(outDir, 0755)
603605
outPath := filepath.Join(outDir, "nuclei-global.json")
604606

605-
cmd := exec.Command("nuclei", "-l", tmpFile.Name(), "-t", templatePath, "-c", "50", "-silent", "-jsonl", "-o", outPath)
606-
stdout, err := cmd.StdoutPipe()
607-
if err != nil {
608-
return fmt.Errorf("stdout pipe failed: %w", err)
609-
}
610-
cmd.Stderr = os.Stderr // log errors to console
611-
612-
if err := cmd.Start(); err != nil {
613-
return fmt.Errorf("nuclei start failed: %w", err)
614-
}
615-
616607
matches := 0
617-
scanner := bufio.NewScanner(stdout)
618-
for scanner.Scan() {
619-
line := scanner.Text()
620-
// Nuclei JSONL format
621-
var res struct {
622-
TemplateID string `json:"template-id"`
623-
MatchedAt string `json:"matched-at"`
624-
Host string `json:"host"`
625-
Info struct {
626-
Name string `json:"name"`
627-
Severity string `json:"severity"`
628-
} `json:"info"`
629-
}
630-
if err := json.Unmarshal([]byte(line), &res); err == nil && res.TemplateID != "" {
608+
err := nuclei.RunGlobalTemplate(tmpFile.Name(), templatePath, outPath, 50, func(event *output.ResultEvent) {
609+
if event != nil && event.TemplateID != "" {
631610
matches++
632611
msg := fmt.Sprintf("🎯 **Global Nuclei Hit!**\n**Template:** `%s` (%s)\n**Target:** `%s`\n**Severity:** `%s`",
633-
res.TemplateID, res.Info.Name, res.MatchedAt, res.Info.Severity)
612+
event.TemplateID, event.Info.Name, event.MatchedAt, event.Info.SeverityHolder.Severity.String())
634613
utils.SendWebhookLogAsync(msg)
635-
stdLog(scanID, "[VULN] %s [%s] on %s", res.Info.Name, res.Info.Severity, res.MatchedAt)
614+
stdLog(scanID, "[VULN] %s [%s] on %s", event.Info.Name, event.Info.SeverityHolder.Severity.String(), event.MatchedAt)
636615
}
637-
}
616+
})
638617

639-
cmd.Wait()
618+
if err != nil {
619+
return fmt.Errorf("nuclei SDK scan failed: %w", err)
620+
}
640621

641622
stdLog(scanID, "[OK] Global Nuclei scan completed. Matches: %d", matches)
642623
utils.SendWebhookLogAsync(fmt.Sprintf("✅ **Global Nuclei Scan Completed**\nTemplate: `%s`\nTargets: %d\nMatches: %d", templateNameForLog, totalSubs, matches))

internal/scanner/nuclei/nuclei.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,74 @@ func runNucleiCommand(targetFile, templateDir string, threads int, outputFile st
579579
return err
580580
}
581581

582+
// RunGlobalTemplate executes a specific nuclei template against a list of targets using the SDK.
583+
func RunGlobalTemplate(targetFile, templatePath, outPath string, threads int, onResult func(event *nucleiOutput.ResultEvent)) error {
584+
targets, err := readTargetLines(targetFile)
585+
if err != nil {
586+
return fmt.Errorf("failed to read targets: %w", err)
587+
}
588+
if len(targets) == 0 {
589+
return fmt.Errorf("no targets found in %s", targetFile)
590+
}
591+
if err := utils.EnsureDir(filepath.Dir(outPath)); err != nil {
592+
return fmt.Errorf("failed to ensure output dir: %w", err)
593+
}
594+
fh, err := os.Create(outPath)
595+
if err != nil {
596+
return fmt.Errorf("failed to create output file: %w", err)
597+
}
598+
defer fh.Close()
599+
600+
jsonWriter, err := nucleiOutput.NewWriter(
601+
nucleiOutput.WithWriter(fh),
602+
nucleiOutput.WithJson(true, false),
603+
)
604+
if err != nil {
605+
return fmt.Errorf("failed to initialize JSON writer: %w", err)
606+
}
607+
defer jsonWriter.Close()
608+
609+
engine, err := nucleiSDK.NewNucleiEngineCtx(
610+
context.Background(),
611+
nucleiSDK.DisableUpdateCheck(),
612+
nucleiSDK.WithVerbosity(nucleiSDK.VerbosityOptions{Silent: true}),
613+
nucleiSDK.WithTemplatesOrWorkflows(nucleiSDK.TemplateSources{
614+
Templates: []string{templatePath},
615+
}),
616+
nucleiSDK.WithConcurrency(nucleiSDK.Concurrency{
617+
TemplateConcurrency: max(1, min(threads, 25)),
618+
HostConcurrency: max(1, threads),
619+
HeadlessHostConcurrency: max(1, min(threads, 10)),
620+
HeadlessTemplateConcurrency: max(1, min(threads, 10)),
621+
JavascriptTemplateConcurrency: max(1, min(threads, 10)),
622+
TemplatePayloadConcurrency: 25,
623+
ProbeConcurrency: max(1, min(threads, 50)),
624+
}),
625+
)
626+
if err != nil {
627+
return fmt.Errorf("failed to initialize SDK: %w", err)
628+
}
629+
defer engine.Close()
630+
631+
engine.LoadTargets(targets, false)
632+
writeErr := error(nil)
633+
err = engine.ExecuteWithCallback(func(event *nucleiOutput.ResultEvent) {
634+
if event == nil || writeErr != nil {
635+
return
636+
}
637+
if wErr := jsonWriter.Write(event); wErr != nil {
638+
writeErr = wErr
639+
}
640+
if onResult != nil {
641+
onResult(event)
642+
}
643+
})
644+
if writeErr != nil {
645+
return writeErr
646+
}
647+
return err
648+
}
649+
582650
func extractDomainFromURL(url string) string {
583651
// Simple extraction: remove http:// or https://, then take first part before /
584652
url = strings.TrimPrefix(url, "http://")

trufflehog_src

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)