100 lines
3.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package matchers
import (
"errors"
"fmt"
"reflect"
"strings"
"github.com/antchfx/xpath"
sliceutil "github.com/projectdiscovery/utils/slice"
)
var commonExpectedFields = []string{"Type", "Condition", "Name", "MatchAll", "Negative", "Internal"}
// Validate perform initial validation on the matcher structure
func (matcher *Matcher) Validate() error {
// Build a map of YAMLtag names that are actually set (non-zero) in the matcher.
matcherMap := make(map[string]interface{})
val := reflect.ValueOf(*matcher)
typ := reflect.TypeOf(*matcher)
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
// skip internal / unexported or opt-out fields
yamlTag := strings.Split(field.Tag.Get("yaml"), ",")[0]
if yamlTag == "" || yamlTag == "-" {
continue
}
if val.Field(i).IsZero() {
continue
}
matcherMap[yamlTag] = struct{}{}
}
var err error
var expectedFields []string
switch matcher.matcherType {
case DSLMatcher:
expectedFields = append(commonExpectedFields, "DSL")
case StatusMatcher:
expectedFields = append(commonExpectedFields, "Status", "Part")
case SizeMatcher:
expectedFields = append(commonExpectedFields, "Size", "Part")
case WordsMatcher:
expectedFields = append(commonExpectedFields, "Words", "Part", "Encoding", "CaseInsensitive")
case BinaryMatcher:
expectedFields = append(commonExpectedFields, "Binary", "Part", "Encoding", "CaseInsensitive")
case RegexMatcher:
expectedFields = append(commonExpectedFields, "Regex", "Part", "Encoding", "CaseInsensitive")
case XPathMatcher:
expectedFields = append(commonExpectedFields, "XPath", "Part")
}
if err = checkFields(matcher, matcherMap, expectedFields...); err != nil {
return err
}
// validate the XPath query
if matcher.matcherType == XPathMatcher {
for _, query := range matcher.XPath {
if _, err = xpath.Compile(query); err != nil {
return err
}
}
}
return nil
}
func checkFields(m *Matcher, matcherMap map[string]interface{}, expectedFields ...string) error {
var foundUnexpectedFields []string
for marshaledFieldName := range matcherMap {
// revert back the marshaled name to the original field
structFieldName, err := getFieldNameFromYamlTag(marshaledFieldName, *m)
if err != nil {
return err
}
if !sliceutil.Contains(expectedFields, structFieldName) {
foundUnexpectedFields = append(foundUnexpectedFields, structFieldName)
}
}
if len(foundUnexpectedFields) > 0 {
return fmt.Errorf("matcher %s has unexpected fields: %s", m.matcherType, strings.Join(foundUnexpectedFields, ","))
}
return nil
}
func getFieldNameFromYamlTag(tagName string, object interface{}) (string, error) {
reflectType := reflect.TypeOf(object)
if reflectType.Kind() != reflect.Struct {
return "", errors.New("the object must be a struct")
}
for idx := 0; idx < reflectType.NumField(); idx++ {
field := reflectType.Field(idx)
tagParts := strings.Split(field.Tag.Get("yaml"), ",")
if len(tagParts) > 0 && tagParts[0] == tagName {
return field.Name, nil
}
}
return "", fmt.Errorf("field %s not found", tagName)
}