Skip to content

Commit 15bc3e5

Browse files
Added support for additional validation rules in custom detector (#4413)
* Added support for additional validation rules in custom detector * refactored the approach to support validations for each regex in config * resolved comments
1 parent bb899f2 commit 15bc3e5

File tree

9 files changed

+850
-44
lines changed

9 files changed

+850
-44
lines changed

examples/generic_with_filters.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ detectors:
1111
regex:
1212
secret: |-
1313
(?i)[\w.-]{0,50}?(?:access|auth|(?-i:[Aa]pi|API)|credential|creds|key|passw(?:or)?d|secret|token)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([\w.=-]{10,150}|[a-z0-9][a-z0-9+/]{11,}={0,3})(?:[\x60'"\s;]|\\[nr]|$)
14+
validations:
15+
secret: # name of the regex to apply these validations to
16+
contains_digit: true
17+
contains_special_char: true
1418
entropy: 3.5
1519
# exclude_regexes_capture:
1620
# - |-

pkg/custom_detectors/CUSTOM_DETECTORS.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ This guide will walk you through setting up a custom detector in TruffleHog to i
4343
- **`exclude_regexes_match`**: This parameter enables you to define regex patterns to exclude entire matches from being reported as secrets. This applies to the entire matched string, not just the token.
4444
- **`entropy`**: This parameter is used to assess the randomness of detected strings. High entropy often indicates that a string is a potential secret, such as an API key or password, due to its complexity and unpredictability. It helps in filtering false-positives. While an entropy threshold of `3` can be a starting point, it's essential to adjust this value based on your project's specific requirements and the nature of the data you have.
4545
- **`exclude_words`**: This parameter allows you to specify a list of words that, if present in a detected string, will cause TruffleHog to ignore that string. This is a substring match and does not enforce word boundaries. It applies only to the token.
46+
- **`validations`**: This parameter lets you define extra validation rules for each regex specified in the regex option. These rules address limitations of Go's RE2 engine, such as the lack of lookahead support, and are applied after a regex match to help reduce false positives.
47+
Available validation options:
48+
- **`contains_digit`**: Ensures the match contains at least one numeric digit (0-9). Useful for API keys or tokens that must include numbers.
49+
- **`contains_lowercase`**: Ensures the match contains at least one lowercase letter (a-z). Common requirement for passwords and mixed-case tokens.
50+
- **`contains_uppercase`**: Ensures the match contains at least one uppercase letter (A-Z). Helps validate tokens that follow mixed-case conventions.
51+
- **`contains_special_char`**: Ensures the match contains at least one special character from the set `!@#$%^&*()_+-=[]{}|;:,.<>?`. Useful for complex passwords or encoded tokens.
52+
4653

4754
[Here](/examples/generic_with_filters.yml) is an example of a custom detector using these parameters.
4855

pkg/custom_detectors/custom_detectors.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ func (c *CustomRegexWebhook) FromData(ctx context.Context, verify bool, data []b
116116

117117
MatchLoop:
118118
for _, match := range matches {
119-
for _, values := range match {
119+
for key, values := range match {
120120
// attempt to use capture group
121121
secret := values[0]
122122
if len(values) > 1 {
@@ -150,6 +150,26 @@ MatchLoop:
150150
continue MatchLoop
151151
}
152152
}
153+
154+
if validations := c.GetValidations(); validations != nil {
155+
validationRules := []struct {
156+
enabled bool
157+
validator func(string) bool
158+
}{
159+
{validations[key].GetContainsDigit(), ContainsDigit},
160+
{validations[key].GetContainsLowercase(), ContainsLowercase},
161+
{validations[key].GetContainsUppercase(), ContainsUppercase},
162+
{validations[key].GetContainsSpecialChar(), ContainsSpecialChar},
163+
}
164+
165+
for _, rule := range validationRules {
166+
if rule.enabled && !rule.validator(secret) {
167+
// skip this match if a validation rule is enabled but missing from the secret
168+
continue MatchLoop
169+
}
170+
}
171+
}
172+
153173
}
154174

155175
g.Go(func() error {

0 commit comments

Comments
 (0)