Skip to content

Commit a3599bb

Browse files
committed
Fix: Prevent mutation of Next.js cache objects in Redis handler
Redis handler mutated the original `cacheHandlerValue` object Next.js uses for responses. When `parseBuffersToStrings()` converted Buffers to base64, Next.js sent the mutated object to users. So we clone value object before mutation to isolate changes from Next.js's reference.
1 parent 06be1a0 commit a3599bb

File tree

1 file changed

+14
-4
lines changed

1 file changed

+14
-4
lines changed

packages/nextjs-cache-handler/src/handlers/redis-strings.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,13 @@ export default function createHandler({
228228
let expireOperation: Promise<number> | undefined;
229229
const lifespan = cacheHandlerValue.lifespan;
230230

231-
if (cacheHandlerValue?.value) {
232-
parseBuffersToStrings(cacheHandlerValue);
231+
// Clone only the value object to avoid mutating Next.js's original
232+
const valueForStorage = cacheHandlerValue.value
233+
? { ...cacheHandlerValue.value }
234+
: null;
235+
236+
if (valueForStorage) {
237+
parseBuffersToStrings({ ...cacheHandlerValue, value: valueForStorage });
233238
}
234239

235240
const setTagsOperation = client
@@ -248,13 +253,18 @@ export default function createHandler({
248253

249254
await Promise.all([setTagsOperation, setSharedTtlOperation]);
250255

256+
const serializedValue = JSON.stringify({
257+
...cacheHandlerValue,
258+
value: valueForStorage,
259+
});
260+
251261
switch (keyExpirationStrategy) {
252262
case "EXAT": {
253263
setOperation = client
254264
.withAbortSignal(AbortSignal.timeout(timeoutMs))
255265
.set(
256266
keyPrefix + key,
257-
JSON.stringify(cacheHandlerValue),
267+
serializedValue,
258268
typeof lifespan?.expireAt === "number"
259269
? {
260270
EXAT: lifespan.expireAt,
@@ -266,7 +276,7 @@ export default function createHandler({
266276
case "EXPIREAT": {
267277
setOperation = client
268278
.withAbortSignal(AbortSignal.timeout(timeoutMs))
269-
.set(keyPrefix + key, JSON.stringify(cacheHandlerValue));
279+
.set(keyPrefix + key, serializedValue);
270280

271281
expireOperation = lifespan
272282
? client

0 commit comments

Comments
 (0)