Skip to content

Commit b0c8d18

Browse files
committed
ensure we validate balancing [], () and {}
1 parent f826811 commit b0c8d18

File tree

3 files changed

+94
-15
lines changed

3 files changed

+94
-15
lines changed

src/jit/lib/setupContextUtils.js

+44-14
Original file line numberDiff line numberDiff line change
@@ -125,31 +125,61 @@ function withIdentifiers(styles) {
125125
})
126126
}
127127

128+
let matchingBrackets = new Map([
129+
['{', '}'],
130+
['[', ']'],
131+
['(', ')'],
132+
])
133+
let inverseMatchingBrackets = new Map(
134+
Array.from(matchingBrackets.entries()).map(([k, v]) => [v, k])
135+
)
136+
137+
let quotes = new Set(['"', "'", '`'])
138+
139+
// Arbitrary values must contain balanced brackets (), [] and {}. Escaped
140+
// values don't count, and brackets inside quotes also don't count.
141+
//
142+
// E.g.: w-[this-is]w-[weird-and-invalid]
143+
// E.g.: w-[this-is\\]w-\\[weird-but-valid]
144+
// E.g.: content-['this-is-also-valid]-weirdly-enough']
128145
function isValidArbitraryValue(value) {
129-
if (value === undefined) {
130-
return true
131-
}
132-
133-
// When an arbitrary value contans `[]` then it is probably invalid, unless
134-
// it is properly escaped. Or it is content that exists between quotes.
135-
// E.g.: w-[this-is]w-[weird-and-invalid]
136-
// E.g.: w-[this-is\\]w-\\[weird-but-valid]
137-
// E.g.: content-['this-is-also-valid]-weirdly-enough']
146+
let stack = []
138147
let inQuotes = false
148+
139149
for (let i = 0; i < value.length; i++) {
140-
if (['"', "'", '`'].includes(value[i])) {
150+
let char = value[i]
151+
152+
// Non-escaped quotes allow us to "allow" anything in between
153+
if (quotes.has(char) && value[i - 1] !== '\\') {
141154
inQuotes = !inQuotes
142155
}
143156

144157
if (inQuotes) continue
158+
if (value[i - 1] === '\\') continue // Escaped
159+
160+
if (matchingBrackets.has(char)) {
161+
stack.push(char)
162+
} else if (inverseMatchingBrackets.has(char)) {
163+
let inverse = inverseMatchingBrackets.get(char)
145164

146-
if (value[i] === '[' || value[i] === ']') {
147-
if (value[i - 1] !== '\\') {
165+
// Nothing to pop from, therefore it is unbalanced
166+
if (stack.length <= 0) {
167+
return false
168+
}
169+
170+
// Popped value must match the inverse value, otherwise it is unbalanced
171+
if (stack.pop() !== inverse) {
148172
return false
149173
}
150174
}
151175
}
152176

177+
// If there is still something on the stack, it is also unbalanced
178+
if (stack.length > 0) {
179+
return false
180+
}
181+
182+
// All good, totally balanced!
153183
return true
154184
}
155185

@@ -297,11 +327,11 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
297327
type = [].concat(type)
298328
let [value, coercedType] = coerceValue(type, modifier, options.values, tailwindConfig)
299329

300-
if (!isValidArbitraryValue(value)) {
330+
if (!type.includes(coercedType) || value === undefined) {
301331
return []
302332
}
303333

304-
if (!type.includes(coercedType) || value === undefined) {
334+
if (!isValidArbitraryValue(value)) {
305335
return []
306336
}
307337

tests/jit/arbitrary-values.test.css

+22
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,28 @@
9393
.w-\[calc\(100\%\/3-1rem\*2\)\] {
9494
width: calc(100% / 3 - 1rem * 2);
9595
}
96+
.w-\[\{\}\] {
97+
width: {
98+
}
99+
}
100+
.w-\[\{\{\}\}\] {
101+
width: {
102+
{
103+
}
104+
}
105+
}
106+
.w-\[\(\)\] {
107+
width: ();
108+
}
109+
.w-\[\(\(\)\)\] {
110+
width: (());
111+
}
112+
.w-\[\'\)\(\)\'\] {
113+
width: ')()';
114+
}
115+
.w-\[\'\}\{\}\'\] {
116+
width: '}{}';
117+
}
96118
.min-w-\[3\.23rem\] {
97119
min-width: 3.23rem;
98120
}

tests/jit/arbitrary-values.test.html

+28-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
<div class="w-[var(--width)]"></div>
2222
<div class="w-[var(--width,calc(100%+1rem))]"></div>
2323
<div class="w-[calc(100%/3-1rem*2)]"></div>
24-
<div class="w-[do-not-generate-this]w-[it-is-invalid-syntax]"></div>
2524
<div class="min-w-[3.23rem]"></div>
2625
<div class="min-w-[calc(100%+1rem)]"></div>
2726
<div class="min-w-[var(--width)]"></div>
@@ -117,5 +116,33 @@
117116
<div class="delay-[var(--delay)]"></div>
118117
<div class="content-['hello']"></div>
119118
<div class="content-[attr(content-before)]"></div>
119+
120+
<!-- Balancing issues, this is not checking the validity of the actual value, but purely syntax-wise -->
121+
<div class="w-[do-not-generate-this]w-[it-is-invalid-syntax]"></div><!-- INVALID -->
122+
<div class="grid-cols-[[linename],1fr,auto]"></div><!-- VALID -->
123+
<div class="w-[{}]"></div><!-- VALID -->
124+
<div class="w-[{{}}]"></div><!-- VALID -->
125+
<div class="w-[[]]"></div><!-- VALID -->
126+
<div class="w-[[[]]]"></div><!-- VALID -->
127+
<div class="w-[()]"></div><!-- VALID -->
128+
<div class="w-[(())]"></div><!-- VALID -->
129+
<div class="w-[][]"></div><!-- INVALID -->
130+
<div class="w-[)(]"></div><!-- INVALID -->
131+
<div class="w-[}{]"></div><!-- INVALID -->
132+
<div class="w-[][]]"></div><!-- INVALID -->
133+
<div class="w-[)()]"></div><!-- INVALID -->
134+
<div class="w-[}{}]"></div><!-- INVALID -->
135+
<div class="w-['][]']"></div><!-- VALID -->
136+
<div class="w-[')()']"></div><!-- VALID -->
137+
<div class="w-['}{}']"></div><!-- VALID -->
138+
<div class="w-[[']']]"></div><!-- VALID -->
139+
<div class="w-[(')')]"></div><!-- VALID -->
140+
<div class="w-[{'}'}]"></div><!-- VALID -->
141+
<div class="w-[{[}]]"></div><!-- INVALID -->
142+
<div class="w-[[{]}]"></div><!-- INVALID -->
143+
<div class="w-[{(})]"></div><!-- INVALID -->
144+
<div class="w-[({)}]"></div><!-- INVALID -->
145+
<div class="w-[([)]]"></div><!-- INVALID -->
146+
<div class="w-[[(])]"></div><!-- INVALID -->
120147
</body>
121148
</html>

0 commit comments

Comments
 (0)