mirror of https://github.com/daffainfo/nuclei.git
Merge pull request #813 from projectdiscovery/validate-templates
Added validate flag to validate templatesdev
commit
b2b6e4243b
|
@ -24,7 +24,9 @@ func main() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gologger.Fatal().Msgf("Could not create runner: %s\n", err)
|
gologger.Fatal().Msgf("Could not create runner: %s\n", err)
|
||||||
}
|
}
|
||||||
nucleiRunner.RunEnumeration()
|
if err := nucleiRunner.RunEnumeration(); err != nil {
|
||||||
|
gologger.Fatal().Msgf("Could not run nuclei: %s\n", err)
|
||||||
|
}
|
||||||
nucleiRunner.Close()
|
nucleiRunner.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,6 +98,7 @@ based on templates offering massive extensibility and ease of use.`)
|
||||||
set.IntVar(&options.InteractionsPollDuration, "interactions-poll-duration", 5, "Number of seconds before each interaction poll request")
|
set.IntVar(&options.InteractionsPollDuration, "interactions-poll-duration", 5, "Number of seconds before each interaction poll request")
|
||||||
set.IntVar(&options.InteractionsColldownPeriod, "interactions-cooldown-period", 5, "Extra time for interaction polling before exiting")
|
set.IntVar(&options.InteractionsColldownPeriod, "interactions-cooldown-period", 5, "Extra time for interaction polling before exiting")
|
||||||
set.BoolVar(&options.VerboseVerbose, "vv", false, "Display Extra Verbose Information")
|
set.BoolVar(&options.VerboseVerbose, "vv", false, "Display Extra Verbose Information")
|
||||||
|
set.BoolVar(&options.Validate, "validate", false, "Validate the passed templates to nuclei")
|
||||||
_ = set.Parse()
|
_ = set.Parse()
|
||||||
|
|
||||||
if cfgFile != "" {
|
if cfgFile != "" {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/logrusorgru/aurora"
|
"github.com/logrusorgru/aurora"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"github.com/projectdiscovery/gologger"
|
"github.com/projectdiscovery/gologger"
|
||||||
"github.com/projectdiscovery/hmap/store/hybrid"
|
"github.com/projectdiscovery/hmap/store/hybrid"
|
||||||
"github.com/projectdiscovery/nuclei/v2/internal/colorizer"
|
"github.com/projectdiscovery/nuclei/v2/internal/colorizer"
|
||||||
|
@ -74,13 +75,13 @@ func New(options *types.Options) (*Runner, error) {
|
||||||
if options.ReportingConfig != "" {
|
if options.ReportingConfig != "" {
|
||||||
file, err := os.Open(options.ReportingConfig)
|
file, err := os.Open(options.ReportingConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gologger.Fatal().Msgf("Could not open reporting config file: %s\n", err)
|
return nil, errors.Wrap(err, "could not open reporting config file")
|
||||||
}
|
}
|
||||||
|
|
||||||
reportingOptions = &reporting.Options{}
|
reportingOptions = &reporting.Options{}
|
||||||
if parseErr := yaml.NewDecoder(file).Decode(reportingOptions); parseErr != nil {
|
if parseErr := yaml.NewDecoder(file).Decode(reportingOptions); parseErr != nil {
|
||||||
file.Close()
|
file.Close()
|
||||||
gologger.Fatal().Msgf("Could not parse reporting config file: %s\n", parseErr)
|
return nil, errors.Wrap(parseErr, "could not parse reporting config file")
|
||||||
}
|
}
|
||||||
file.Close()
|
file.Close()
|
||||||
}
|
}
|
||||||
|
@ -101,11 +102,11 @@ func New(options *types.Options) (*Runner, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if reportingOptions != nil {
|
if reportingOptions != nil {
|
||||||
if client, err := reporting.New(reportingOptions, options.ReportingDB); err != nil {
|
client, err := reporting.New(reportingOptions, options.ReportingDB)
|
||||||
gologger.Fatal().Msgf("Could not create issue reporting client: %s\n", err)
|
if err != nil {
|
||||||
} else {
|
return nil, errors.Wrap(err, "could not create issue reporting client")
|
||||||
runner.issuesClient = client
|
|
||||||
}
|
}
|
||||||
|
runner.issuesClient = client
|
||||||
}
|
}
|
||||||
|
|
||||||
// output coloring
|
// output coloring
|
||||||
|
@ -121,11 +122,11 @@ func New(options *types.Options) (*Runner, error) {
|
||||||
if (len(options.Templates) == 0 || !options.NewTemplates || (options.Targets == "" && !options.Stdin && options.Target == "")) && options.UpdateTemplates {
|
if (len(options.Templates) == 0 || !options.NewTemplates || (options.Targets == "" && !options.Stdin && options.Target == "")) && options.UpdateTemplates {
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
if hm, err := hybrid.New(hybrid.DefaultDiskOptions); err != nil {
|
hm, err := hybrid.New(hybrid.DefaultDiskOptions)
|
||||||
gologger.Fatal().Msgf("Could not create temporary input file: %s\n", err)
|
if err != nil {
|
||||||
} else {
|
return nil, errors.Wrap(err, "could not create temporary input file")
|
||||||
runner.hostMap = hm
|
|
||||||
}
|
}
|
||||||
|
runner.hostMap = hm
|
||||||
|
|
||||||
runner.inputCount = 0
|
runner.inputCount = 0
|
||||||
dupeCount := 0
|
dupeCount := 0
|
||||||
|
@ -157,9 +158,9 @@ func New(options *types.Options) (*Runner, error) {
|
||||||
|
|
||||||
// Handle taget file
|
// Handle taget file
|
||||||
if options.Targets != "" {
|
if options.Targets != "" {
|
||||||
input, err := os.Open(options.Targets)
|
input, inputErr := os.Open(options.Targets)
|
||||||
if err != nil {
|
if inputErr != nil {
|
||||||
gologger.Fatal().Msgf("Could not open targets file '%s': %s\n", options.Targets, err)
|
return nil, errors.Wrap(inputErr, "could not open targets file")
|
||||||
}
|
}
|
||||||
scanner := bufio.NewScanner(input)
|
scanner := bufio.NewScanner(input)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
|
@ -185,7 +186,7 @@ func New(options *types.Options) (*Runner, error) {
|
||||||
// Create the output file if asked
|
// Create the output file if asked
|
||||||
outputWriter, err := output.NewStandardWriter(!options.NoColor, options.NoMeta, options.JSON, options.Output, options.TraceLogFile)
|
outputWriter, err := output.NewStandardWriter(!options.NoColor, options.NoMeta, options.JSON, options.Output, options.TraceLogFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gologger.Fatal().Msgf("Could not create output file '%s': %s\n", options.Output, err)
|
return nil, errors.Wrap(err, "could not create output file")
|
||||||
}
|
}
|
||||||
runner.output = outputWriter
|
runner.output = outputWriter
|
||||||
|
|
||||||
|
@ -245,14 +246,14 @@ func (r *Runner) Close() {
|
||||||
|
|
||||||
// RunEnumeration sets up the input layer for giving input nuclei.
|
// RunEnumeration sets up the input layer for giving input nuclei.
|
||||||
// binary and runs the actual enumeration
|
// binary and runs the actual enumeration
|
||||||
func (r *Runner) RunEnumeration() {
|
func (r *Runner) RunEnumeration() error {
|
||||||
defer r.Close()
|
defer r.Close()
|
||||||
|
|
||||||
// If user asked for new templates to be executed, collect the list from template directory.
|
// If user asked for new templates to be executed, collect the list from template directory.
|
||||||
if r.options.NewTemplates {
|
if r.options.NewTemplates {
|
||||||
templatesLoaded, err := r.readNewTemplatesFile()
|
templatesLoaded, err := r.readNewTemplatesFile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gologger.Warning().Msgf("Could not get newly added templates: %s\n", err)
|
return errors.Wrap(err, "could not get newly added templates")
|
||||||
}
|
}
|
||||||
r.options.Templates = append(r.options.Templates, templatesLoaded...)
|
r.options.Templates = append(r.options.Templates, templatesLoaded...)
|
||||||
}
|
}
|
||||||
|
@ -287,7 +288,14 @@ func (r *Runner) RunEnumeration() {
|
||||||
}
|
}
|
||||||
store, err := loader.New(&loaderConfig)
|
store, err := loader.New(&loaderConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gologger.Fatal().Msgf("Could not load templates from config: %s\n", err)
|
return errors.Wrap(err, "could not load templates from config")
|
||||||
|
}
|
||||||
|
if r.options.Validate {
|
||||||
|
if !store.ValidateTemplates(r.options.Templates, r.options.Workflows) {
|
||||||
|
return errors.New("an error occurred during templates validation")
|
||||||
|
}
|
||||||
|
gologger.Info().Msgf("All templates validated successfully\n")
|
||||||
|
return nil // exit
|
||||||
}
|
}
|
||||||
store.Load()
|
store.Load()
|
||||||
|
|
||||||
|
@ -405,7 +413,7 @@ func (r *Runner) RunEnumeration() {
|
||||||
|
|
||||||
// 0 matches means no templates were found in directory
|
// 0 matches means no templates were found in directory
|
||||||
if templateCount == 0 {
|
if templateCount == 0 {
|
||||||
gologger.Fatal().Msgf("Error, no templates were found.\n")
|
return errors.New("no templates were found")
|
||||||
}
|
}
|
||||||
|
|
||||||
results := &atomic.Bool{}
|
results := &atomic.Bool{}
|
||||||
|
@ -445,6 +453,7 @@ func (r *Runner) RunEnumeration() {
|
||||||
if r.browser != nil {
|
if r.browser != nil {
|
||||||
r.browser.Close()
|
r.browser.Close()
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// readNewTemplatesFile reads newly added templates from directory if it exists
|
// readNewTemplatesFile reads newly added templates from directory if it exists
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package loader
|
package loader
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/projectdiscovery/gologger"
|
"github.com/projectdiscovery/gologger"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/catalog"
|
"github.com/projectdiscovery/nuclei/v2/pkg/catalog"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/loader/filter"
|
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/loader/filter"
|
||||||
|
@ -81,6 +83,67 @@ func (s *Store) Load() {
|
||||||
s.workflows = s.LoadWorkflows(s.config.Workflows)
|
s.workflows = s.LoadWorkflows(s.config.Workflows)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidateTemplates takes a list of templates and validates them
|
||||||
|
// erroring out on discovering any faulty templates.
|
||||||
|
func (s *Store) ValidateTemplates(templatesList, workflowsList []string) bool {
|
||||||
|
includedTemplates := s.config.Catalog.GetTemplatesPath(templatesList)
|
||||||
|
includedWorkflows := s.config.Catalog.GetTemplatesPath(workflowsList)
|
||||||
|
templatesMap := s.pathFilter.Match(includedTemplates)
|
||||||
|
workflowsMap := s.pathFilter.Match(includedWorkflows)
|
||||||
|
|
||||||
|
notErrored := true
|
||||||
|
for k := range templatesMap {
|
||||||
|
_, err := s.loadTemplate(k, false)
|
||||||
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), "cannot create template executer") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err == filter.ErrExcluded {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
notErrored = false
|
||||||
|
gologger.Error().Msgf("Error occurred loading template %s: %s\n", k, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
_, err = templates.Parse(k, s.config.ExecutorOptions)
|
||||||
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), "cannot create template executer") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err == filter.ErrExcluded {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
notErrored = false
|
||||||
|
gologger.Error().Msgf("Error occurred parsing template %s: %s\n", k, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for k := range workflowsMap {
|
||||||
|
_, err := s.loadTemplate(k, true)
|
||||||
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), "cannot create template executer") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err == filter.ErrExcluded {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
notErrored = false
|
||||||
|
gologger.Error().Msgf("Error occurred loading workflow %s: %s\n", k, err)
|
||||||
|
}
|
||||||
|
_, err = templates.Parse(k, s.config.ExecutorOptions)
|
||||||
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), "cannot create template executer") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err == filter.ErrExcluded {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
notErrored = false
|
||||||
|
gologger.Error().Msgf("Error occurred parsing workflow %s: %s\n", k, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return notErrored
|
||||||
|
}
|
||||||
|
|
||||||
// LoadTemplates takes a list of templates and returns paths for them
|
// LoadTemplates takes a list of templates and returns paths for them
|
||||||
func (s *Store) LoadTemplates(templatesList []string) []*templates.Template {
|
func (s *Store) LoadTemplates(templatesList []string) []*templates.Template {
|
||||||
includedTemplates := s.config.Catalog.GetTemplatesPath(templatesList)
|
includedTemplates := s.config.Catalog.GetTemplatesPath(templatesList)
|
||||||
|
|
|
@ -167,7 +167,7 @@ func printCallback(stats clistats.StatisticsClient) {
|
||||||
builder.WriteRune(')')
|
builder.WriteRune(')')
|
||||||
builder.WriteRune('\n')
|
builder.WriteRune('\n')
|
||||||
|
|
||||||
gologger.Print().Msgf("%s", builder.String())
|
fmt.Printf("%s", builder.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
// getMetrics returns a map of important metrics for client
|
// getMetrics returns a map of important metrics for client
|
||||||
|
|
|
@ -104,6 +104,8 @@ type Options struct {
|
||||||
Silent bool
|
Silent bool
|
||||||
// Version specifies if we should just show version and exit
|
// Version specifies if we should just show version and exit
|
||||||
Version bool
|
Version bool
|
||||||
|
// Validate validates the templates passed to nuclei.
|
||||||
|
Validate bool
|
||||||
// Verbose flag indicates whether to show verbose output or not
|
// Verbose flag indicates whether to show verbose output or not
|
||||||
Verbose bool
|
Verbose bool
|
||||||
VerboseVerbose bool
|
VerboseVerbose bool
|
||||||
|
|
Loading…
Reference in New Issue