nuclei/v2/internal/runner/options.go

167 lines
6.6 KiB
Go
Raw Normal View History

2020-04-04 03:45:39 +05:30
package runner
import (
2020-08-29 15:26:11 +02:00
"errors"
2020-04-04 03:45:39 +05:30
"flag"
2020-08-29 15:26:11 +02:00
"net/url"
2020-04-04 03:45:39 +05:30
"os"
"github.com/projectdiscovery/gologger"
)
// ParseOptions parses the command line flags provided by a user
func ParseOptions() *Options {
options := &Options{}
2020-12-19 00:10:58 +05:30
flag.BoolVar(&options.Vhost, "vhost", false, "Input supplied is a comma-separated vhost list")
flag.BoolVar(&options.Sandbox, "sandbox", false, "Run workflows in isolated sandbox mode")
flag.BoolVar(&options.Metrics, "metrics", false, "Expose nuclei metrics on a port")
flag.IntVar(&options.MetricsPort, "metrics-port", 9092, "Port to expose nuclei metrics on")
2020-12-13 14:17:58 +05:30
flag.IntVar(&options.MaxWorkflowDuration, "workflow-duration", 10, "Max time for workflow run on single URL in minutes")
flag.StringVar(&options.Target, "target", "", "Target is a single target to scan using template")
2020-08-02 15:48:10 +02:00
flag.Var(&options.Templates, "t", "Template input dir/file/files to run on host. Can be used multiple times. Supports globbing.")
flag.Var(&options.ExcludedTemplates, "exclude", "Template input dir/file/files to exclude. Can be used multiple times. Supports globbing.")
2020-08-02 18:33:55 +02:00
flag.StringVar(&options.Severity, "severity", "", "Filter templates based on their severity and only run the matching ones. Comma-separated values can be used to specify multiple severities.")
2020-04-04 17:24:31 +05:30
flag.StringVar(&options.Targets, "l", "", "List of URLs to run templates on")
2020-04-04 03:45:39 +05:30
flag.StringVar(&options.Output, "o", "", "File to write output to (optional)")
2020-04-27 23:49:53 +05:30
flag.StringVar(&options.ProxyURL, "proxy-url", "", "URL of the proxy server")
2020-04-28 04:01:25 +02:00
flag.StringVar(&options.ProxySocksURL, "proxy-socks-url", "", "URL of the proxy socks server")
2020-04-04 17:24:31 +05:30
flag.BoolVar(&options.Silent, "silent", false, "Show only results in output")
flag.BoolVar(&options.Version, "version", false, "Show version of nuclei")
2020-04-04 03:45:39 +05:30
flag.BoolVar(&options.Verbose, "v", false, "Show Verbose output")
2020-11-19 15:15:48 +05:30
flag.BoolVar(&options.NoColor, "no-color", false, "Disable colors in output")
2020-04-04 18:10:26 +05:30
flag.IntVar(&options.Timeout, "timeout", 5, "Time to wait in seconds before timeout")
flag.IntVar(&options.Retries, "retries", 1, "Number of times to retry a failed request")
flag.BoolVar(&options.RandomAgent, "random-agent", false, "Use randomly selected HTTP User-Agent header value")
2020-05-22 00:23:38 +02:00
flag.Var(&options.CustomHeaders, "H", "Custom Header.")
2020-06-22 19:30:01 +05:30
flag.BoolVar(&options.Debug, "debug", false, "Allow debugging of request/responses")
2020-06-25 03:53:37 +05:30
flag.BoolVar(&options.UpdateTemplates, "update-templates", false, "Update Templates updates the installed templates (optional)")
flag.StringVar(&options.TraceLogFile, "trace-log", "", "File to write sent requests trace log")
2020-06-27 20:52:54 +05:30
flag.StringVar(&options.TemplatesDirectory, "update-directory", "", "Directory to use for storing nuclei-templates")
2020-06-27 20:19:43 +05:30
flag.BoolVar(&options.JSON, "json", false, "Write json output to files")
flag.BoolVar(&options.JSONRequests, "include-rr", false, "Write requests/responses for matches in JSON output")
flag.BoolVar(&options.EnableProgressBar, "stats", false, "Display stats of the running scan")
2020-08-29 15:26:11 +02:00
flag.BoolVar(&options.TemplateList, "tl", false, "List available templates")
flag.IntVar(&options.RateLimit, "rate-limit", 150, "Rate-Limit (maximum requests/second")
flag.BoolVar(&options.StopAtFirstMatch, "stop-at-first-match", false, "Stop processing http requests at first match (this may break template/workflow logic)")
2020-10-20 19:21:11 +02:00
flag.IntVar(&options.BulkSize, "bulk-size", 25, "Maximum Number of hosts analyzed in parallel per template")
flag.IntVar(&options.TemplateThreads, "c", 10, "Maximum Number of templates executed in parallel")
2020-10-17 02:10:47 +02:00
flag.BoolVar(&options.Project, "project", false, "Use a project folder to avoid sending same request multiple times")
flag.StringVar(&options.ProjectPath, "project-path", "", "Use a user defined project folder, temporary folder is used if not specified but enabled")
2020-10-20 01:57:38 +05:30
flag.BoolVar(&options.NoMeta, "no-meta", false, "Don't display metadata for the matches")
flag.BoolVar(&options.TemplatesVersion, "templates-version", false, "Shows the installed nuclei-templates version")
2020-10-23 10:13:34 +02:00
flag.StringVar(&options.BurpCollaboratorBiid, "burp-collaborator-biid", "", "Burp Collaborator BIID")
2020-04-04 03:45:39 +05:30
flag.Parse()
// Check if stdin pipe was given
options.Stdin = hasStdin()
// Read the inputs and configure the logging
options.configureOutput()
// Show the user the banner
showBanner()
if options.Version {
gologger.Infof("Current Version: %s\n", Version)
os.Exit(0)
}
if options.TemplatesVersion {
config, err := readConfiguration()
if err != nil {
gologger.Fatalf("Could not read template configuration: %s\n", err)
}
gologger.Infof("Current nuclei-templates version: %s (%s)\n", config.CurrentVersion, config.TemplatesDirectory)
os.Exit(0)
}
2020-04-04 03:45:39 +05:30
// Validate the options passed by the user and if any
// invalid options have been used, exit.
err := options.validateOptions()
if err != nil {
gologger.Fatalf("Program exiting: %s\n", err)
}
2020-04-04 03:45:39 +05:30
return options
}
func hasStdin() bool {
2020-09-28 12:50:00 +02:00
stat, err := os.Stdin.Stat()
2020-04-04 03:45:39 +05:30
if err != nil {
return false
}
2020-09-28 12:50:00 +02:00
isPipedFromChrDev := (stat.Mode() & os.ModeCharDevice) == 0
isPipedFromFIFO := (stat.Mode() & os.ModeNamedPipe) != 0
2020-09-28 12:50:00 +02:00
return isPipedFromChrDev || isPipedFromFIFO
2020-04-04 03:45:39 +05:30
}
2020-08-29 15:26:11 +02:00
// validateOptions validates the configuration options passed
func (options *Options) validateOptions() error {
// Both verbose and silent flags were used
if options.Verbose && options.Silent {
return errors.New("both verbose and silent mode specified")
}
if !options.TemplateList {
2020-08-29 15:26:11 +02:00
// Check if a list of templates was provided and it exists
if len(options.Templates) == 0 && !options.UpdateTemplates {
return errors.New("no template/templates provided")
}
if options.Targets == "" && !options.Stdin && options.Target == "" && !options.UpdateTemplates {
return errors.New("no target input provided")
}
}
// Validate proxy options if provided
2020-08-29 16:25:30 +02:00
err := validateProxyURL(
options.ProxyURL,
"invalid http proxy format (It should be http://username:password@host:port)",
)
if err != nil {
return err
}
err = validateProxyURL(
options.ProxySocksURL,
"invalid socks proxy format (It should be socks5://username:password@host:port)",
)
if err != nil {
return err
2020-08-29 15:26:11 +02:00
}
2020-08-29 16:25:30 +02:00
return nil
}
func validateProxyURL(proxyURL, message string) error {
if proxyURL != "" && !isValidURL(proxyURL) {
return errors.New(message)
2020-08-29 15:26:11 +02:00
}
return nil
}
2020-08-29 16:25:30 +02:00
func isValidURL(urlString string) bool {
_, err := url.Parse(urlString)
2020-08-29 15:26:11 +02:00
return err == nil
}
// configureOutput configures the output on the screen
func (options *Options) configureOutput() {
// If the user desires verbose output, show verbose output
if options.Verbose {
gologger.MaxLevel = gologger.Verbose
}
if options.NoColor {
gologger.UseColors = false
}
if options.Silent {
gologger.MaxLevel = gologger.Silent
}
}