diff --git a/pkg/detectors/detectors.go b/pkg/detectors/detectors.go index 939ffae5b..dbcec64f2 100644 --- a/pkg/detectors/detectors.go +++ b/pkg/detectors/detectors.go @@ -181,6 +181,17 @@ func (r *Result) GetPrimarySecretValue() string { return r.primarySecret.Value } +// ClearSecrets removes fields that may contain raw secret material. +func (r *Result) ClearSecrets() { + r.Raw = nil + r.RawV2 = nil + r.SecretParts = nil + r.primarySecret = struct { + Value string + Line int64 + }{} +} + // SetChunkOffset records the byte position of this result's secret within the chunk data. func (r *Result) SetChunkOffset(offset int64) { r.chunkOffset = offset diff --git a/pkg/detectors/result_test.go b/pkg/detectors/result_test.go new file mode 100644 index 000000000..95baebef9 --- /dev/null +++ b/pkg/detectors/result_test.go @@ -0,0 +1,31 @@ +package detectors + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestClearSecrets(t *testing.T) { + result := Result{ + Raw: []byte("raw-secret"), + RawV2: []byte("raw-secret-v2"), + Redacted: "redacted", + SecretParts: map[string]string{"key": "raw-secret"}, + ExtraData: map[string]string{"account": "123"}, + } + result.SetPrimarySecretValue("secret-value") + result.SetPrimarySecretLine(12) + result.SetVerificationError(assert.AnError) + + result.ClearSecrets() + + assert.Nil(t, result.Raw) + assert.Nil(t, result.RawV2) + assert.Nil(t, result.SecretParts) + assert.Empty(t, result.GetPrimarySecretValue()) + assert.Zero(t, result.primarySecret.Line) + assert.Equal(t, "redacted", result.Redacted) + assert.Equal(t, map[string]string{"account": "123"}, result.ExtraData) + assert.EqualError(t, result.VerificationError(), assert.AnError.Error()) +} diff --git a/pkg/verificationcache/verification_cache.go b/pkg/verificationcache/verification_cache.go index cdccdd7ec..494c7e64b 100644 --- a/pkg/verificationcache/verification_cache.go +++ b/pkg/verificationcache/verification_cache.go @@ -133,8 +133,7 @@ func (v *VerificationCache) FromData( copyForCaching := r // Do not persist raw secret values in a long-lived cache - copyForCaching.Raw = nil - copyForCaching.RawV2 = nil + copyForCaching.ClearSecrets() v.resultCache.Set(string(cacheKey), copyForCaching) } @@ -183,8 +182,7 @@ func (v *VerificationCache) verifyCacheMisses( verifyResult(i) copyForCaching := results[i] // Do not persist raw secret values in a long-lived cache - copyForCaching.Raw = nil - copyForCaching.RawV2 = nil + copyForCaching.ClearSecrets() v.resultCache.Set(string(cacheKey), copyForCaching) } diff --git a/pkg/verificationcache/verification_cache_test.go b/pkg/verificationcache/verification_cache_test.go index 8472b140e..81ea780c6 100644 --- a/pkg/verificationcache/verification_cache_test.go +++ b/pkg/verificationcache/verification_cache_test.go @@ -23,7 +23,16 @@ func (t *testDetector) FromData(_ context.Context, verify bool, _ []byte) ([]det t.fromDataCallCount = t.fromDataCallCount + 1 var results []detectors.Result for _, r := range t.results { - copy := detectors.Result{Redacted: r.Redacted, Raw: r.Raw, RawV2: r.RawV2, DetectorType: r.DetectorType} + copy := detectors.Result{ + Redacted: r.Redacted, + Raw: r.Raw, + RawV2: r.RawV2, + DetectorType: r.DetectorType, + SecretParts: r.SecretParts, + } + if v := r.GetPrimarySecretValue(); v != "" { + copy.SetPrimarySecretValue(v) + } if verify { copy.CopyVerificationInfo(&r) } @@ -535,3 +544,32 @@ func TestVerificationCache_FromData_ResultVerifier_ForceCacheUpdate(t *testing.T assert.Equal(t, int32(0), metrics.ResultCacheMisses.Load()) assert.Equal(t, int32(0), metrics.ResultCacheHitsWasted.Load()) } + +func TestVerificationCache_FromData_DoesNotCacheSecretMaterial(t *testing.T) { + result := detectors.Result{ + Redacted: "hello", + Raw: []byte("hello"), + RawV2: []byte("helloV2"), + Verified: true, + SecretParts: map[string]string{"key": "hello"}, + } + result.SetPrimarySecretValue("hello") + detector := testDetector{results: []detectors.Result{result}} + cache := New(simple.NewCache[detectors.Result](), nil) + + results, err := cache.FromData(logContext.Background(), &detector, true, false, nil) + require.NoError(t, err) + + require.Len(t, results, 1) + assert.Equal(t, []byte("hello"), results[0].Raw) + assert.Equal(t, []byte("helloV2"), results[0].RawV2) + assert.Equal(t, map[string]string{"key": "hello"}, results[0].SecretParts) + assert.Equal(t, "hello", results[0].GetPrimarySecretValue()) + + cached := cache.resultCache.Values() + require.Len(t, cached, 1) + assert.Nil(t, cached[0].Raw) + assert.Nil(t, cached[0].RawV2) + assert.Nil(t, cached[0].SecretParts) + assert.Empty(t, cached[0].GetPrimarySecretValue()) +}