Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check severity att while validating #3540

Merged
5 changes: 3 additions & 2 deletions v2/pkg/catalog/loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,8 +319,9 @@ func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) []*templ
loadedTemplates = append(loadedTemplates, parsed)
}
}
} else if err != nil {
gologger.Warning().Msgf("Could not load template %s: %s\n", templatePath, err)
}
if err != nil {
gologger.Warning().Msg(err.Error())
}
}

Expand Down
50 changes: 44 additions & 6 deletions v2/pkg/parsers/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/projectdiscovery/nuclei/v2/pkg/templates"
"github.com/projectdiscovery/nuclei/v2/pkg/templates/cache"
"github.com/projectdiscovery/nuclei/v2/pkg/templates/signer"
"github.com/projectdiscovery/nuclei/v2/pkg/templates/types"
"github.com/projectdiscovery/nuclei/v2/pkg/utils"
"github.com/projectdiscovery/nuclei/v2/pkg/utils/stats"
"gopkg.in/yaml.v2"
Expand All @@ -20,22 +21,32 @@ import (
const (
errMandatoryFieldMissingFmt = "mandatory '%s' field is missing"
errInvalidFieldFmt = "invalid field format for '%s' (allowed format is %s)"
warningFieldMissingFmt = "field '%s' is missing"
)

// LoadTemplate returns true if the template is valid and matches the filtering criteria.
func LoadTemplate(templatePath string, tagFilter *filter.TagFilter, extraTags []string, catalog catalog.Catalog) (bool, error) {
template, templateParseError := ParseTemplate(templatePath, catalog)
if templateParseError != nil {
return false, templateParseError
return false, fmt.Errorf("Could not load template %s: %s", templatePath, templateParseError)
}

if len(template.Workflows) > 0 {
return false, nil
}

if validationError := validateTemplateFields(template); validationError != nil {
validationError, validationWarning := validateTemplateFields(template)
if validationError != nil {
stats.Increment(SyntaxErrorStats)
return false, validationError
if validationWarning != nil {
return false, fmt.Errorf("Could not load template %s: %s, with syntax warning: %s", templatePath, validationError, validationWarning)
} else {
return false, fmt.Errorf("Could not load template %s: %s", templatePath, validationError)
}
}
// If we have warnings, we should still return true
if validationWarning != nil {
stats.Increment(SyntaxWarningStats)
return true, fmt.Errorf("Loaded template %s: with syntax warning : %s", templatePath, validationWarning)
}

return isTemplateInfoMetadataMatch(tagFilter, template, extraTags)
Expand All @@ -49,7 +60,8 @@ func LoadWorkflow(templatePath string, catalog catalog.Catalog) (bool, error) {
}

if len(template.Workflows) > 0 {
if validationError := validateTemplateFields(template); validationError != nil {
if validationError, _ := validateTemplateFields(template); validationError != nil {
stats.Increment(SyntaxErrorStats)
return false, validationError
}
return true, nil
Expand All @@ -68,7 +80,15 @@ func isTemplateInfoMetadataMatch(tagFilter *filter.TagFilter, template *template
return match, err
}

func validateTemplateFields(template *templates.Template) error {
func validateTemplateFields(template *templates.Template) (err error, warning error) {
ehsandeep marked this conversation as resolved.
Show resolved Hide resolved
err = validateTemplateMandatoryFields(template)
warning = validateTemplateOptionalFields(template)
return
}

// validateTemplateMandatoryFields validates the mandatory fields of a template
// return error from this function will cause hard fail and not proceed further
func validateTemplateMandatoryFields(template *templates.Template) error {
info := template.Info

var errors []string
Expand All @@ -94,6 +114,24 @@ func validateTemplateFields(template *templates.Template) error {
return nil
}

// validateTemplateOptionalFields validates the optional fields of a template
// return error from this function will throw a warning and proceed further
func validateTemplateOptionalFields(template *templates.Template) error {
info := template.Info

var warnings []string

if template.Type() != types.WorkflowProtocol && utils.IsBlank(info.SeverityHolder.Severity.String()) {
warnings = append(warnings, fmt.Sprintf(warningFieldMissingFmt, "severity"))
}

if len(warnings) > 0 {
return fmt.Errorf(strings.Join(warnings, ", "))
}

return nil
}

var (
parsedTemplatesCache *cache.Templates
ShouldValidate bool
Expand Down
38 changes: 28 additions & 10 deletions v2/pkg/parsers/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/disk"
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/loader/filter"
"github.com/projectdiscovery/nuclei/v2/pkg/model"
"github.com/projectdiscovery/nuclei/v2/pkg/model/types/severity"
"github.com/projectdiscovery/nuclei/v2/pkg/model/types/stringslice"
"github.com/projectdiscovery/nuclei/v2/pkg/templates"
"github.com/stretchr/testify/require"
Expand All @@ -24,32 +25,49 @@ func TestLoadTemplate(t *testing.T) {
templateErr error

expectedErr error
isValid bool
}{
{
name: "valid",
template: &templates.Template{
ID: "CVE-2021-27330",
Info: model.Info{
Name: "Valid template",
Authors: stringslice.StringSlice{Value: "Author"},
Name: "Valid template",
Authors: stringslice.StringSlice{Value: "Author"},
SeverityHolder: severity.Holder{Severity: severity.Medium},
},
},
isValid: true,
},
{
name: "emptyTemplate",
template: &templates.Template{},
expectedErr: errors.New("mandatory 'name' field is missing, mandatory 'author' field is missing, mandatory 'id' field is missing"),
isValid: false,
expectedErr: errors.New("mandatory 'name' field is missing, mandatory 'author' field is missing, mandatory 'id' field is missing, with syntax warning: field 'severity' is missing"),
},
{
name: "emptyNameWithInvalidID",
template: &templates.Template{
ID: "invalid id",
Info: model.Info{
Authors: stringslice.StringSlice{Value: "Author"},
Authors: stringslice.StringSlice{Value: "Author"},
SeverityHolder: severity.Holder{Severity: severity.Medium},
},
},
expectedErr: errors.New("mandatory 'name' field is missing, invalid field format for 'id' (allowed format is ^([a-zA-Z0-9]+[-_])*[a-zA-Z0-9]+$)"),
},
{
name: "emptySeverity",
template: &templates.Template{
ID: "CVE-2021-27330",
Info: model.Info{
Name: "Valid template",
Authors: stringslice.StringSlice{Value: "Author"},
},
},
isValid: true,
expectedErr: errors.New("field 'severity' is missing"),
},
}

for _, tc := range tt {
Expand All @@ -59,12 +77,11 @@ func TestLoadTemplate(t *testing.T) {
tagFilter, err := filter.New(&filter.Config{})
require.Nil(t, err)
success, err := LoadTemplate(tc.name, tagFilter, nil, catalog)
require.Equal(t, tc.isValid, success)
if tc.expectedErr == nil {
require.NoError(t, err)
require.True(t, success)
} else {
require.Equal(t, tc.expectedErr, err)
require.False(t, success)
require.ErrorContains(t, err, tc.expectedErr.Error())
}
})
}
Expand Down Expand Up @@ -92,8 +109,9 @@ func TestLoadTemplate(t *testing.T) {
template := &templates.Template{
ID: tc.id,
Info: model.Info{
Name: "Valid template",
Authors: stringslice.StringSlice{Value: "Author"},
Name: "Valid template",
Authors: stringslice.StringSlice{Value: "Author"},
SeverityHolder: severity.Holder{Severity: severity.Medium},
},
}
parsedTemplatesCache.Store(name, template, nil)
Expand All @@ -105,7 +123,7 @@ func TestLoadTemplate(t *testing.T) {
require.NoError(t, err)
require.True(t, success)
} else {
require.Equal(t, errors.New("invalid field format for 'id' (allowed format is ^([a-zA-Z0-9]+[-_])*[a-zA-Z0-9]+$)"), err)
require.ErrorContains(t, err, "invalid field format for 'id' (allowed format is ^([a-zA-Z0-9]+[-_])*[a-zA-Z0-9]+$)")
require.False(t, success)
}
})
Expand Down
11 changes: 5 additions & 6 deletions v2/pkg/parsers/workflow_loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,13 @@ func (w *workflowLoader) GetTemplatePathsByTags(templateTags []string) []string
for template, err := range errs {
gologger.Error().Msgf("Could not find template '%s': %s", template, err)
}

templatePathMap := w.pathFilter.Match(includedTemplates)

loadedTemplates := make([]string, 0, len(templatePathMap))
for templatePath := range templatePathMap {
loaded, err := LoadTemplate(templatePath, w.tagFilter, templateTags, w.options.Catalog)
if err != nil {
gologger.Warning().Msgf("Could not load template %s: %s\n", templatePath, err)
} else if loaded {
loaded, _ := LoadTemplate(templatePath, w.tagFilter, templateTags, w.options.Catalog)
if loaded {
loadedTemplates = append(loadedTemplates, templatePath)
}
}
Expand All @@ -69,8 +68,8 @@ func (w *workflowLoader) GetTemplatePaths(templatesList []string, noValidate boo
loadedTemplates := make([]string, 0, len(templatesPathMap))
for templatePath := range templatesPathMap {
matched, err := LoadTemplate(templatePath, w.tagFilter, nil, w.options.Catalog)
if err != nil {
gologger.Warning().Msgf("Could not load template %s: %s\n", templatePath, err)
if err != nil && !matched {
gologger.Warning().Msg(err.Error())
} else if matched || noValidate {
loadedTemplates = append(loadedTemplates, templatePath)
}
Expand Down