fix(cache): encode non-ASCII characters in cache tags at construction#1143
Conversation
|
/bigbonk review |
commit: |
|
There was a problem hiding this comment.
Clean, well-scoped fix. Reviewed for correctness, completeness, and Next.js parity.
Encoding implementation (encode-cache-tag.ts): Faithful port of Next.js's packages/next/src/server/lib/encode-cache-tag.ts from vercel/next.js#93601. The two-regex pattern (non-g for the test fast-path, g for the replace) avoids the stale-lastIndex pitfall. Matching runs of out-of-class code units prevents URIError on surrogate pairs. Idempotence on %xx input is a natural consequence of % being in \x20-\x7e — good that the tests verify this explicitly.
Boundary coverage: Encoding is applied at every user-facing entry point:
revalidateTag,revalidatePath,updateTag— single-tag encoding beforehandler.revalidateTag()cacheTag()— batch encoding before pushing intoctx.tagsunstable_cache— batch encoding ofoptions.tagsat wrapper creation timefetch(..., { next: { tags } })— batch encoding increatePatchedFetchbuildPageCacheTags/buildAppPageCacheTags—tags.map(encodeCacheTag)on the output array
All downstream consumers (ISR cache, KV cache handler, dispatch, RSC handler, route handler, soft tags) only receive already-encoded tags and never introduce new unencoded user input. No missed boundaries.
Next.js parity: Next.js applies encoding at the same boundaries — getImplicitTags, validateTags, revalidatePath, revalidateTag, updateTag, and the patched fetch. The vinext equivalents line up correctly.
Tests: Good coverage — ASCII fast-path, multi-script (Hebrew/Arabic/Japanese), surrogate pairs, %xx round-trip, control characters, idempotence, and integration with both tag builders. The path-derived builder tests verify the critical invariant: tags stored at render time match what revalidatePath/revalidateTag produce.
Ship it.
|
Reviewed and approved PR #1143. The fix is a correct, complete port of Next.js's |
Tags with non-ASCII characters (Hebrew, Arabic, CJK, emoji) can crash ISR responses with ERR_INVALID_CHAR when written to x-next-cache-tags, and silently break invalidation when storage form diverges from the form revalidateTag/revalidatePath produce. Add encodeCacheTag/encodeCacheTags helper (matches behavior of vercel/next.js#93601) and apply it at every public boundary: cacheTag, revalidateTag, revalidatePath, updateTag, unstable_cache options.tags, fetch next.tags, and the path-derived _N_T_ tag builders. Closes #1138
4cb76c9 to
73e8b47
Compare
Summary
encodeCacheTag/encodeCacheTagshelper (mirrors vercel/next.js#93601) that percent-encodes any byte outside\t\x20-\x7e, fast-paths ASCII, handles surrogate pairs as whole code points, and is idempotent on%xxinput.x-next-cache-tags/ Cloudflare cache-tag header see the same canonical ASCII-safe form:cacheTag,revalidateTag,revalidatePath,updateTag,unstable_cacheoptions.tags,fetch(..., { next: { tags } }), and the path-derived_N_T_tag builders (buildPageCacheTags,buildAppPageCacheTags).tests/encode-cache-tag.test.tscovering ASCII fast-path, multi-script encoding (Hebrew/Arabic/Japanese), surrogate-pair safety,%xxround-trip preservation, control-char encoding, idempotence, and that the path-derived builders produce ASCII-only output for non-ASCII pathnames + extra tags.Closes #1138.
Test plan
pnpm test:unit tests/encode-cache-tag.test.ts— 11 cases passpnpm test:unit tests/page-cache-tags.test.ts tests/app-page-cache.test.ts tests/fetch-cache.test.ts— existing suites unaffected (121 pass)pnpm test:integration tests/kv-cache-handler.test.ts— 56 pass