From 7a5b6abd4f772a91c34202129f2852e82e3ea5a8 Mon Sep 17 00:00:00 2001 From: Philipp Spiess Date: Tue, 11 Mar 2025 15:53:16 +0100 Subject: [PATCH 1/2] Don't extract links as arbitrary properties --- CHANGELOG.md | 1 + .../oxide/src/extractor/arbitrary_property_machine.rs | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23469db88876..a4a9559af9e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Treat starting single quote as verbatim text in Slim ([#17085](https://github.com/tailwindlabs/tailwindcss/pull/17085)) - Ensure `.node` and `.wasm` files are not scanned for utilities ([#17123](https://github.com/tailwindlabs/tailwindcss/pull/17123)) - Improve performance when scanning `JSON` files ([#17125](https://github.com/tailwindlabs/tailwindcss/pull/17125)) +- Don't create invalid CSS when encountering a link wrapped in square brackets ([#17129](https://github.com/tailwindlabs/tailwindcss/pull/17129)) ## [4.0.12] - 2025-03-07 diff --git a/crates/oxide/src/extractor/arbitrary_property_machine.rs b/crates/oxide/src/extractor/arbitrary_property_machine.rs index c1460092fca2..1b479a440a18 100644 --- a/crates/oxide/src/extractor/arbitrary_property_machine.rs +++ b/crates/oxide/src/extractor/arbitrary_property_machine.rs @@ -163,6 +163,7 @@ impl Machine for ArbitraryPropertyMachine { #[inline] fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState { let len = cursor.input.len(); + let start_of_value = cursor.pos; while cursor.pos < len { match cursor.curr.into() { Class::Escape => match cursor.next.into() { @@ -222,6 +223,9 @@ impl Machine for ArbitraryPropertyMachine { // Any kind of whitespace is not allowed Class::Whitespace => return self.restart(), + // URLs are not allowed + Class::Slash if start_of_value == cursor.pos => return self.restart(), + // Everything else is valid _ => cursor.advance(), }; @@ -278,6 +282,9 @@ enum Class { #[bytes(b':')] Colon, + #[bytes(b'/')] + Slash, + #[bytes(b' ', b'\t', b'\n', b'\r', b'\x0C')] Whitespace, @@ -341,6 +348,9 @@ mod tests { ("[:red]", vec![]), // Empty brackets are not allowed ("[]", vec![]), + // URLs + ("[http://example.com]", vec![]), + ("[https://example.com]", vec![]), // Missing colon in more complex example (r#"[CssClass("gap-y-4")]"#, vec![]), // Brackets must be balanced From a1aa79a32b25be28d79097feb58f3898b41dbf23 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Tue, 11 Mar 2025 17:37:36 +0100 Subject: [PATCH 2/2] Apply suggestions from code review --- crates/oxide/src/extractor/arbitrary_property_machine.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/oxide/src/extractor/arbitrary_property_machine.rs b/crates/oxide/src/extractor/arbitrary_property_machine.rs index 1b479a440a18..947073c2fb99 100644 --- a/crates/oxide/src/extractor/arbitrary_property_machine.rs +++ b/crates/oxide/src/extractor/arbitrary_property_machine.rs @@ -163,7 +163,7 @@ impl Machine for ArbitraryPropertyMachine { #[inline] fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState { let len = cursor.input.len(); - let start_of_value = cursor.pos; + let start_of_value_pos = cursor.pos; while cursor.pos < len { match cursor.curr.into() { Class::Escape => match cursor.next.into() { @@ -224,7 +224,7 @@ impl Machine for ArbitraryPropertyMachine { Class::Whitespace => return self.restart(), // URLs are not allowed - Class::Slash if start_of_value == cursor.pos => return self.restart(), + Class::Slash if start_of_value_pos == cursor.pos => return self.restart(), // Everything else is valid _ => cursor.advance(),