* Allow filesystem exclusion to eagerly prune the enumerated tree rather than skipping files afterwards * added test case * resolved charlie's comment * resolved cody comments * updated test case * remove shouldExclude method * added test cases and fixed the logic * removed print statement * renamed test case * assert->require in tests
124 lines
3.1 KiB
Go
124 lines
3.1 KiB
Go
package common
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"regexp"
|
|
|
|
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
|
|
)
|
|
|
|
type Filter struct {
|
|
include *FilterRuleSet
|
|
exclude *FilterRuleSet
|
|
}
|
|
|
|
type FilterRuleSet []regexp.Regexp
|
|
|
|
// FilterEmpty returns a Filter that always passes.
|
|
func FilterEmpty() *Filter {
|
|
filter, err := FilterFromFiles("", "")
|
|
if err != nil {
|
|
context.Background().Logger().Error(err, "could not create empty filter")
|
|
os.Exit(1)
|
|
}
|
|
return filter
|
|
}
|
|
|
|
// FilterFromFiles creates a Filter using the rules in the provided include and exclude files.
|
|
func FilterFromFiles(includeFilterPath, excludeFilterPath string) (*Filter, error) {
|
|
includeRules, err := FilterRulesFromFile(includeFilterPath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not create include rules: %s", err)
|
|
}
|
|
excludeRules, err := FilterRulesFromFile(excludeFilterPath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not create exclude rules: %s", err)
|
|
}
|
|
|
|
// If no includeFilterPath is provided, every pattern should pass the include rules.
|
|
if includeFilterPath == "" {
|
|
includeRules = &FilterRuleSet{*regexp.MustCompile("")}
|
|
}
|
|
|
|
filter := &Filter{
|
|
include: includeRules,
|
|
exclude: excludeRules,
|
|
}
|
|
|
|
return filter, nil
|
|
}
|
|
|
|
// FilterRulesFromFile loads the list of regular expression filter rules in `source` and creates a FilterRuleSet.
|
|
func FilterRulesFromFile(source string) (*FilterRuleSet, error) {
|
|
rules := FilterRuleSet{}
|
|
if source == "" {
|
|
return &rules, nil
|
|
}
|
|
|
|
commentPattern := regexp.MustCompile(`^\s*#`)
|
|
emptyLinePattern := regexp.MustCompile(`^\s*$`)
|
|
|
|
file, err := os.Open(source)
|
|
logger := context.Background().Logger().WithValues("file", source)
|
|
if err != nil {
|
|
logger.Error(err, "unable to open filter file", "file", source)
|
|
os.Exit(1)
|
|
}
|
|
defer func(file *os.File) {
|
|
err := file.Close()
|
|
if err != nil {
|
|
logger.Error(err, "unable to close filter file")
|
|
os.Exit(1)
|
|
}
|
|
}(file)
|
|
|
|
scanner := bufio.NewScanner(file)
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
if commentPattern.MatchString(line) {
|
|
continue
|
|
}
|
|
if emptyLinePattern.MatchString(line) {
|
|
continue
|
|
}
|
|
pattern, err := regexp.Compile(line)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("can not compile regular expression: %s", line)
|
|
}
|
|
rules = append(rules, *pattern)
|
|
}
|
|
return &rules, nil
|
|
}
|
|
|
|
// Pass returns true if the include FilterRuleSet matches the pattern and the exclude FilterRuleSet does not match.
|
|
func (filter *Filter) Pass(object string) bool {
|
|
if filter == nil {
|
|
return true
|
|
}
|
|
|
|
excluded := filter.exclude.Matches(object)
|
|
included := filter.include.Matches(object)
|
|
|
|
return !excluded && included
|
|
}
|
|
|
|
// Matches will return true if any of the regular expressions in the FilterRuleSet match the pattern.
|
|
func (rules *FilterRuleSet) Matches(object string) bool {
|
|
if rules == nil {
|
|
return false
|
|
}
|
|
for _, rule := range *rules {
|
|
if rule.MatchString(object) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// ShouldExclude return true if any regular expressions in the exclude FilterRuleSet matches the path.
|
|
func (filter *Filter) ShouldExclude(path string) bool {
|
|
return filter.exclude.Matches(path)
|
|
}
|