diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b639aed9009..39e89832bf42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Improve canonicalizations for `tracking-*` utilities ([#19827](https://github.com/tailwindlabs/tailwindcss/pull/19827)) +- Fix crash due to invalid characters in candidate ([#19829](https://github.com/tailwindlabs/tailwindcss/pull/19829)) ## [4.2.2] - 2026-03-18 diff --git a/packages/tailwindcss/src/index.test.ts b/packages/tailwindcss/src/index.test.ts index 5eb7407d81dd..bf1c72c42178 100644 --- a/packages/tailwindcss/src/index.test.ts +++ b/packages/tailwindcss/src/index.test.ts @@ -1501,6 +1501,14 @@ describe('Parsing theme values from CSS', () => { `) }) + // https://github.com/tailwindlabs/tailwindcss/issues/19786 + test('out-of-range escaped CSS variable candidates do not crash the build', async () => { + // Shouldn't crash + await run([ + String.raw`--Coding-Projects-CharacterMapper-Master-Workspace\d8819554-4725-4235-9d22-2d0ed572e924`, + ]) + }) + test('`@keyframes` in `@theme` are hoisted', async () => { expect( await compileCss( diff --git a/packages/tailwindcss/src/utils/escape.test.ts b/packages/tailwindcss/src/utils/escape.test.ts index ff7715b9d8dc..f4c8e1192862 100644 --- a/packages/tailwindcss/src/utils/escape.test.ts +++ b/packages/tailwindcss/src/utils/escape.test.ts @@ -11,4 +11,14 @@ describe('unescape', () => { test('removes backslashes', () => { expect(unescape(String.raw`red-1\/2`)).toMatchInlineSnapshot(`"red-1/2"`) }) + + test('replaces out-of-range escaped code points', () => { + expect( + unescape( + String.raw`--Coding-Projects-CharacterMapper-Master-Workspace\d8819554-4725-4235-9d22-2d0ed572e924`, + ), + ).toMatchInlineSnapshot( + `"--Coding-Projects-CharacterMapper-Master-Workspace�54-4725-4235-9d22-2d0ed572e924"`, + ) + }) }) diff --git a/packages/tailwindcss/src/utils/escape.ts b/packages/tailwindcss/src/utils/escape.ts index cbcfa62a0b42..c430e4ad42bf 100644 --- a/packages/tailwindcss/src/utils/escape.ts +++ b/packages/tailwindcss/src/utils/escape.ts @@ -74,8 +74,24 @@ export function escape(value: string) { export function unescape(escaped: string) { return escaped.replace(/\\([\dA-Fa-f]{1,6}[\t\n\f\r ]?|[\S\s])/g, (match) => { - return match.length > 2 - ? String.fromCodePoint(Number.parseInt(match.slice(1).trim(), 16)) - : match[1] + if (match.length <= 2) { + return match[1] + } + + let codePoint = Number.parseInt(match.slice(1).trim(), 16) + + if ( + // Invalid codepoint: https://infra.spec.whatwg.org/#code-point + codePoint === 0x0000 || + codePoint > 0x10ffff || + // Is surrogate: https://infra.spec.whatwg.org/#leading-surrogate + // - A leading surrogate is a code point that is in the range U+D800 to U+DBFF, inclusive. + // - A trailing surrogate is a code point that is in the range U+DC00 to U+DFFF, inclusive. + (codePoint >= 0xd800 && codePoint <= 0xdfff) + ) { + return '\uFFFD' + } + + return String.fromCodePoint(codePoint) }) }