nuclei/v2/pkg/parsers/parser.go

117 lines
3.4 KiB
Go

package parsers
import (
"fmt"
"io/ioutil"
"os"
"regexp"
"gopkg.in/yaml.v2"
"github.com/projectdiscovery/gologger"
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/loader/filter"
"github.com/projectdiscovery/nuclei/v2/pkg/model"
"github.com/projectdiscovery/nuclei/v2/pkg/templates"
"github.com/projectdiscovery/nuclei/v2/pkg/utils"
)
const mandatoryFieldMissingTemplate = "mandatory '%s' field is missing"
// LoadTemplate loads a template by parsing metadata and running all tag
// and path based filters on the template.
func LoadTemplate(templatePath string, tagFilter *filter.TagFilter) (bool, error) {
return load(templatePath, false, nil, tagFilter)
}
// LoadTemplate loads a template by parsing metadata and running all tag
// based filters on the template.
//
// isWorkflow when false, means that the template itself is not a workflow
// however options tags may have been passed in workflowTags slice.
func LoadWorkflow(templatePath string, isWorkflow bool, tagFilter *filter.TagFilter, workflowTags []string) (bool, error) {
return load(templatePath, isWorkflow, workflowTags, tagFilter)
}
func load(path string, isWorkflow bool, workflowTags []string, tagFilter *filter.TagFilter) (bool, error) {
template, templateParseError := parseTemplate(path)
if templateParseError != nil {
return false, templateParseError
}
// If this is called for a workflow and we don't have a workflow, return
if isWorkflow && len(template.Workflows) == 0 {
return false, nil
}
templateInfo := template.Info
if validationError := validateMandatoryInfoFields(&templateInfo); validationError != nil {
return false, validationError
}
// Validation of the metadata match happens in all scenarios.
templateMatch, matchErr := isInfoMetadataMatch(tagFilter, &templateInfo, []string{})
if matchErr != nil || !templateMatch {
return false, matchErr
}
return true, nil
}
func isInfoMetadataMatch(tagFilter *filter.TagFilter, templateInfo *model.Info, workflowTags []string) (bool, error) {
templateTags := templateInfo.Tags.ToSlice()
templateAuthors := templateInfo.Authors.ToSlice()
templateSeverity := templateInfo.SeverityHolder.Severity
var match bool
var err error
if len(workflowTags) == 0 {
match, err = tagFilter.Match(templateTags, templateAuthors, templateSeverity)
} else {
match, err = tagFilter.MatchWithWorkflowTags(templateTags, templateAuthors, templateSeverity, workflowTags)
}
if err == filter.ErrExcluded {
return false, filter.ErrExcluded
}
return match, nil
}
func validateMandatoryInfoFields(info *model.Info) error {
if info == nil {
return fmt.Errorf(mandatoryFieldMissingTemplate, "info")
}
if utils.IsBlank(info.Name) {
return fmt.Errorf(mandatoryFieldMissingTemplate, "name")
}
if info.Authors.IsEmpty() {
return fmt.Errorf(mandatoryFieldMissingTemplate, "author")
}
return nil
}
var fieldErrorRegexp = regexp.MustCompile(`not found in`)
func parseTemplate(templatePath string) (*templates.Template, error) {
f, err := os.Open(templatePath)
if err != nil {
return nil, err
}
defer f.Close()
data, err := ioutil.ReadAll(f)
if err != nil {
return nil, err
}
template := &templates.Template{}
err = yaml.UnmarshalStrict(data, template)
if err != nil {
if fieldErrorRegexp.MatchString(err.Error()) {
gologger.Warning().Msgf("Unrecognized fields in template %s: %s", templatePath, err)
return template, nil
}
return nil, err
}
return template, nil
}