Files
trufflehog/pkg/detectors/dropbox/dropbox_test.go
lukem-tsandCursor 248ffd5bfb fix(dropbox): prevent long sl.u. tokens from being truncated before verification (#5012)
Newer scoped Dropbox short-lived tokens (sl.u.…) can be ~1.5KB. The scanning
engine only passes a keyword-centered window of the chunk (512 bytes by default)
to FromData, so these tokens were truncated before the regex saw them, producing
an invalid token that always verified as false.

Implement detectors.MaxSecretSizeProvider on the Dropbox scanner so the engine
widens its window to fit the full token. Add a regression test that drives a long
token through the Aho-Corasick windowing path.

Co-authored-by: Cursor <[email protected]>
2026-06-17 08:24:34 +10:00

131 lines
3.6 KiB
Go

package dropbox
import (
"context"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
)
var (
validPattern = `
# Configuration File: config.yaml
database:
host: $DB_HOST
port: $DB_PORT
username: $DB_USERNAME
password: $DB_PASS # IMPORTANT: Do not share this password publicly
api:
auth_type: "Bearer"
in: "Header"
api_version: v1
dropbox_secret: "sl.4ihqlizKRm9J8tJvdBUecLPfYunjh3Nx73cUBGcRKpTFxRny3cYKdaQdzVF_rBIEO9emJaHyRWeM_tm5pYJFTc1TwYjM2fHlhSdhKkzHJjf5dx86fUlaO_eKY9r4ijZ8eD"
base_url: "https://api.example.com/$api_version/example"
response_code: 200
# Notes:
# - Remember to rotate the secret every 90 days.
# - The above credentials should only be used in a secure environment.
`
secret = "sl.4ihqlizKRm9J8tJvdBUecLPfYunjh3Nx73cUBGcRKpTFxRny3cYKdaQdzVF_rBIEO9emJaHyRWeM_tm5pYJFTc1TwYjM2fHlhSdhKkzHJjf5dx86fUlaO_eKY9r4ijZ8eD"
)
func TestDropBox_Pattern(t *testing.T) {
d := Scanner{}
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
tests := []struct {
name string
input string
want []string
}{
{
name: "valid pattern",
input: validPattern,
want: []string{secret},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
if len(matchedDetectors) == 0 {
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
return
}
results, err := d.FromData(context.Background(), false, []byte(test.input))
if err != nil {
t.Errorf("error = %v", err)
return
}
if len(results) != len(test.want) {
if len(results) == 0 {
t.Errorf("did not receive result")
} else {
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
}
return
}
actual := make(map[string]struct{}, len(results))
for _, r := range results {
if len(r.RawV2) > 0 {
actual[string(r.RawV2)] = struct{}{}
} else {
actual[string(r.Raw)] = struct{}{}
}
}
expected := make(map[string]struct{}, len(test.want))
for _, v := range test.want {
expected[v] = struct{}{}
}
if diff := cmp.Diff(expected, actual); diff != "" {
t.Errorf("%s diff: (-want +got)\n%s", test.name, diff)
}
})
}
}
// TestDropBox_LongTokenThroughEngineWindow guards the MaxSecretSize override. The scanning
// engine only passes a keyword-centered window of the chunk to FromData (512 bytes by
// default). Newer scoped Dropbox tokens (sl.u.…) can be ~1.5KB, so without MaxSecretSize the
// token is truncated before the regex sees it and verification fails. This exercises the full
// Aho-Corasick windowing path rather than calling FromData on the whole input directly.
func TestDropBox_LongTokenThroughEngineWindow(t *testing.T) {
token := "sl.u." + strings.Repeat("aB3-_", 300) // 5 + 1500 = 1505 chars, single base64url run
chunk := []byte("DROPBOX_TOKEN=" + token + "\n")
d := Scanner{}
core := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
matches := core.FindDetectorMatches(chunk)
if len(matches) == 0 {
t.Fatal("no detector matches for long token")
}
var found bool
for _, m := range matches {
for _, data := range m.Matches() {
results, err := d.FromData(context.Background(), false, data)
if err != nil {
t.Fatal(err)
}
for _, r := range results {
if string(r.Raw) == token {
found = true
}
}
}
}
if !found {
t.Errorf("full %d-char token was not captured through the engine window (MaxSecretSize regression?)", len(token))
}
}