From 248ffd5bfb35dbe043817020a70369bec134e6f2 Mon Sep 17 00:00:00 2001 From: lukem-ts Date: Wed, 17 Jun 2026 08:24:34 +1000 Subject: [PATCH] fix(dropbox): prevent long sl.u. tokens from being truncated before verification (#5012) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- pkg/detectors/dropbox/dropbox.go | 6 +++++ pkg/detectors/dropbox/dropbox_test.go | 36 +++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/pkg/detectors/dropbox/dropbox.go b/pkg/detectors/dropbox/dropbox.go index 3f3c5055f..503777b18 100644 --- a/pkg/detectors/dropbox/dropbox.go +++ b/pkg/detectors/dropbox/dropbox.go @@ -20,6 +20,12 @@ type Scanner struct { // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) +var _ detectors.MaxSecretSizeProvider = (*Scanner)(nil) + +// MaxSecretSize overrides the engine's default keyword window (512 bytes) so the full +// token is passed to FromData. Newer scoped Dropbox short-lived tokens (sl.u.…) can be +// ~1.5KB; without this the engine truncates the chunk window and verification fails. +func (s Scanner) MaxSecretSize() int64 { return 4096 } var ( defaultClient = common.SaneHttpClient() diff --git a/pkg/detectors/dropbox/dropbox_test.go b/pkg/detectors/dropbox/dropbox_test.go index 990284e3c..6a584d1e6 100644 --- a/pkg/detectors/dropbox/dropbox_test.go +++ b/pkg/detectors/dropbox/dropbox_test.go @@ -2,6 +2,7 @@ package dropbox import ( "context" + "strings" "testing" "github.com/google/go-cmp/cmp" @@ -92,3 +93,38 @@ func TestDropBox_Pattern(t *testing.T) { }) } } + +// 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)) + } +}