Files
trufflehog/pkg/common/filter.go
Brad Larsen 2bffafb280 Clean up use of wasilibs/go-re2 (#5273)
* perf: switch nearly all remaining uses of stdlib regexp to wasilibs/go-re2
* perf: upgrade github.com/wasilibs/go-re2 from v1.9.0 to v1.12.0
* gofmt modified files
* snowflake detector: hoist constant regex compilation to package-level variables
* add golangci lint to steer folks to go-re2 instead of regexp
2026-09-09 17:25:38 -04:00

124 lines
3.1 KiB
Go

package common
import (
"bufio"
"fmt"
regexp "github.com/wasilibs/go-re2"
"os"
"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)
}