Files
trufflehog/pkg/custom_detectors/validation_test.go
Jordan Tunstill 37b77001d0
Test / test (push) Waiting to run
Scan for secrets / test (push) Waiting to run
Test / test-community (push) Waiting to run
Lint / golangci-lint (push) Waiting to run
Lint / man-page-staleness (push) Waiting to run
Lint / semgrep (push) Waiting to run
Lint / checksecretparts (push) Waiting to run
Release / mark-latest (push) Canceled after 0s
Release / Release (push) Canceled after 0s
adding customizable successRanges and rotatedRanges to customDetector (#4892)
* adding customizable successRanges and rotatedRanges to customDetector

*setting definitive = true in the legacy 200 path so that an earlier ranged verifier's rangesInEffect = true can't trigger a spurious SetVerificationError after the legacy verifier already confirmed the secret as live.

* adressed review comments
2026-05-11 08:27:50 -07:00

437 lines
8.9 KiB
Go

package custom_detectors
import (
"testing"
)
func TestCustomDetectorsKeywordValidation(t *testing.T) {
tests := []struct {
name string
input []string
wantErr bool
}{
{
name: "Test empty list of keywords",
input: []string{},
wantErr: true,
},
{
name: "Test empty keyword",
input: []string{""},
wantErr: true,
},
{
name: "Test valid keywords",
input: []string{"hello", "world"},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ValidateKeywords(tt.input)
if (got != nil && !tt.wantErr) || (got == nil && tt.wantErr) {
t.Errorf("ValidateKeywords() error = %v, wantErr %v", got, tt.wantErr)
}
})
}
}
func TestCustomDetectorsRegexValidation(t *testing.T) {
tests := []struct {
name string
input map[string]string
wantErr bool
}{
{
name: "Test list of keywords",
input: map[string]string{
"id_pat_example": "([a-zA-Z0-9]{32})",
},
wantErr: false,
},
{
name: "Test empty list of keywords",
input: map[string]string{},
wantErr: true,
},
{
name: "Test invalid regex",
input: map[string]string{
"test": "!!?(?:?)[a-zA-Z0-9]{32}",
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ValidateRegex(tt.input)
if (got != nil && !tt.wantErr) || (got == nil && tt.wantErr) {
t.Errorf("ValidateRegex() error = %v, wantErr %v", got, tt.wantErr)
}
})
}
}
func TestCustomDetectorsVerifyEndpointValidation(t *testing.T) {
tests := []struct {
name string
endpoint string
unsafe bool
wantErr bool
}{
{
name: "Test http endpoint with unsafe flag",
endpoint: "http://localhost:8000/{id_pat_example}",
unsafe: true,
wantErr: false,
},
{
name: "Test http endpoint without unsafe flag",
endpoint: "http://localhost:8000/{id_pat_example}",
unsafe: false,
wantErr: true,
},
{
name: "Test https endpoint with unsafe flag",
endpoint: "https://localhost:8000/{id_pat_example}",
unsafe: true,
wantErr: false,
},
{
name: "Test https endpoint without unsafe flag",
endpoint: "https://localhost:8000/{id_pat_example}",
unsafe: false,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ValidateVerifyEndpoint(tt.endpoint, tt.unsafe)
if (got != nil && !tt.wantErr) || (got == nil && tt.wantErr) {
t.Errorf("ValidateVerifyEndpoint() error = %v, wantErr %v", got, tt.wantErr)
}
})
}
}
func TestCustomDetectorsVerifyHeadersValidation(t *testing.T) {
tests := []struct {
name string
headers []string
wantErr bool
}{
{
name: "Test single header",
headers: []string{"Authorization: Bearer {secret_pat_example.0}"},
wantErr: false,
},
{
name: "Test invalid header",
headers: []string{"Hello world"},
wantErr: true,
},
{
name: "Test ugly header",
headers: []string{"Hello:::::::world::hi:"},
wantErr: false,
},
{
name: "Test empty header",
headers: []string{},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ValidateVerifyHeaders(tt.headers)
if (got != nil && !tt.wantErr) || (got == nil && tt.wantErr) {
t.Errorf("ValidateVerifyHeaders() error = %v, wantErr %v", got, tt.wantErr)
}
})
}
}
func TestCustomDetectorsVerifyRangeValidation(t *testing.T) {
tests := []struct {
name string
ranges []string
wantErr bool
}{
{
name: "Test multiple mixed ranges",
ranges: []string{"200", "300-350"},
wantErr: false,
},
{
name: "Test invalid non-number range",
ranges: []string{"hi"},
wantErr: true,
},
{
name: "Test invalid lower to upper range",
ranges: []string{"200-100"},
wantErr: true,
},
{
name: "Test invalid http range",
ranges: []string{"400-1000"},
wantErr: true,
},
{
name: "Test multiple ranges with invalid inputs",
ranges: []string{"322", "hello-world", "100-200"},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ValidateVerifyRanges(tt.ranges)
if (got != nil && !tt.wantErr) || (got == nil && tt.wantErr) {
t.Errorf("ValidateVerifyRanges() error = %v, wantErr %v", got, tt.wantErr)
}
})
}
}
func TestStatusCodeMatchesRanges(t *testing.T) {
tests := []struct {
name string
code int
ranges []string
want bool
}{
{
name: "single code match",
code: 200,
ranges: []string{"200"},
want: true,
},
{
name: "single code no match",
code: 201,
ranges: []string{"200"},
want: false,
},
{
name: "range lower bound",
code: 200,
ranges: []string{"200-250"},
want: true,
},
{
name: "range upper bound",
code: 250,
ranges: []string{"200-250"},
want: true,
},
{
name: "range middle",
code: 225,
ranges: []string{"200-250"},
want: true,
},
{
name: "range outside below",
code: 199,
ranges: []string{"200-250"},
want: false,
},
{
name: "range outside above",
code: 251,
ranges: []string{"200-250"},
want: false,
},
{
name: "multiple ranges first match",
code: 200,
ranges: []string{"200", "300-399"},
want: true,
},
{
name: "multiple ranges second match",
code: 301,
ranges: []string{"200", "300-399"},
want: true,
},
{
name: "multiple ranges no match",
code: 500,
ranges: []string{"200", "300-399"},
want: false,
},
{
name: "empty ranges",
code: 200,
ranges: nil,
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := StatusCodeMatchesRanges(tt.code, tt.ranges)
if got != tt.want {
t.Errorf("StatusCodeMatchesRanges(%d, %v) = %v, want %v", tt.code, tt.ranges, got, tt.want)
}
})
}
}
func TestCustomDetectorsVerifyRegexVarsValidation(t *testing.T) {
tests := []struct {
name string
regex map[string]string
body string
wantErr bool
}{
{
name: "Regex defined but not used in body",
regex: map[string]string{"id": "[0-9]{1,10}", "id_pat_example": "([a-zA-Z0-9]{32})"},
body: "hello world",
wantErr: false,
},
{
name: "Regex defined and is used in body",
regex: map[string]string{"id": "[0-9]{1,10}", "id_pat_example": "([a-zA-Z0-9]{32})"},
body: "hello world {id}",
wantErr: false,
},
{
name: "Regex var in body but not defined",
regex: map[string]string{"id": "[0-9]{1,10}", "id_pat_example": "([a-zA-Z0-9]{32})"},
body: "hello world {hello}",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ValidateRegexVars(tt.regex, tt.body)
if (got != nil && !tt.wantErr) || (got == nil && tt.wantErr) {
t.Errorf("ValidateRegexVars() error = %v, wantErr %v", got, tt.wantErr)
}
})
}
}
func TestContainsDigit(t *testing.T) {
type args struct {
s string
}
tests := []struct {
name string
args args
want bool
}{
{
name: "contains digit",
args: args{s: "lzscqf&60M"},
want: true,
},
{
name: "does not contains digit",
args: args{s: "ZlDQOdaM*vsT"},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := ContainsDigit(tt.args.s); got != tt.want {
t.Errorf("ContainsDigit() = %v, want %v", got, tt.want)
}
})
}
}
func TestContainsLowercase(t *testing.T) {
type args struct {
s string
}
tests := []struct {
name string
args args
want bool
}{
{
name: "contains lower case",
args: args{s: "g0AJBHdnhRG2"},
want: true,
},
{
name: "does not contains lower case",
args: args{s: "V7T#MEA6@+TN"},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := ContainsLowercase(tt.args.s); got != tt.want {
t.Errorf("ContainsDigit() = %v, want %v", got, tt.want)
}
})
}
}
func TestContainsUppercase(t *testing.T) {
type args struct {
s string
}
tests := []struct {
name string
args args
want bool
}{
{
name: "contains upper case",
args: args{s: "G1sKkJeKlSQf"},
want: true,
},
{
name: "does not contains upper case",
args: args{s: "pq6-14ydz1@d"},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := ContainsUppercase(tt.args.s); got != tt.want {
t.Errorf("ContainsDigit() = %v, want %v", got, tt.want)
}
})
}
}
func TestContainsSpecialChar(t *testing.T) {
type args struct {
s string
}
tests := []struct {
name string
args args
want bool
}{
{
name: "contains upper case",
args: args{s: "HP$gE7s=do0B"},
want: true,
},
{
name: "does not contains upper case",
args: args{s: "w9gvBYctrSjB"},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := ContainsSpecialChar(tt.args.s); got != tt.want {
t.Errorf("ContainsDigit() = %v, want %v", got, tt.want)
}
})
}
}