[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
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:
@@ -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
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user