mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2025-12-17 17:56:56 +00:00
* feat: add `-enable-global-matchers` flag Signed-off-by: Dwi Siswanto <git@dw1.io> * refactor(templates): use embedded `types.Options` in `Template` Signed-off-by: Dwi Siswanto <git@dw1.io> * feat(lib): add `EnableGlobalMatchersTemplates` SDK opt Signed-off-by: Dwi Siswanto <git@dw1.io> --------- Signed-off-by: Dwi Siswanto <git@dw1.io>
731 lines
41 KiB
Go
731 lines
41 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io/fs"
|
|
"os"
|
|
"os/signal"
|
|
"path/filepath"
|
|
"runtime"
|
|
"runtime/pprof"
|
|
"runtime/trace"
|
|
"strings"
|
|
"time"
|
|
|
|
_pdcp "github.com/projectdiscovery/nuclei/v3/internal/pdcp"
|
|
"github.com/projectdiscovery/utils/auth/pdcp"
|
|
"github.com/projectdiscovery/utils/env"
|
|
_ "github.com/projectdiscovery/utils/pprof"
|
|
stringsutil "github.com/projectdiscovery/utils/strings"
|
|
|
|
"github.com/projectdiscovery/goflags"
|
|
"github.com/projectdiscovery/gologger"
|
|
"github.com/projectdiscovery/gologger/levels"
|
|
"github.com/projectdiscovery/interactsh/pkg/client"
|
|
"github.com/projectdiscovery/nuclei/v3/internal/runner"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/input/provider"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/installer"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/severity"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/operators/common/dsl"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/uncover"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/http"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/templates/extensions"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/templates/signer"
|
|
templateTypes "github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/types/scanstrategy"
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/utils/monitor"
|
|
errorutil "github.com/projectdiscovery/utils/errors"
|
|
fileutil "github.com/projectdiscovery/utils/file"
|
|
unitutils "github.com/projectdiscovery/utils/unit"
|
|
updateutils "github.com/projectdiscovery/utils/update"
|
|
)
|
|
|
|
var (
|
|
cfgFile string
|
|
templateProfile string
|
|
memProfile string // optional profile file path
|
|
options = &types.Options{}
|
|
)
|
|
|
|
func main() {
|
|
// enables CLI specific configs mostly interactive behavior
|
|
config.CurrentAppMode = config.AppModeCLI
|
|
|
|
if err := runner.ConfigureOptions(); err != nil {
|
|
gologger.Fatal().Msgf("Could not initialize options: %s\n", err)
|
|
}
|
|
_ = readConfig()
|
|
|
|
if options.ListDslSignatures {
|
|
gologger.Info().Msgf("The available custom DSL functions are:")
|
|
fmt.Println(dsl.GetPrintableDslFunctionSignatures(options.NoColor))
|
|
return
|
|
}
|
|
|
|
// sign the templates if requested - only glob syntax is supported
|
|
if options.SignTemplates {
|
|
// use parsed options when initializing signer instead of default options
|
|
templates.UseOptionsForSigner(options)
|
|
tsigner, err := signer.NewTemplateSigner(nil, nil) // will read from env , config or generate new keys
|
|
if err != nil {
|
|
gologger.Fatal().Msgf("couldn't initialize signer crypto engine: %s\n", err)
|
|
}
|
|
|
|
successCounter := 0
|
|
errorCounter := 0
|
|
for _, item := range options.Templates {
|
|
err := filepath.WalkDir(item, func(iterItem string, d fs.DirEntry, err error) error {
|
|
if err != nil || d.IsDir() || !strings.HasSuffix(iterItem, extensions.YAML) {
|
|
// skip non yaml files
|
|
return nil
|
|
}
|
|
|
|
if err := templates.SignTemplate(tsigner, iterItem); err != nil {
|
|
if err != templates.ErrNotATemplate {
|
|
// skip warnings and errors as given items are not templates
|
|
errorCounter++
|
|
gologger.Error().Msgf("could not sign '%s': %s\n", iterItem, err)
|
|
}
|
|
} else {
|
|
successCounter++
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
gologger.Error().Msgf("%s\n", err)
|
|
}
|
|
}
|
|
gologger.Info().Msgf("All templates signatures were elaborated success=%d failed=%d\n", successCounter, errorCounter)
|
|
return
|
|
}
|
|
|
|
// Profiling & tracing related code
|
|
if memProfile != "" {
|
|
memProfile = strings.TrimSuffix(memProfile, filepath.Ext(memProfile)) + ".prof"
|
|
memProfileFile, err := os.Create(memProfile)
|
|
if err != nil {
|
|
gologger.Fatal().Msgf("profile: could not create memory profile %q file: %v", memProfile, err)
|
|
}
|
|
|
|
traceFilepath := strings.TrimSuffix(memProfile, filepath.Ext(memProfile)) + ".trace"
|
|
traceFile, err := os.Create(traceFilepath)
|
|
if err != nil {
|
|
gologger.Fatal().Msgf("profile: could not create trace %q file: %v", traceFilepath, err)
|
|
}
|
|
|
|
oldMemProfileRate := runtime.MemProfileRate
|
|
runtime.MemProfileRate = 4096
|
|
|
|
// Start tracing
|
|
if err := trace.Start(traceFile); err != nil {
|
|
gologger.Fatal().Msgf("profile: could not start trace: %v", err)
|
|
}
|
|
|
|
defer func() {
|
|
// Start CPU profiling
|
|
if err := pprof.WriteHeapProfile(memProfileFile); err != nil {
|
|
gologger.Fatal().Msgf("profile: could not start CPU profile: %v", err)
|
|
}
|
|
memProfileFile.Close()
|
|
traceFile.Close()
|
|
trace.Stop()
|
|
runtime.MemProfileRate = oldMemProfileRate
|
|
|
|
gologger.Info().Msgf("Memory profile saved at %q", memProfile)
|
|
gologger.Info().Msgf("Traced at %q", traceFilepath)
|
|
}()
|
|
}
|
|
|
|
runner.ParseOptions(options)
|
|
|
|
if options.ScanUploadFile != "" {
|
|
if err := runner.UploadResultsToCloud(options); err != nil {
|
|
gologger.Fatal().Msgf("could not upload scan results to cloud dashboard: %s\n", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
nucleiRunner, err := runner.New(options)
|
|
if err != nil {
|
|
gologger.Fatal().Msgf("Could not create runner: %s\n", err)
|
|
}
|
|
if nucleiRunner == nil {
|
|
return
|
|
}
|
|
|
|
if options.HangMonitor {
|
|
stackMonitor := monitor.NewStackMonitor()
|
|
cancel := stackMonitor.Start(10 * time.Second)
|
|
defer cancel()
|
|
stackMonitor.RegisterCallback(func(dumpID string) error {
|
|
resumeFileName := fmt.Sprintf("crash-resume-file-%s.dump", dumpID)
|
|
if options.EnableCloudUpload {
|
|
gologger.Info().Msgf("Uploading scan results to cloud...")
|
|
}
|
|
nucleiRunner.Close()
|
|
gologger.Info().Msgf("Creating resume file: %s\n", resumeFileName)
|
|
err := nucleiRunner.SaveResumeConfig(resumeFileName)
|
|
if err != nil {
|
|
return errorutil.NewWithErr(err).Msgf("couldn't create crash resume file")
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
// Setup graceful exits
|
|
resumeFileName := types.DefaultResumeFilePath()
|
|
c := make(chan os.Signal, 1)
|
|
defer close(c)
|
|
signal.Notify(c, os.Interrupt)
|
|
go func() {
|
|
for range c {
|
|
gologger.Info().Msgf("CTRL+C pressed: Exiting\n")
|
|
gologger.Info().Msgf("Attempting graceful shutdown...")
|
|
if options.EnableCloudUpload {
|
|
gologger.Info().Msgf("Uploading scan results to cloud...")
|
|
}
|
|
nucleiRunner.Close()
|
|
if options.ShouldSaveResume() {
|
|
gologger.Info().Msgf("Creating resume file: %s\n", resumeFileName)
|
|
err := nucleiRunner.SaveResumeConfig(resumeFileName)
|
|
if err != nil {
|
|
gologger.Error().Msgf("Couldn't create resume file: %s\n", err)
|
|
}
|
|
}
|
|
os.Exit(1)
|
|
}
|
|
}()
|
|
|
|
if err := nucleiRunner.RunEnumeration(); err != nil {
|
|
if options.Validate {
|
|
gologger.Fatal().Msgf("Could not validate templates: %s\n", err)
|
|
} else {
|
|
gologger.Fatal().Msgf("Could not run nuclei: %s\n", err)
|
|
}
|
|
}
|
|
nucleiRunner.Close()
|
|
// on successful execution remove the resume file in case it exists
|
|
if fileutil.FileExists(resumeFileName) {
|
|
os.Remove(resumeFileName)
|
|
}
|
|
}
|
|
|
|
func readConfig() *goflags.FlagSet {
|
|
|
|
// when true updates nuclei binary to latest version
|
|
var updateNucleiBinary bool
|
|
var pdcpauth string
|
|
var fuzzFlag bool
|
|
|
|
flagSet := goflags.NewFlagSet()
|
|
flagSet.CaseSensitive = true
|
|
flagSet.SetDescription(`Nuclei is a fast, template based vulnerability scanner focusing
|
|
on extensive configurability, massive extensibility and ease of use.`)
|
|
|
|
/* TODO Important: The defined default values, especially for slice/array types are NOT DEFAULT VALUES, but rather implicit values to which the user input is appended.
|
|
This can be very confusing and should be addressed
|
|
*/
|
|
|
|
flagSet.CreateGroup("input", "Target",
|
|
flagSet.StringSliceVarP(&options.Targets, "target", "u", nil, "target URLs/hosts to scan", goflags.CommaSeparatedStringSliceOptions),
|
|
flagSet.StringVarP(&options.TargetsFilePath, "list", "l", "", "path to file containing a list of target URLs/hosts to scan (one per line)"),
|
|
flagSet.StringSliceVarP(&options.ExcludeTargets, "exclude-hosts", "eh", nil, "hosts to exclude to scan from the input list (ip, cidr, hostname)", goflags.FileCommaSeparatedStringSliceOptions),
|
|
flagSet.StringVar(&options.Resume, "resume", "", "resume scan using resume.cfg (clustering will be disabled)"),
|
|
flagSet.BoolVarP(&options.ScanAllIPs, "scan-all-ips", "sa", false, "scan all the IP's associated with dns record"),
|
|
flagSet.StringSliceVarP(&options.IPVersion, "ip-version", "iv", nil, "IP version to scan of hostname (4,6) - (default 4)", goflags.CommaSeparatedStringSliceOptions),
|
|
)
|
|
|
|
flagSet.CreateGroup("target-format", "Target-Format",
|
|
flagSet.StringVarP(&options.InputFileMode, "input-mode", "im", "list", fmt.Sprintf("mode of input file (%v)", provider.SupportedInputFormats())),
|
|
flagSet.BoolVarP(&options.FormatUseRequiredOnly, "required-only", "ro", false, "use only required fields in input format when generating requests"),
|
|
flagSet.BoolVarP(&options.SkipFormatValidation, "skip-format-validation", "sfv", false, "skip format validation (like missing vars) when parsing input file"),
|
|
)
|
|
|
|
flagSet.CreateGroup("templates", "Templates",
|
|
flagSet.BoolVarP(&options.NewTemplates, "new-templates", "nt", false, "run only new templates added in latest nuclei-templates release"),
|
|
flagSet.StringSliceVarP(&options.NewTemplatesWithVersion, "new-templates-version", "ntv", nil, "run new templates added in specific version", goflags.CommaSeparatedStringSliceOptions),
|
|
flagSet.BoolVarP(&options.AutomaticScan, "automatic-scan", "as", false, "automatic web scan using wappalyzer technology detection to tags mapping"),
|
|
flagSet.StringSliceVarP(&options.Templates, "templates", "t", nil, "list of template or template directory to run (comma-separated, file)", goflags.FileCommaSeparatedStringSliceOptions),
|
|
flagSet.StringSliceVarP(&options.TemplateURLs, "template-url", "turl", nil, "template url or list containing template urls to run (comma-separated, file)", goflags.FileCommaSeparatedStringSliceOptions),
|
|
flagSet.StringSliceVarP(&options.Workflows, "workflows", "w", nil, "list of workflow or workflow directory to run (comma-separated, file)", goflags.FileCommaSeparatedStringSliceOptions),
|
|
flagSet.StringSliceVarP(&options.WorkflowURLs, "workflow-url", "wurl", nil, "workflow url or list containing workflow urls to run (comma-separated, file)", goflags.FileCommaSeparatedStringSliceOptions),
|
|
flagSet.BoolVar(&options.Validate, "validate", false, "validate the passed templates to nuclei"),
|
|
flagSet.BoolVarP(&options.NoStrictSyntax, "no-strict-syntax", "nss", false, "disable strict syntax check on templates"),
|
|
flagSet.BoolVarP(&options.TemplateDisplay, "template-display", "td", false, "displays the templates content"),
|
|
flagSet.BoolVar(&options.TemplateList, "tl", false, "list all available templates"),
|
|
flagSet.BoolVar(&options.TagList, "tgl", false, "list all available tags"),
|
|
flagSet.StringSliceVarConfigOnly(&options.RemoteTemplateDomainList, "remote-template-domain", []string{"cloud.projectdiscovery.io"}, "allowed domain list to load remote templates from"),
|
|
flagSet.BoolVar(&options.SignTemplates, "sign", false, "signs the templates with the private key defined in NUCLEI_SIGNATURE_PRIVATE_KEY env variable"),
|
|
flagSet.BoolVar(&options.EnableCodeTemplates, "code", false, "enable loading code protocol-based templates"),
|
|
flagSet.BoolVarP(&options.DisableUnsignedTemplates, "disable-unsigned-templates", "dut", false, "disable running unsigned templates or templates with mismatched signature"),
|
|
flagSet.BoolVarP(&options.EnableSelfContainedTemplates, "enable-self-contained", "esc", false, "enable loading self-contained templates"),
|
|
flagSet.BoolVarP(&options.EnableGlobalMatchersTemplates, "enable-global-matchers", "egm", false, "enable loading global matchers templates"),
|
|
flagSet.BoolVar(&options.EnableFileTemplates, "file", false, "enable loading file templates"),
|
|
)
|
|
|
|
flagSet.CreateGroup("filters", "Filtering",
|
|
flagSet.StringSliceVarP(&options.Authors, "author", "a", nil, "templates to run based on authors (comma-separated, file)", goflags.FileNormalizedStringSliceOptions),
|
|
flagSet.StringSliceVar(&options.Tags, "tags", nil, "templates to run based on tags (comma-separated, file)", goflags.FileNormalizedStringSliceOptions),
|
|
flagSet.StringSliceVarP(&options.ExcludeTags, "exclude-tags", "etags", nil, "templates to exclude based on tags (comma-separated, file)", goflags.FileNormalizedStringSliceOptions),
|
|
flagSet.StringSliceVarP(&options.IncludeTags, "include-tags", "itags", nil, "tags to be executed even if they are excluded either by default or configuration", goflags.FileNormalizedStringSliceOptions), // TODO show default deny list
|
|
flagSet.StringSliceVarP(&options.IncludeIds, "template-id", "id", nil, "templates to run based on template ids (comma-separated, file, allow-wildcard)", goflags.FileNormalizedStringSliceOptions),
|
|
flagSet.StringSliceVarP(&options.ExcludeIds, "exclude-id", "eid", nil, "templates to exclude based on template ids (comma-separated, file)", goflags.FileNormalizedStringSliceOptions),
|
|
flagSet.StringSliceVarP(&options.IncludeTemplates, "include-templates", "it", nil, "path to template file or directory to be executed even if they are excluded either by default or configuration", goflags.FileCommaSeparatedStringSliceOptions),
|
|
flagSet.StringSliceVarP(&options.ExcludedTemplates, "exclude-templates", "et", nil, "path to template file or directory to exclude (comma-separated, file)", goflags.FileCommaSeparatedStringSliceOptions),
|
|
flagSet.StringSliceVarP(&options.ExcludeMatchers, "exclude-matchers", "em", nil, "template matchers to exclude in result", goflags.FileCommaSeparatedStringSliceOptions),
|
|
flagSet.VarP(&options.Severities, "severity", "s", fmt.Sprintf("templates to run based on severity. Possible values: %s", severity.GetSupportedSeverities().String())),
|
|
flagSet.VarP(&options.ExcludeSeverities, "exclude-severity", "es", fmt.Sprintf("templates to exclude based on severity. Possible values: %s", severity.GetSupportedSeverities().String())),
|
|
flagSet.VarP(&options.Protocols, "type", "pt", fmt.Sprintf("templates to run based on protocol type. Possible values: %s", templateTypes.GetSupportedProtocolTypes())),
|
|
flagSet.VarP(&options.ExcludeProtocols, "exclude-type", "ept", fmt.Sprintf("templates to exclude based on protocol type. Possible values: %s", templateTypes.GetSupportedProtocolTypes())),
|
|
flagSet.StringSliceVarP(&options.IncludeConditions, "template-condition", "tc", nil, "templates to run based on expression condition", goflags.StringSliceOptions),
|
|
)
|
|
|
|
flagSet.CreateGroup("output", "Output",
|
|
flagSet.StringVarP(&options.Output, "output", "o", "", "output file to write found issues/vulnerabilities"),
|
|
flagSet.BoolVarP(&options.StoreResponse, "store-resp", "sresp", false, "store all request/response passed through nuclei to output directory"),
|
|
flagSet.StringVarP(&options.StoreResponseDir, "store-resp-dir", "srd", runner.DefaultDumpTrafficOutputFolder, "store all request/response passed through nuclei to custom directory"),
|
|
flagSet.BoolVar(&options.Silent, "silent", false, "display findings only"),
|
|
flagSet.BoolVarP(&options.NoColor, "no-color", "nc", false, "disable output content coloring (ANSI escape codes)"),
|
|
flagSet.BoolVarP(&options.JSONL, "jsonl", "j", false, "write output in JSONL(ines) format"),
|
|
flagSet.BoolVarP(&options.JSONRequests, "include-rr", "irr", true, "include request/response pairs in the JSON, JSONL, and Markdown outputs (for findings only) [DEPRECATED use `-omit-raw`]"),
|
|
flagSet.BoolVarP(&options.OmitRawRequests, "omit-raw", "or", false, "omit request/response pairs in the JSON, JSONL, and Markdown outputs (for findings only)"),
|
|
flagSet.BoolVarP(&options.OmitTemplate, "omit-template", "ot", false, "omit encoded template in the JSON, JSONL output"),
|
|
flagSet.BoolVarP(&options.NoMeta, "no-meta", "nm", false, "disable printing result metadata in cli output"),
|
|
flagSet.BoolVarP(&options.Timestamp, "timestamp", "ts", false, "enables printing timestamp in cli output"),
|
|
flagSet.StringVarP(&options.ReportingDB, "report-db", "rdb", "", "nuclei reporting database (always use this to persist report data)"),
|
|
flagSet.BoolVarP(&options.MatcherStatus, "matcher-status", "ms", false, "display match failure status"),
|
|
flagSet.StringVarP(&options.MarkdownExportDirectory, "markdown-export", "me", "", "directory to export results in markdown format"),
|
|
flagSet.StringVarP(&options.SarifExport, "sarif-export", "se", "", "file to export results in SARIF format"),
|
|
flagSet.StringVarP(&options.JSONExport, "json-export", "je", "", "file to export results in JSON format"),
|
|
flagSet.StringVarP(&options.JSONLExport, "jsonl-export", "jle", "", "file to export results in JSONL(ine) format"),
|
|
flagSet.StringSliceVarP(&options.Redact, "redact", "rd", nil, "redact given list of keys from query parameter, request header and body", goflags.CommaSeparatedStringSliceOptions),
|
|
)
|
|
|
|
flagSet.CreateGroup("configs", "Configurations",
|
|
flagSet.StringVar(&cfgFile, "config", "", "path to the nuclei configuration file"),
|
|
flagSet.StringVarP(&templateProfile, "profile", "tp", "", "template profile config file to run"),
|
|
flagSet.BoolVarP(&options.ListTemplateProfiles, "profile-list", "tpl", false, "list community template profiles"),
|
|
flagSet.BoolVarP(&options.FollowRedirects, "follow-redirects", "fr", false, "enable following redirects for http templates"),
|
|
flagSet.BoolVarP(&options.FollowHostRedirects, "follow-host-redirects", "fhr", false, "follow redirects on the same host"),
|
|
flagSet.IntVarP(&options.MaxRedirects, "max-redirects", "mr", 10, "max number of redirects to follow for http templates"),
|
|
flagSet.BoolVarP(&options.DisableRedirects, "disable-redirects", "dr", false, "disable redirects for http templates"),
|
|
flagSet.StringVarP(&options.ReportingConfig, "report-config", "rc", "", "nuclei reporting module configuration file"), // TODO merge into the config file or rename to issue-tracking
|
|
flagSet.StringSliceVarP(&options.CustomHeaders, "header", "H", nil, "custom header/cookie to include in all http request in header:value format (cli, file)", goflags.FileStringSliceOptions),
|
|
flagSet.RuntimeMapVarP(&options.Vars, "var", "V", nil, "custom vars in key=value format"),
|
|
flagSet.StringVarP(&options.ResolversFile, "resolvers", "r", "", "file containing resolver list for nuclei"),
|
|
flagSet.BoolVarP(&options.SystemResolvers, "system-resolvers", "sr", false, "use system DNS resolving as error fallback"),
|
|
flagSet.BoolVarP(&options.DisableClustering, "disable-clustering", "dc", false, "disable clustering of requests"),
|
|
flagSet.BoolVar(&options.OfflineHTTP, "passive", false, "enable passive HTTP response processing mode"),
|
|
flagSet.BoolVarP(&options.ForceAttemptHTTP2, "force-http2", "fh2", false, "force http2 connection on requests"),
|
|
flagSet.BoolVarP(&options.EnvironmentVariables, "env-vars", "ev", false, "enable environment variables to be used in template"),
|
|
flagSet.StringVarP(&options.ClientCertFile, "client-cert", "cc", "", "client certificate file (PEM-encoded) used for authenticating against scanned hosts"),
|
|
flagSet.StringVarP(&options.ClientKeyFile, "client-key", "ck", "", "client key file (PEM-encoded) used for authenticating against scanned hosts"),
|
|
flagSet.StringVarP(&options.ClientCAFile, "client-ca", "ca", "", "client certificate authority file (PEM-encoded) used for authenticating against scanned hosts"),
|
|
flagSet.BoolVarP(&options.ShowMatchLine, "show-match-line", "sml", false, "show match lines for file templates, works with extractors only"),
|
|
flagSet.BoolVar(&options.ZTLS, "ztls", false, "use ztls library with autofallback to standard one for tls13 [Deprecated] autofallback to ztls is enabled by default"), //nolint:all
|
|
flagSet.StringVar(&options.SNI, "sni", "", "tls sni hostname to use (default: input domain name)"),
|
|
flagSet.DurationVarP(&options.DialerKeepAlive, "dialer-keep-alive", "dka", 0, "keep-alive duration for network requests."),
|
|
flagSet.BoolVarP(&options.AllowLocalFileAccess, "allow-local-file-access", "lfa", false, "allows file (payload) access anywhere on the system"),
|
|
flagSet.BoolVarP(&options.RestrictLocalNetworkAccess, "restrict-local-network-access", "lna", false, "blocks connections to the local / private network"),
|
|
flagSet.StringVarP(&options.Interface, "interface", "i", "", "network interface to use for network scan"),
|
|
flagSet.StringVarP(&options.AttackType, "attack-type", "at", "", "type of payload combinations to perform (batteringram,pitchfork,clusterbomb)"),
|
|
flagSet.StringVarP(&options.SourceIP, "source-ip", "sip", "", "source ip address to use for network scan"),
|
|
flagSet.IntVarP(&options.ResponseReadSize, "response-size-read", "rsr", 0, "max response size to read in bytes"),
|
|
flagSet.IntVarP(&options.ResponseSaveSize, "response-size-save", "rss", unitutils.Mega, "max response size to read in bytes"),
|
|
flagSet.CallbackVar(resetCallback, "reset", "reset removes all nuclei configuration and data files (including nuclei-templates)"),
|
|
flagSet.BoolVarP(&options.TlsImpersonate, "tls-impersonate", "tlsi", false, "enable experimental client hello (ja3) tls randomization"),
|
|
flagSet.StringVarP(&options.HttpApiEndpoint, "http-api-endpoint", "hae", "", "experimental http api endpoint"),
|
|
)
|
|
|
|
flagSet.CreateGroup("interactsh", "interactsh",
|
|
flagSet.StringVarP(&options.InteractshURL, "interactsh-server", "iserver", "", fmt.Sprintf("interactsh server url for self-hosted instance (default: %s)", client.DefaultOptions.ServerURL)),
|
|
flagSet.StringVarP(&options.InteractshToken, "interactsh-token", "itoken", "", "authentication token for self-hosted interactsh server"),
|
|
flagSet.IntVar(&options.InteractionsCacheSize, "interactions-cache-size", 5000, "number of requests to keep in the interactions cache"),
|
|
flagSet.IntVar(&options.InteractionsEviction, "interactions-eviction", 60, "number of seconds to wait before evicting requests from cache"),
|
|
flagSet.IntVar(&options.InteractionsPollDuration, "interactions-poll-duration", 5, "number of seconds to wait before each interaction poll request"),
|
|
flagSet.IntVar(&options.InteractionsCoolDownPeriod, "interactions-cooldown-period", 5, "extra time for interaction polling before exiting"),
|
|
flagSet.BoolVarP(&options.NoInteractsh, "no-interactsh", "ni", false, "disable interactsh server for OAST testing, exclude OAST based templates"),
|
|
)
|
|
|
|
flagSet.CreateGroup("fuzzing", "Fuzzing",
|
|
flagSet.StringVarP(&options.FuzzingType, "fuzzing-type", "ft", "", "overrides fuzzing type set in template (replace, prefix, postfix, infix)"),
|
|
flagSet.StringVarP(&options.FuzzingMode, "fuzzing-mode", "fm", "", "overrides fuzzing mode set in template (multiple, single)"),
|
|
flagSet.BoolVar(&fuzzFlag, "fuzz", false, "enable loading fuzzing templates (Deprecated: use -dast instead)"),
|
|
flagSet.BoolVar(&options.DAST, "dast", false, "enable / run dast (fuzz) nuclei templates"),
|
|
flagSet.BoolVarP(&options.DisplayFuzzPoints, "display-fuzz-points", "dfp", false, "display fuzz points in the output for debugging"),
|
|
flagSet.IntVar(&options.FuzzParamFrequency, "fuzz-param-frequency", 10, "frequency of uninteresting parameters for fuzzing before skipping"),
|
|
flagSet.StringVarP(&options.FuzzAggressionLevel, "fuzz-aggression", "fa", "low", "fuzzing aggression level controls payload count for fuzz (low, medium, high)"),
|
|
)
|
|
|
|
flagSet.CreateGroup("uncover", "Uncover",
|
|
flagSet.BoolVarP(&options.Uncover, "uncover", "uc", false, "enable uncover engine"),
|
|
flagSet.StringSliceVarP(&options.UncoverQuery, "uncover-query", "uq", nil, "uncover search query", goflags.FileStringSliceOptions),
|
|
flagSet.StringSliceVarP(&options.UncoverEngine, "uncover-engine", "ue", nil, fmt.Sprintf("uncover search engine (%s) (default shodan)", uncover.GetUncoverSupportedAgents()), goflags.FileStringSliceOptions),
|
|
flagSet.StringVarP(&options.UncoverField, "uncover-field", "uf", "ip:port", "uncover fields to return (ip,port,host)"),
|
|
flagSet.IntVarP(&options.UncoverLimit, "uncover-limit", "ul", 100, "uncover results to return"),
|
|
flagSet.IntVarP(&options.UncoverRateLimit, "uncover-ratelimit", "ur", 60, "override ratelimit of engines with unknown ratelimit (default 60 req/min)"),
|
|
)
|
|
|
|
flagSet.CreateGroup("rate-limit", "Rate-Limit",
|
|
flagSet.IntVarP(&options.RateLimit, "rate-limit", "rl", 150, "maximum number of requests to send per second"),
|
|
flagSet.DurationVarP(&options.RateLimitDuration, "rate-limit-duration", "rld", time.Second, "maximum number of requests to send per second"),
|
|
flagSet.IntVarP(&options.RateLimitMinute, "rate-limit-minute", "rlm", 0, "maximum number of requests to send per minute (DEPRECATED)"),
|
|
flagSet.IntVarP(&options.BulkSize, "bulk-size", "bs", 25, "maximum number of hosts to be analyzed in parallel per template"),
|
|
flagSet.IntVarP(&options.TemplateThreads, "concurrency", "c", 25, "maximum number of templates to be executed in parallel"),
|
|
flagSet.IntVarP(&options.HeadlessBulkSize, "headless-bulk-size", "hbs", 10, "maximum number of headless hosts to be analyzed in parallel per template"),
|
|
flagSet.IntVarP(&options.HeadlessTemplateThreads, "headless-concurrency", "headc", 10, "maximum number of headless templates to be executed in parallel"),
|
|
flagSet.IntVarP(&options.JsConcurrency, "js-concurrency", "jsc", 120, "maximum number of javascript runtimes to be executed in parallel"),
|
|
flagSet.IntVarP(&options.PayloadConcurrency, "payload-concurrency", "pc", 25, "max payload concurrency for each template"),
|
|
flagSet.IntVarP(&options.ProbeConcurrency, "probe-concurrency", "prc", 50, "http probe concurrency with httpx"),
|
|
)
|
|
flagSet.CreateGroup("optimization", "Optimizations",
|
|
flagSet.IntVar(&options.Timeout, "timeout", 10, "time to wait in seconds before timeout"),
|
|
flagSet.IntVar(&options.Retries, "retries", 1, "number of times to retry a failed request"),
|
|
flagSet.BoolVarP(&options.LeaveDefaultPorts, "leave-default-ports", "ldp", false, "leave default HTTP/HTTPS ports (eg. host:80,host:443)"),
|
|
flagSet.IntVarP(&options.MaxHostError, "max-host-error", "mhe", 30, "max errors for a host before skipping from scan"),
|
|
flagSet.StringSliceVarP(&options.TrackError, "track-error", "te", nil, "adds given error to max-host-error watchlist (standard, file)", goflags.FileStringSliceOptions),
|
|
flagSet.BoolVarP(&options.NoHostErrors, "no-mhe", "nmhe", false, "disable skipping host from scan based on errors"),
|
|
flagSet.BoolVar(&options.Project, "project", false, "use a project folder to avoid sending same request multiple times"),
|
|
flagSet.StringVar(&options.ProjectPath, "project-path", os.TempDir(), "set a specific project path"),
|
|
flagSet.BoolVarP(&options.StopAtFirstMatch, "stop-at-first-match", "spm", false, "stop processing HTTP requests after the first match (may break template/workflow logic)"),
|
|
flagSet.BoolVar(&options.Stream, "stream", false, "stream mode - start elaborating without sorting the input"),
|
|
flagSet.EnumVarP(&options.ScanStrategy, "scan-strategy", "ss", goflags.EnumVariable(0), "strategy to use while scanning(auto/host-spray/template-spray)", goflags.AllowdTypes{
|
|
scanstrategy.Auto.String(): goflags.EnumVariable(0),
|
|
scanstrategy.HostSpray.String(): goflags.EnumVariable(1),
|
|
scanstrategy.TemplateSpray.String(): goflags.EnumVariable(2),
|
|
}),
|
|
flagSet.DurationVarP(&options.InputReadTimeout, "input-read-timeout", "irt", time.Duration(3*time.Minute), "timeout on input read"),
|
|
flagSet.BoolVarP(&options.DisableHTTPProbe, "no-httpx", "nh", false, "disable httpx probing for non-url input"),
|
|
flagSet.BoolVar(&options.DisableStdin, "no-stdin", false, "disable stdin processing"),
|
|
)
|
|
|
|
flagSet.CreateGroup("headless", "Headless",
|
|
flagSet.BoolVar(&options.Headless, "headless", false, "enable templates that require headless browser support (root user on Linux will disable sandbox)"),
|
|
flagSet.IntVar(&options.PageTimeout, "page-timeout", 20, "seconds to wait for each page in headless mode"),
|
|
flagSet.BoolVarP(&options.ShowBrowser, "show-browser", "sb", false, "show the browser on the screen when running templates with headless mode"),
|
|
flagSet.StringSliceVarP(&options.HeadlessOptionalArguments, "headless-options", "ho", nil, "start headless chrome with additional options", goflags.FileCommaSeparatedStringSliceOptions),
|
|
flagSet.BoolVarP(&options.UseInstalledChrome, "system-chrome", "sc", false, "use local installed Chrome browser instead of nuclei installed"),
|
|
flagSet.BoolVarP(&options.ShowActions, "list-headless-action", "lha", false, "list available headless actions"),
|
|
)
|
|
|
|
flagSet.CreateGroup("debug", "Debug",
|
|
flagSet.BoolVar(&options.Debug, "debug", false, "show all requests and responses"),
|
|
flagSet.BoolVarP(&options.DebugRequests, "debug-req", "dreq", false, "show all sent requests"),
|
|
flagSet.BoolVarP(&options.DebugResponse, "debug-resp", "dresp", false, "show all received responses"),
|
|
flagSet.StringSliceVarP(&options.Proxy, "proxy", "p", nil, "list of http/socks5 proxy to use (comma separated or file input)", goflags.FileCommaSeparatedStringSliceOptions),
|
|
flagSet.BoolVarP(&options.ProxyInternal, "proxy-internal", "pi", false, "proxy all internal requests"),
|
|
flagSet.BoolVarP(&options.ListDslSignatures, "list-dsl-function", "ldf", false, "list all supported DSL function signatures"),
|
|
flagSet.StringVarP(&options.TraceLogFile, "trace-log", "tlog", "", "file to write sent requests trace log"),
|
|
flagSet.StringVarP(&options.ErrorLogFile, "error-log", "elog", "", "file to write sent requests error log"),
|
|
flagSet.CallbackVar(printVersion, "version", "show nuclei version"),
|
|
flagSet.BoolVarP(&options.HangMonitor, "hang-monitor", "hm", false, "enable nuclei hang monitoring"),
|
|
flagSet.BoolVarP(&options.Verbose, "verbose", "v", false, "show verbose output"),
|
|
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.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"),
|
|
flagSet.BoolVarP(&options.HealthCheck, "health-check", "hc", false, "run diagnostic check up"),
|
|
)
|
|
|
|
flagSet.CreateGroup("update", "Update",
|
|
flagSet.BoolVarP(&updateNucleiBinary, "update", "up", false, "update nuclei engine to the latest released version"),
|
|
flagSet.BoolVarP(&options.UpdateTemplates, "update-templates", "ut", false, "update nuclei-templates to latest released version"),
|
|
flagSet.StringVarP(&options.NewTemplatesDirectory, "update-template-dir", "ud", "", "custom directory to install / update nuclei-templates"),
|
|
flagSet.CallbackVarP(disableUpdatesCallback, "disable-update-check", "duc", "disable automatic nuclei/templates update check"),
|
|
)
|
|
|
|
flagSet.CreateGroup("stats", "Statistics",
|
|
flagSet.BoolVar(&options.EnableProgressBar, "stats", false, "display statistics about the running scan"),
|
|
flagSet.BoolVarP(&options.StatsJSON, "stats-json", "sj", false, "display statistics in JSONL(ines) format"),
|
|
flagSet.IntVarP(&options.StatsInterval, "stats-interval", "si", 5, "number of seconds to wait between showing a statistics update"),
|
|
flagSet.IntVarP(&options.MetricsPort, "metrics-port", "mp", 9092, "port to expose nuclei metrics on"),
|
|
)
|
|
|
|
flagSet.CreateGroup("cloud", "Cloud",
|
|
flagSet.DynamicVar(&pdcpauth, "auth", "true", "configure projectdiscovery cloud (pdcp) api key"),
|
|
flagSet.StringVarP(&options.TeamID, "team-id", "tid", _pdcp.TeamIDEnv, "upload scan results to given team id (optional)"),
|
|
flagSet.BoolVarP(&options.EnableCloudUpload, "cloud-upload", "cup", false, "upload scan results to pdcp dashboard [DEPRECATED use -dashboard]"),
|
|
flagSet.StringVarP(&options.ScanID, "scan-id", "sid", "", "upload scan results to existing scan id (optional)"),
|
|
flagSet.StringVarP(&options.ScanName, "scan-name", "sname", "", "scan name to set (optional)"),
|
|
flagSet.BoolVarP(&options.EnableCloudUpload, "dashboard", "pd", false, "upload / view nuclei results in projectdiscovery cloud (pdcp) UI dashboard"),
|
|
flagSet.StringVarP(&options.ScanUploadFile, "dashboard-upload", "pdu", "", "upload / view nuclei results file (jsonl) in projectdiscovery cloud (pdcp) UI dashboard"),
|
|
)
|
|
|
|
flagSet.CreateGroup("Authentication", "Authentication",
|
|
flagSet.StringSliceVarP(&options.SecretsFile, "secret-file", "sf", nil, "path to config file containing secrets for nuclei authenticated scan", goflags.CommaSeparatedStringSliceOptions),
|
|
flagSet.BoolVarP(&options.PreFetchSecrets, "prefetch-secrets", "ps", false, "prefetch secrets from the secrets file"),
|
|
)
|
|
|
|
flagSet.SetCustomHelpText(`EXAMPLES:
|
|
Run nuclei on single host:
|
|
$ nuclei -target example.com
|
|
|
|
Run nuclei with specific template directories:
|
|
$ nuclei -target example.com -t http/cves/ -t ssl
|
|
|
|
Run nuclei against a list of hosts:
|
|
$ nuclei -list hosts.txt
|
|
|
|
Run nuclei with a JSON output:
|
|
$ nuclei -target example.com -json-export output.json
|
|
|
|
Run nuclei with sorted Markdown outputs (with environment variables):
|
|
$ MARKDOWN_EXPORT_SORT_MODE=template nuclei -target example.com -markdown-export nuclei_report/
|
|
|
|
Additional documentation is available at: https://docs.nuclei.sh/getting-started/running
|
|
`)
|
|
|
|
// nuclei has multiple migrations
|
|
// ex: resume.cfg moved to platform standard cache dir from config dir
|
|
// ex: config.yaml moved to platform standard config dir from linux specific config dir
|
|
// and hence it will be attempted in config package during init
|
|
goflags.DisableAutoConfigMigration = true
|
|
_ = flagSet.Parse()
|
|
|
|
// when fuzz flag is enabled, set the dast flag to true
|
|
if fuzzFlag {
|
|
// backwards compatibility for fuzz flag
|
|
options.DAST = true
|
|
}
|
|
|
|
// All cloud-based templates depend on both code and self-contained templates.
|
|
if options.EnableCodeTemplates {
|
|
options.EnableSelfContainedTemplates = true
|
|
}
|
|
|
|
// api key hierarchy: cli flag > env var > .pdcp/credential file
|
|
if pdcpauth == "true" {
|
|
runner.AuthWithPDCP()
|
|
} else if len(pdcpauth) == 36 {
|
|
ph := pdcp.PDCPCredHandler{}
|
|
if _, err := ph.GetCreds(); err == pdcp.ErrNoCreds {
|
|
apiServer := env.GetEnvOrDefault("PDCP_API_SERVER", pdcp.DefaultApiServer)
|
|
if validatedCreds, err := ph.ValidateAPIKey(pdcpauth, apiServer, config.BinaryName); err == nil {
|
|
_ = ph.SaveCreds(validatedCreds)
|
|
}
|
|
}
|
|
}
|
|
|
|
gologger.DefaultLogger.SetTimestamp(options.Timestamp, levels.LevelDebug)
|
|
|
|
if options.VerboseVerbose {
|
|
// hide release notes if silent mode is enabled
|
|
installer.HideReleaseNotes = false
|
|
}
|
|
|
|
if options.Timeout > 30 {
|
|
// default github binary/template download timeout is 30 sec
|
|
updateutils.DownloadUpdateTimeout = time.Duration(options.Timeout) * time.Second
|
|
}
|
|
if updateNucleiBinary {
|
|
runner.NucleiToolUpdateCallback()
|
|
}
|
|
|
|
if options.LeaveDefaultPorts {
|
|
http.LeaveDefaultPorts = true
|
|
}
|
|
if customConfigDir := os.Getenv(config.NucleiConfigDirEnv); customConfigDir != "" {
|
|
config.DefaultConfig.SetConfigDir(customConfigDir)
|
|
readFlagsConfig(flagSet)
|
|
}
|
|
if cfgFile != "" {
|
|
if !fileutil.FileExists(cfgFile) {
|
|
gologger.Fatal().Msgf("given config file '%s' does not exist", cfgFile)
|
|
}
|
|
// merge config file with flags
|
|
if err := flagSet.MergeConfigFile(cfgFile); err != nil {
|
|
gologger.Fatal().Msgf("Could not read config: %s\n", err)
|
|
}
|
|
}
|
|
if options.NewTemplatesDirectory != "" {
|
|
config.DefaultConfig.SetTemplatesDir(options.NewTemplatesDirectory)
|
|
}
|
|
|
|
defaultProfilesPath := filepath.Join(config.DefaultConfig.GetTemplateDir(), "profiles")
|
|
if templateProfile != "" {
|
|
if filepath.Ext(templateProfile) == "" {
|
|
if tp := findProfilePathById(templateProfile, defaultProfilesPath); tp != "" {
|
|
templateProfile = tp
|
|
} else {
|
|
gologger.Fatal().Msgf("'%s' is not a profile-id or profile path", templateProfile)
|
|
}
|
|
}
|
|
if !filepath.IsAbs(templateProfile) {
|
|
if filepath.Dir(templateProfile) == "profiles" {
|
|
defaultProfilesPath = filepath.Join(config.DefaultConfig.GetTemplateDir())
|
|
}
|
|
currentDir, err := os.Getwd()
|
|
if err == nil && fileutil.FileExists(filepath.Join(currentDir, templateProfile)) {
|
|
templateProfile = filepath.Join(currentDir, templateProfile)
|
|
} else {
|
|
templateProfile = filepath.Join(defaultProfilesPath, templateProfile)
|
|
}
|
|
}
|
|
if !fileutil.FileExists(templateProfile) {
|
|
gologger.Fatal().Msgf("given template profile file '%s' does not exist", templateProfile)
|
|
}
|
|
if err := flagSet.MergeConfigFile(templateProfile); err != nil {
|
|
gologger.Fatal().Msgf("Could not read template profile: %s\n", err)
|
|
}
|
|
}
|
|
|
|
if len(options.SecretsFile) > 0 {
|
|
for _, secretFile := range options.SecretsFile {
|
|
if !fileutil.FileExists(secretFile) {
|
|
gologger.Fatal().Msgf("given secrets file '%s' does not exist", options.SecretsFile)
|
|
}
|
|
}
|
|
}
|
|
|
|
cleanupOldResumeFiles()
|
|
return flagSet
|
|
}
|
|
|
|
// cleanupOldResumeFiles cleans up resume files older than 10 days.
|
|
func cleanupOldResumeFiles() {
|
|
root := config.DefaultConfig.GetCacheDir()
|
|
filter := fileutil.FileFilters{
|
|
OlderThan: 24 * time.Hour * 10, // cleanup on the 10th day
|
|
Prefix: "resume-",
|
|
}
|
|
_ = fileutil.DeleteFilesOlderThan(root, filter)
|
|
}
|
|
|
|
// readFlagsConfig reads the config file from the default config dir and copies it to the current config dir.
|
|
func readFlagsConfig(flagset *goflags.FlagSet) {
|
|
// check if config.yaml file exists
|
|
defaultCfgFile, err := flagset.GetConfigFilePath()
|
|
if err != nil {
|
|
// something went wrong either dir is not readable or something else went wrong upstream in `goflags`
|
|
// warn and exit in this case
|
|
gologger.Warning().Msgf("Could not read config file: %s\n", err)
|
|
return
|
|
}
|
|
cfgFile := config.DefaultConfig.GetFlagsConfigFilePath()
|
|
if !fileutil.FileExists(cfgFile) {
|
|
if !fileutil.FileExists(defaultCfgFile) {
|
|
// if default config does not exist, warn and exit
|
|
gologger.Warning().Msgf("missing default config file : %s", defaultCfgFile)
|
|
return
|
|
}
|
|
// if does not exist copy it from the default config
|
|
if err = fileutil.CopyFile(defaultCfgFile, cfgFile); err != nil {
|
|
gologger.Warning().Msgf("Could not copy config file: %s\n", err)
|
|
}
|
|
return
|
|
}
|
|
// if config file exists, merge it with the default config
|
|
if err = flagset.MergeConfigFile(cfgFile); err != nil {
|
|
gologger.Warning().Msgf("failed to merge configfile with flags got: %s\n", err)
|
|
}
|
|
}
|
|
|
|
// disableUpdatesCallback disables the update check.
|
|
func disableUpdatesCallback() {
|
|
config.DefaultConfig.DisableUpdateCheck()
|
|
}
|
|
|
|
// printVersion prints the nuclei version and exits.
|
|
func printVersion() {
|
|
gologger.Info().Msgf("Nuclei Engine Version: %s", config.Version)
|
|
gologger.Info().Msgf("Nuclei Config Directory: %s", config.DefaultConfig.GetConfigDir())
|
|
gologger.Info().Msgf("Nuclei Cache Directory: %s", config.DefaultConfig.GetCacheDir()) // cache dir contains resume files
|
|
gologger.Info().Msgf("PDCP Directory: %s", pdcp.PDCPDir)
|
|
os.Exit(0)
|
|
}
|
|
|
|
// printTemplateVersion prints the nuclei template version and exits.
|
|
func printTemplateVersion() {
|
|
cfg := config.DefaultConfig
|
|
gologger.Info().Msgf("Public nuclei-templates version: %s (%s)\n", cfg.TemplateVersion, cfg.TemplatesDirectory)
|
|
|
|
if fileutil.FolderExists(cfg.CustomS3TemplatesDirectory) {
|
|
gologger.Info().Msgf("Custom S3 templates location: %s\n", cfg.CustomS3TemplatesDirectory)
|
|
}
|
|
if fileutil.FolderExists(cfg.CustomGitHubTemplatesDirectory) {
|
|
gologger.Info().Msgf("Custom GitHub templates location: %s ", cfg.CustomGitHubTemplatesDirectory)
|
|
}
|
|
if fileutil.FolderExists(cfg.CustomGitLabTemplatesDirectory) {
|
|
gologger.Info().Msgf("Custom GitLab templates location: %s ", cfg.CustomGitLabTemplatesDirectory)
|
|
}
|
|
if fileutil.FolderExists(cfg.CustomAzureTemplatesDirectory) {
|
|
gologger.Info().Msgf("Custom Azure templates location: %s ", cfg.CustomAzureTemplatesDirectory)
|
|
}
|
|
os.Exit(0)
|
|
}
|
|
|
|
func resetCallback() {
|
|
warning := fmt.Sprintf(`
|
|
Using '-reset' will delete all nuclei configurations files and all nuclei-templates
|
|
|
|
Following files will be deleted:
|
|
1. All Config + Resumes files at %v
|
|
2. All nuclei-templates at %v
|
|
|
|
Note: Make sure you have backup of your custom nuclei-templates before proceeding
|
|
|
|
`, config.DefaultConfig.GetConfigDir(), config.DefaultConfig.TemplatesDirectory)
|
|
gologger.Print().Msg(warning)
|
|
reader := bufio.NewReader(os.Stdin)
|
|
for {
|
|
fmt.Print("Are you sure you want to continue? [y/n]: ")
|
|
resp, err := reader.ReadString('\n')
|
|
if err != nil {
|
|
gologger.Fatal().Msgf("could not read response: %s", err)
|
|
}
|
|
resp = strings.TrimSpace(resp)
|
|
if stringsutil.EqualFoldAny(resp, "y", "yes") {
|
|
break
|
|
}
|
|
if stringsutil.EqualFoldAny(resp, "n", "no", "") {
|
|
fmt.Println("Exiting...")
|
|
os.Exit(0)
|
|
}
|
|
}
|
|
err := os.RemoveAll(config.DefaultConfig.GetConfigDir())
|
|
if err != nil {
|
|
gologger.Fatal().Msgf("could not delete config dir: %s", err)
|
|
}
|
|
err = os.RemoveAll(config.DefaultConfig.TemplatesDirectory)
|
|
if err != nil {
|
|
gologger.Fatal().Msgf("could not delete templates dir: %s", err)
|
|
}
|
|
gologger.Info().Msgf("Successfully deleted all nuclei configurations files and nuclei-templates")
|
|
os.Exit(0)
|
|
}
|
|
|
|
func findProfilePathById(profileId, templatesDir string) string {
|
|
var profilePath string
|
|
err := filepath.WalkDir(templatesDir, func(iterItem string, d fs.DirEntry, err error) error {
|
|
ext := filepath.Ext(iterItem)
|
|
isYaml := ext == extensions.YAML || ext == extensions.YML
|
|
if err != nil || d.IsDir() || !isYaml {
|
|
// skip non yaml files
|
|
return nil
|
|
}
|
|
if strings.TrimSuffix(filepath.Base(iterItem), ext) == profileId {
|
|
profilePath = iterItem
|
|
return fmt.Errorf("FOUND")
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil && err.Error() != "FOUND" {
|
|
gologger.Error().Msgf("%s\n", err)
|
|
}
|
|
return profilePath
|
|
}
|
|
|
|
func init() {
|
|
// print stacktrace of errors in debug mode
|
|
if strings.EqualFold(os.Getenv("DEBUG"), "true") {
|
|
errorutil.ShowStackTrace = true
|
|
}
|
|
}
|