Files
trufflehog/pkg/detectors/klaviyo/klaviyo_test.go
Martin LocklearandCursor 28f515fbb9 Restructure Klaviyo detector pattern tests (#5026)
Rewrite the single table-driven pattern test as one focused test per
case, and add the coverage the tightened regex needs.

* Legacy lowercase-hex and the newer prefixed company_id format each get
  their own positive test.
* New negatives: uppercase-hex body, a legacy body one char short, and a
  new-format hex tail one char long.
* Inline each test's input and assertions; drop the shared fixture block.

Co-authored-by: Cursor <[email protected]>
2026-06-09 14:55:58 -04:00

86 lines
3.0 KiB
Go

// Pattern tests for the Klaviyo detector.
package klaviyo
import (
"context"
"fmt"
"testing"
"github.com/stretchr/testify/suite"
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
)
// KlaviyoPatternSuite drives keyPat through the engine's real path (keyword
// prefilter, then FromData). One method per case, so a failure names it.
type KlaviyoPatternSuite struct {
suite.Suite
d Scanner
core *ahocorasick.Core
}
func TestKlaviyoPatternSuite(t *testing.T) {
suite.Run(t, new(KlaviyoPatternSuite))
}
// Fresh scanner and prefilter per method, so state can't leak between cases.
func (s *KlaviyoPatternSuite) SetupTest() {
s.d = Scanner{}
s.core = ahocorasick.NewAhoCorasickCore([]detectors.Detector{s.d})
}
// Legacy keys must keep matching despite the tighter lowercase-hex rule.
func (s *KlaviyoPatternSuite) TestLegacyHexFormat() {
key := "pk_a1b2c3d4e5f60718293a4b5c6d7e8f9012" // pk_ + 34 lowercase hex
input := fmt.Sprintf("klaviyo token = '%s'", key)
s.Require().NotEmpty(s.core.FindDetectorMatches([]byte(input)), "keyword prefilter should fire")
results, err := s.d.FromData(context.Background(), false, []byte(input))
s.Require().NoError(err)
s.Require().Len(results, 1)
s.Equal(key, string(results[0].Raw))
}
// The newer prefixed format. Mixed-case company_id is the bit a hex-only
// guess would miss.
func (s *KlaviyoPatternSuite) TestNewPrefixedFormat() {
key := "pk_AbCdEf_f0e1d2c3b4a5968778695a4b3c2d1e0f00" // pk_ + 6 alnum company_id + _ + 34 hex
input := fmt.Sprintf("klaviyo token = '%s'", key)
s.Require().NotEmpty(s.core.FindDetectorMatches([]byte(input)), "keyword prefilter should fire")
results, err := s.d.FromData(context.Background(), false, []byte(input))
s.Require().NoError(err)
s.Require().Len(results, 1)
s.Equal(key, string(results[0].Raw))
}
// Punctuation in the body should never read as a key.
func (s *KlaviyoPatternSuite) TestPunctuationBodyRejected() {
results, err := s.d.FromData(context.Background(), false, []byte("klaviyo = 'pk_1234567890abcdefghijklmnopqrstu-_='"))
s.Require().NoError(err)
s.Empty(results)
}
// Legacy bodies are lowercase hex (vendor-confirmed), so uppercase is a false
// positive.
func (s *KlaviyoPatternSuite) TestUppercaseHexRejected() {
results, err := s.d.FromData(context.Background(), false, []byte("klaviyo = 'pk_A1B2C3D4E5F60718293A4B5C6D7E8F9012'"))
s.Require().NoError(err)
s.Empty(results)
}
// Guards the legacy {34} length.
func (s *KlaviyoPatternSuite) TestLegacyTooShortRejected() {
results, err := s.d.FromData(context.Background(), false, []byte("klaviyo = 'pk_a1b2c3d4e5f60718293a4b5c6d7e8f901'")) // 33 hex
s.Require().NoError(err)
s.Empty(results)
}
// Guards the new-format {34} tail and trailing \b.
func (s *KlaviyoPatternSuite) TestNewFormatTailTooLongRejected() {
results, err := s.d.FromData(context.Background(), false, []byte("klaviyo = 'pk_AbCdEf_f0e1d2c3b4a5968778695a4b3c2d1e0f001'")) // 35 hex tail
s.Require().NoError(err)
s.Empty(results)
}