Skip to content

Commit

Permalink
ensure we validate balancing [], () and {}
Browse files Browse the repository at this point in the history
  • Loading branch information
RobinMalfait committed Aug 25, 2021
1 parent f826811 commit 6bae085
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 14 deletions.
56 changes: 43 additions & 13 deletions src/jit/lib/setupContextUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,31 +125,61 @@ function withIdentifiers(styles) {
})
}

let matchingBrackets = new Map([
['{', '}'],
['[', ']'],
['(', ')'],
])
let inverseMatchingBrackets = new Map(
Array.from(matchingBrackets.entries()).map(([k, v]) => [v, k])
)

let quotes = new Set(['"', "'", '`'])

// Arbitrary values must contain balanced brackets (), [] and {}. Escaped
// values don't count, and brackets inside quotes also don't count.
//
// E.g.: w-[this-is]w-[weird-and-invalid]
// E.g.: w-[this-is\\]w-\\[weird-but-valid]
// E.g.: content-['this-is-also-valid]-weirdly-enough']
function isValidArbitraryValue(value) {
if (value === undefined) {
return true
}
let stack = []

// When an arbitrary value contans `[]` then it is probably invalid, unless
// it is properly escaped. Or it is content that exists between quotes.
// E.g.: w-[this-is]w-[weird-and-invalid]
// E.g.: w-[this-is\\]w-\\[weird-but-valid]
// E.g.: content-['this-is-also-valid]-weirdly-enough']
let inQuotes = false
for (let i = 0; i < value.length; i++) {
if (['"', "'", '`'].includes(value[i])) {
let char = value[i]

// Non-escaped quotes allow us to "allow" anything in between
if (quotes.has(char) && value[i - 1] !== '\\') {
inQuotes = !inQuotes
}

if (inQuotes) continue
if (value[i - 1] === '\\') continue // Escaped

if (matchingBrackets.has(char)) {
stack.push(char)
} else if (inverseMatchingBrackets.has(char)) {
let inverse = inverseMatchingBrackets.get(char)

// Nothing to pop from, therefore it is unbalanced
if (stack.length <= 0) {
return false
}

if (value[i] === '[' || value[i] === ']') {
if (value[i - 1] !== '\\') {
// Popped value must match the inverse value, otherwise it is unbalanced
if (stack.pop() !== inverse) {
return false
}
}
}

// If there is still something on the stack, it is also unbalanced
if (stack.length > 0) {
return false
}

// All good, totally balanced!
return true
}

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

if (!isValidArbitraryValue(value)) {
if (!type.includes(coercedType) || value === undefined) {
return []
}

if (!type.includes(coercedType) || value === undefined) {
if (!isValidArbitraryValue(value)) {
return []
}

Expand Down
22 changes: 22 additions & 0 deletions tests/jit/arbitrary-values.test.css
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,28 @@
.w-\[calc\(100\%\/3-1rem\*2\)\] {
width: calc(100% / 3 - 1rem * 2);
}
.w-\[\{\}\] {
width: {
}
}
.w-\[\{\{\}\}\] {
width: {
{
}
}
}
.w-\[\(\)\] {
width: ();
}
.w-\[\(\(\)\)\] {
width: (());
}
.w-\[\'\)\(\)\'\] {
width: ')()';
}
.w-\[\'\}\{\}\'\] {
width: '}{}';
}
.min-w-\[3\.23rem\] {
min-width: 3.23rem;
}
Expand Down
29 changes: 28 additions & 1 deletion tests/jit/arbitrary-values.test.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
<div class="w-[var(--width)]"></div>
<div class="w-[var(--width,calc(100%+1rem))]"></div>
<div class="w-[calc(100%/3-1rem*2)]"></div>
<div class="w-[do-not-generate-this]w-[it-is-invalid-syntax]"></div>
<div class="min-w-[3.23rem]"></div>
<div class="min-w-[calc(100%+1rem)]"></div>
<div class="min-w-[var(--width)]"></div>
Expand Down Expand Up @@ -117,5 +116,33 @@
<div class="delay-[var(--delay)]"></div>
<div class="content-['hello']"></div>
<div class="content-[attr(content-before)]"></div>

<!-- Balancing issues, this is not checking the validity of the actual value, but purely syntax-wise -->
<div class="w-[do-not-generate-this]w-[it-is-invalid-syntax]"></div><!-- INVALID -->
<div class="grid-cols-[[linename],1fr,auto]"></div><!-- VALID -->
<div class="w-[{}]"></div><!-- VALID -->
<div class="w-[{{}}]"></div><!-- VALID -->
<div class="w-[[]]"></div><!-- VALID -->
<div class="w-[[[]]]"></div><!-- VALID -->
<div class="w-[()]"></div><!-- VALID -->
<div class="w-[(())]"></div><!-- VALID -->
<div class="w-[][]"></div><!-- INVALID -->
<div class="w-[)(]"></div><!-- INVALID -->
<div class="w-[}{]"></div><!-- INVALID -->
<div class="w-[][]]"></div><!-- INVALID -->
<div class="w-[)()]"></div><!-- INVALID -->
<div class="w-[}{}]"></div><!-- INVALID -->
<div class="w-['][]']"></div><!-- VALID -->
<div class="w-[')()']"></div><!-- VALID -->
<div class="w-['}{}']"></div><!-- VALID -->
<div class="w-[[']']]"></div><!-- VALID -->
<div class="w-[(')')]"></div><!-- VALID -->
<div class="w-[{'}'}]"></div><!-- VALID -->
<div class="w-[{[}]]"></div><!-- INVALID -->
<div class="w-[[{]}]"></div><!-- INVALID -->
<div class="w-[{(})]"></div><!-- INVALID -->
<div class="w-[({)}]"></div><!-- INVALID -->
<div class="w-[([)]]"></div><!-- INVALID -->
<div class="w-[[(])]"></div><!-- INVALID -->
</body>
</html>

0 comments on commit 6bae085

Please sign in to comment.