[SCAN-177] Purge secret parts from verification cache (#5318)
Lint / golangci-lint (push) Waiting to run
Lint / man-page-staleness (push) Waiting to run
Lint / semgrep (push) Waiting to run
Lint / checksecretparts (push) Waiting to run
Scan for secrets / test (push) Waiting to run
Test / test (push) Waiting to run
Test / test-community (push) Waiting to run
Release / mark-latest (push) Canceled after 0s
Release / Release (push) Canceled after 0s

This PR aims to zero out secret areas of a result before caching it.
This commit is contained in:
Casey Tran
2026-09-16 11:56:14 -05:00
committed by GitHub
parent 4ecb5c6edf
commit f714bf454f
4 changed files with 83 additions and 5 deletions
+11
View File
@@ -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
+31
View File
@@ -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())
}
+2 -4
View File
@@ -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)
}
@@ -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())
}