Lint / lint (push) Waiting to run
Release / Release (push) Waiting to run
Scan for secrets / test (push) Waiting to run
Snifftest / Run Snifftest (push) Waiting to run
Test / test (push) Waiting to run
Test / test-detectors (push) Waiting to run
Checking for false positives can lead to results being removed before ever getting the opportunity to verify them. Users are already responsible for verification of custom detectors, so let's not interfere with how they choose to use it.
216 lines
6.0 KiB
Go
216 lines
6.0 KiB
Go
package custom_detectors
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/custom_detectorspb"
|
|
"github.com/trufflesecurity/trufflehog/v3/pkg/protoyaml"
|
|
)
|
|
|
|
func TestCustomRegexTemplateParsing(t *testing.T) {
|
|
testCustomRegexTemplateYaml := `name: Internal bi tool
|
|
keywords:
|
|
- secret_v1_
|
|
- pat_v2_
|
|
regex:
|
|
id_pat_example: ([a-zA-Z0-9]{32})
|
|
secret_pat_example: ([a-zA-Z0-9]{32})
|
|
verify:
|
|
- endpoint: http://localhost:8000/{id_pat_example}
|
|
unsafe: true
|
|
headers:
|
|
- 'Authorization: Bearer {secret_pat_example.0}'
|
|
successRanges:
|
|
- 200-250
|
|
- '288'`
|
|
|
|
var got custom_detectorspb.CustomRegex
|
|
assert.NoError(t, protoyaml.UnmarshalStrict([]byte(testCustomRegexTemplateYaml), &got))
|
|
assert.Equal(t, "Internal bi tool", got.Name)
|
|
assert.Equal(t, []string{"secret_v1_", "pat_v2_"}, got.Keywords)
|
|
assert.Equal(t, map[string]string{
|
|
"id_pat_example": "([a-zA-Z0-9]{32})",
|
|
"secret_pat_example": "([a-zA-Z0-9]{32})",
|
|
}, got.Regex)
|
|
assert.Equal(t, 1, len(got.Verify))
|
|
assert.Equal(t, "http://localhost:8000/{id_pat_example}", got.Verify[0].Endpoint)
|
|
assert.Equal(t, true, got.Verify[0].Unsafe)
|
|
assert.Equal(t, []string{"Authorization: Bearer {secret_pat_example.0}"}, got.Verify[0].Headers)
|
|
assert.Equal(t, []string{"200-250", "288"}, got.Verify[0].SuccessRanges)
|
|
}
|
|
|
|
func TestCustomRegexWebhookParsing(t *testing.T) {
|
|
testCustomRegexWebhookYaml := `name: Internal bi tool
|
|
keywords:
|
|
- secret_v1_
|
|
- pat_v2_
|
|
regex:
|
|
id_pat_example: ([a-zA-Z0-9]{32})
|
|
secret_pat_example: ([a-zA-Z0-9]{32})
|
|
verify:
|
|
- endpoint: http://localhost:8000/
|
|
unsafe: true
|
|
headers:
|
|
- 'Authorization: Bearer token'`
|
|
|
|
var got custom_detectorspb.CustomRegex
|
|
assert.NoError(t, protoyaml.UnmarshalStrict([]byte(testCustomRegexWebhookYaml), &got))
|
|
assert.Equal(t, "Internal bi tool", got.Name)
|
|
assert.Equal(t, []string{"secret_v1_", "pat_v2_"}, got.Keywords)
|
|
assert.Equal(t, map[string]string{
|
|
"id_pat_example": "([a-zA-Z0-9]{32})",
|
|
"secret_pat_example": "([a-zA-Z0-9]{32})",
|
|
}, got.Regex)
|
|
assert.Equal(t, 1, len(got.Verify))
|
|
assert.Equal(t, "http://localhost:8000/", got.Verify[0].Endpoint)
|
|
assert.Equal(t, true, got.Verify[0].Unsafe)
|
|
assert.Equal(t, []string{"Authorization: Bearer token"}, got.Verify[0].Headers)
|
|
}
|
|
|
|
// TestCustomDetectorsParsing tests the full `detectors` configuration.
|
|
func TestCustomDetectorsParsing(t *testing.T) {
|
|
// TODO: Support both template and webhook.
|
|
testYamlConfig := `detectors:
|
|
- name: Internal bi tool
|
|
keywords:
|
|
- secret_v1_
|
|
- pat_v2_
|
|
regex:
|
|
id_pat_example: ([a-zA-Z0-9]{32})
|
|
secret_pat_example: ([a-zA-Z0-9]{32})
|
|
verify:
|
|
- endpoint: http://localhost:8000/
|
|
unsafe: true
|
|
headers:
|
|
- 'Authorization: Bearer token'`
|
|
|
|
var messages custom_detectorspb.CustomDetectors
|
|
assert.NoError(t, protoyaml.UnmarshalStrict([]byte(testYamlConfig), &messages))
|
|
assert.Equal(t, 1, len(messages.Detectors))
|
|
|
|
got := messages.Detectors[0]
|
|
assert.Equal(t, "Internal bi tool", got.Name)
|
|
assert.Equal(t, []string{"secret_v1_", "pat_v2_"}, got.Keywords)
|
|
assert.Equal(t, map[string]string{
|
|
"id_pat_example": "([a-zA-Z0-9]{32})",
|
|
"secret_pat_example": "([a-zA-Z0-9]{32})",
|
|
}, got.Regex)
|
|
assert.Equal(t, 1, len(got.Verify))
|
|
assert.Equal(t, "http://localhost:8000/", got.Verify[0].Endpoint)
|
|
assert.Equal(t, true, got.Verify[0].Unsafe)
|
|
assert.Equal(t, []string{"Authorization: Bearer token"}, got.Verify[0].Headers)
|
|
}
|
|
|
|
func TestFromData_InvalidRegEx(t *testing.T) {
|
|
c := &customRegexWebhook{
|
|
&custom_detectorspb.CustomRegex{
|
|
Name: "Internal bi tool",
|
|
Keywords: []string{"secret_v1_", "pat_v2_"},
|
|
Regex: map[string]string{
|
|
"test": "!!?(?:?)[a-zA-Z0-9]{32}", // invalid regex
|
|
},
|
|
},
|
|
}
|
|
|
|
_, err := c.FromData(context.Background(), false, []byte("test"))
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestProductIndices(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input []int
|
|
want [][]int
|
|
}{
|
|
{
|
|
name: "zero",
|
|
input: []int{3, 0},
|
|
want: nil,
|
|
},
|
|
{
|
|
name: "one input",
|
|
input: []int{3},
|
|
want: [][]int{{0}, {1}, {2}},
|
|
},
|
|
{
|
|
name: "two inputs",
|
|
input: []int{3, 2},
|
|
want: [][]int{
|
|
{0, 0}, {1, 0}, {2, 0},
|
|
{0, 1}, {1, 1}, {2, 1},
|
|
},
|
|
},
|
|
{
|
|
name: "three inputs",
|
|
input: []int{3, 2, 3},
|
|
want: [][]int{
|
|
{0, 0, 0}, {1, 0, 0}, {2, 0, 0},
|
|
{0, 1, 0}, {1, 1, 0}, {2, 1, 0},
|
|
{0, 0, 1}, {1, 0, 1}, {2, 0, 1},
|
|
{0, 1, 1}, {1, 1, 1}, {2, 1, 1},
|
|
{0, 0, 2}, {1, 0, 2}, {2, 0, 2},
|
|
{0, 1, 2}, {1, 1, 2}, {2, 1, 2},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := productIndices(tt.input...)
|
|
assert.Equal(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestProductIndicesMax(t *testing.T) {
|
|
got := productIndices(2, 3, 4, 5, 6)
|
|
assert.GreaterOrEqual(t, 2*3*4*5*6, maxTotalMatches)
|
|
assert.Equal(t, maxTotalMatches, len(got))
|
|
}
|
|
|
|
func TestPermutateMatches(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input map[string][][]string
|
|
want []map[string][]string
|
|
}{
|
|
{
|
|
name: "two matches",
|
|
input: map[string][][]string{"foo": {{"matchA"}, {"matchB"}}, "bar": {{"matchC"}}},
|
|
want: []map[string][]string{
|
|
{"foo": {"matchA"}, "bar": {"matchC"}},
|
|
{"foo": {"matchB"}, "bar": {"matchC"}},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := permutateMatches(tt.input)
|
|
assert.Equal(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDetector(t *testing.T) {
|
|
detector, err := NewWebhookCustomRegex(&custom_detectorspb.CustomRegex{
|
|
Name: "test",
|
|
// "password" is normally flagged as a false positive, but CustomRegex
|
|
// should allow the user to decide and report it as a result.
|
|
Keywords: []string{"password"},
|
|
Regex: map[string]string{"regex": "password=.*"},
|
|
})
|
|
assert.NoError(t, err)
|
|
results, err := detector.FromData(context.Background(), false, []byte(`password="123456"`))
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 1, len(results))
|
|
assert.Equal(t, results[0].Raw, []byte(`password="123456"`))
|
|
}
|
|
|
|
func BenchmarkProductIndices(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
_ = productIndices(3, 2, 6)
|
|
}
|
|
}
|