Skip to content

Commit 9e06e70

Browse files
committed
Disallow all zero values as entries in the cache
Not just literal nils.
1 parent 2b3c72d commit 9e06e70

File tree

2 files changed

+24
-8
lines changed

2 files changed

+24
-8
lines changed

cache/cache.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,10 @@ var (
3737
// negative caching is wanted.
3838
ErrDoesNotExist = errors.New("requested item does not exist")
3939

40-
// ErrNilValue is thrown if a client attempts to set a nil value in the cache.
41-
ErrNilValue = errors.New("nil values are not permitted")
40+
// ErrDisallowedCacheValue is thrown if a client attempts to set an entry in
41+
// the cache to the zero value of the cache type T. This is disallowed to
42+
// prevent accidentally poisoning the cache with invalid data.
43+
ErrDisallowedCacheValue = errors.New("nil and zero values are not permitted")
4244
)
4345

4446
type Fetcher[T any] func(ctx context.Context, key string) (T, error)
@@ -203,11 +205,10 @@ func (c *Cache[T]) fill(ctx context.Context, key string, fetcher Fetcher[T]) (va
203205
}
204206

205207
func (c *Cache[T]) set(ctx context.Context, key string, value T) error {
206-
// Even if T is a pointer type, we almost certainly don't want to accept nil
207-
// values into the cache.
208-
rv := reflect.ValueOf(value)
209-
if rv.Kind() == reflect.Ptr && rv.IsNil() {
210-
return ErrNilValue
208+
// We don't accept the zero value of T into the cache. This could easily be a
209+
// bug and we don't want to take the risk of poisoning the cache.
210+
if reflect.ValueOf(value).IsZero() {
211+
return ErrDisallowedCacheValue
211212
}
212213

213214
keys := c.keysFor(key)

cache/cache_test.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,5 +258,20 @@ func TestCacheSetNilForbidden(t *testing.T) {
258258
cache := NewCache[*testObj](client, "objects", fresh, stale)
259259

260260
err := cache.Set(ctx, "elephant", nil)
261-
assert.ErrorIs(t, err, ErrNilValue)
261+
assert.ErrorIs(t, err, ErrDisallowedCacheValue)
262+
}
263+
264+
func TestCacheSetZeroValueForbidden(t *testing.T) {
265+
ctx := context.Background()
266+
267+
fresh := 10 * time.Second
268+
stale := 30 * time.Second
269+
270+
client, _ := redismock.NewClientMock()
271+
cache := NewCache[testObj](client, "objects", fresh, stale)
272+
273+
var value testObj
274+
275+
err := cache.Set(ctx, "elephant", value)
276+
assert.ErrorIs(t, err, ErrDisallowedCacheValue)
262277
}

0 commit comments

Comments
 (0)