From 19c9236bde3076ea546085c56c22abf92f47f613 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 5 Nov 2024 20:21:24 +0400 Subject: [PATCH] fix: make inmem bookmarks random for each run As inmem storage is not persisted, make sure watch bookmark from one run doesn't work with another run. This still allows watches to be restarted on connection failures, but if the watch is restarted on a program restart, bookmark won't match anymore. Signed-off-by: Andrey Smirnov --- pkg/state/impl/inmem/collection.go | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/pkg/state/impl/inmem/collection.go b/pkg/state/impl/inmem/collection.go index 986045a..c8c0847 100644 --- a/pkg/state/impl/inmem/collection.go +++ b/pkg/state/impl/inmem/collection.go @@ -6,8 +6,10 @@ package inmem import ( "context" + "crypto/rand" "encoding/binary" "fmt" + "io" "slices" "sort" "sync" @@ -265,16 +267,34 @@ func (collection *ResourceCollection) Destroy(ctx context.Context, ptr resource. return nil } +// bookmarkCookie is a random cookie used to encode bookmarks. +// +// As the state is in-memory, we need to distinguish between bookmarks from different runs of the program. +var bookmarkCookie = sync.OnceValue(func() []byte { + cookie := make([]byte, 8) + + _, err := io.ReadFull(rand.Reader, cookie) + if err != nil { + panic(err) + } + + return cookie +}) + func encodeBookmark(pos int64) state.Bookmark { - return binary.BigEndian.AppendUint64(nil, uint64(pos)) + return binary.BigEndian.AppendUint64(slices.Clone(bookmarkCookie()), uint64(pos)) } func decodeBookmark(bookmark state.Bookmark) (int64, error) { - if len(bookmark) != 8 { + if len(bookmark) != 16 { return 0, fmt.Errorf("invalid bookmark length: %d", len(bookmark)) } - return int64(binary.BigEndian.Uint64(bookmark)), nil + if !slices.Equal(bookmark[:8], bookmarkCookie()) { + return 0, fmt.Errorf("invalid bookmark cookie") + } + + return int64(binary.BigEndian.Uint64(bookmark[8:])), nil } // Watch for specific resource changes.