diff --git a/v2/cmd/nuclei/main.go b/v2/cmd/nuclei/main.go index cc4a2c2b..83b13fc6 100644 --- a/v2/cmd/nuclei/main.go +++ b/v2/cmd/nuclei/main.go @@ -71,6 +71,7 @@ on extensive configurability, massive extensibility and ease of use.`) flagSet.StringSliceVarP(&options.ExcludedTemplates, "exclude", "exclude-templates", []string{}, "template or template directory paths to exclude"), flagSet.VarP(&options.Severities, "impact", "severity", fmt.Sprintf("Templates to run based on severity. Possible values: %s", severity.GetSupportedSeverities().String())), + flagSet.VarP(&options.ExcludeSeverities, "exclude-impact", "exclude-severity", fmt.Sprintf("Templates to exclude based on severity. Possible values: %s", severity.GetSupportedSeverities().String())), flagSet.NormalizedStringSliceVar(&options.Author, "author", []string{}, "execute templates that are (co-)created by the specified authors"), ) diff --git a/v2/internal/runner/runner.go b/v2/internal/runner/runner.go index 4d8b7cf5..e87903f1 100644 --- a/v2/internal/runner/runner.go +++ b/v2/internal/runner/runner.go @@ -344,6 +344,7 @@ func (r *Runner) RunEnumeration() error { IncludeTemplates: r.options.IncludeTemplates, Authors: r.options.Author, Severities: r.options.Severities, + ExcludeSeverities: r.options.ExcludeSeverities, IncludeTags: r.options.IncludeTags, TemplatesDirectory: r.options.TemplatesDirectory, Catalog: r.catalog, diff --git a/v2/pkg/catalog/loader/filter/tag_filter.go b/v2/pkg/catalog/loader/filter/tag_filter.go index bfc75bd1..ed8722c9 100644 --- a/v2/pkg/catalog/loader/filter/tag_filter.go +++ b/v2/pkg/catalog/loader/filter/tag_filter.go @@ -9,11 +9,12 @@ import ( // TagFilter is used to filter nuclei templates for tag based execution type TagFilter struct { - allowedTags map[string]struct{} - severities map[severity.Severity]struct{} - authors map[string]struct{} - block map[string]struct{} - matchAllows map[string]struct{} + allowedTags map[string]struct{} + severities map[severity.Severity]struct{} + excludeSeverities map[severity.Severity]struct{} + authors map[string]struct{} + block map[string]struct{} + matchAllows map[string]struct{} } // ErrExcluded is returned for excluded templates @@ -54,15 +55,21 @@ func (tagFilter *TagFilter) Match(templateTags, templateAuthors []string, templa } func isSeverityMatch(tagFilter *TagFilter, templateSeverity severity.Severity) bool { - if len(tagFilter.severities) == 0 || templateSeverity == severity.Undefined { + if (len(tagFilter.excludeSeverities) == 0 && len(tagFilter.severities) == 0) || templateSeverity == severity.Undefined { return true } - if _, ok := tagFilter.severities[templateSeverity]; ok { - return true + included := true + if len(tagFilter.severities) > 0 { + _, included = tagFilter.severities[templateSeverity] } - return false + excluded := false + if len(tagFilter.excludeSeverities) > 0 { + _, excluded = tagFilter.excludeSeverities[templateSeverity] + } + + return included && !excluded } func isAuthorMatch(tagFilter *TagFilter, templateAuthors []string) bool { @@ -110,23 +117,25 @@ func isTagMatch(tagFilter *TagFilter, templateTags []string) bool { } type Config struct { - Tags []string - ExcludeTags []string - Authors []string - Severities severity.Severities - IncludeTags []string + Tags []string + ExcludeTags []string + Authors []string + Severities severity.Severities + ExcludeSeverities severity.Severities + IncludeTags []string } // New returns a tag filter for nuclei tag based execution // -// It takes into account Tags, Severities, Authors, IncludeTags, ExcludeTags. +// It takes into account Tags, Severities, ExcludeSeverities, Authors, IncludeTags, ExcludeTags. func New(config *Config) *TagFilter { filter := &TagFilter{ - allowedTags: make(map[string]struct{}), - authors: make(map[string]struct{}), - severities: make(map[severity.Severity]struct{}), - block: make(map[string]struct{}), - matchAllows: make(map[string]struct{}), + allowedTags: make(map[string]struct{}), + authors: make(map[string]struct{}), + severities: make(map[severity.Severity]struct{}), + excludeSeverities: make(map[severity.Severity]struct{}), + block: make(map[string]struct{}), + matchAllows: make(map[string]struct{}), } for _, tag := range config.ExcludeTags { for _, val := range splitCommaTrim(tag) { @@ -140,6 +149,11 @@ func New(config *Config) *TagFilter { filter.severities[tag] = struct{}{} } } + for _, tag := range config.ExcludeSeverities { + if _, ok := filter.excludeSeverities[tag]; !ok { + filter.excludeSeverities[tag] = struct{}{} + } + } for _, tag := range config.Authors { for _, val := range splitCommaTrim(tag) { if _, ok := filter.authors[val]; !ok { diff --git a/v2/pkg/catalog/loader/filter/tag_filter_test.go b/v2/pkg/catalog/loader/filter/tag_filter_test.go index 8acc9a35..22d18b18 100644 --- a/v2/pkg/catalog/loader/filter/tag_filter_test.go +++ b/v2/pkg/catalog/loader/filter/tag_filter_test.go @@ -73,6 +73,16 @@ func TestTagBasedFilter(t *testing.T) { matched, _ := filter.Match([]string{"fuzz"}, []string{"pdteam"}, severity.High, nil) require.True(t, matched, "could not get correct match") }) + t.Run("match-exclude-severity", func(t *testing.T) { + filter := New(&Config{ + ExcludeSeverities: severity.Severities{severity.Low}, + }) + matched, _ := filter.Match([]string{"fuzz"}, []string{"pdteam"}, severity.High, nil) + require.True(t, matched, "could not get correct match") + + matched, _ = filter.Match([]string{"fuzz"}, []string{"pdteam"}, severity.Low, nil) + require.False(t, matched, "could not get correct match") + }) t.Run("match-exclude-with-tags", func(t *testing.T) { filter := New(&Config{ Tags: []string{"tag"}, diff --git a/v2/pkg/catalog/loader/loader.go b/v2/pkg/catalog/loader/loader.go index fee38244..eac22b4a 100644 --- a/v2/pkg/catalog/loader/loader.go +++ b/v2/pkg/catalog/loader/loader.go @@ -19,11 +19,12 @@ type Config struct { ExcludeTemplates []string IncludeTemplates []string - Tags []string - ExcludeTags []string - Authors []string - Severities severity.Severities - IncludeTags []string + Tags []string + ExcludeTags []string + Authors []string + Severities severity.Severities + ExcludeSeverities severity.Severities + IncludeTags []string Catalog *catalog.Catalog ExecutorOptions protocols.ExecuterOptions @@ -49,11 +50,12 @@ func New(config *Config) (*Store, error) { store := &Store{ config: config, tagFilter: filter.New(&filter.Config{ - Tags: config.Tags, - ExcludeTags: config.ExcludeTags, - Authors: config.Authors, - Severities: config.Severities, - IncludeTags: config.IncludeTags, + Tags: config.Tags, + ExcludeTags: config.ExcludeTags, + Authors: config.Authors, + Severities: config.Severities, + ExcludeSeverities: config.ExcludeSeverities, + IncludeTags: config.IncludeTags, }), pathFilter: filter.NewPathFilter(&filter.PathFilterConfig{ IncludedTemplates: config.IncludeTemplates, diff --git a/v2/pkg/types/types.go b/v2/pkg/types/types.go index 91db2b91..1b80c4ed 100644 --- a/v2/pkg/types/types.go +++ b/v2/pkg/types/types.go @@ -25,6 +25,8 @@ type Options struct { Vars goflags.RuntimeMap // Severities filters templates based on their severity and only run the matching ones. Severities severity.Severities + // ExcludeSeverities specifies severities to exclude + ExcludeSeverities severity.Severities // Author filters templates based on their author and only run the matching ones. Author goflags.NormalizedStringSlice // IncludeTags includes specified tags to be run even while being in denylist