Skip to content

Commit 1d90dea

Browse files
holblinJean-Philippe Zolesio
and
Jean-Philippe Zolesio
authored
Release 4.3.2 (#249)
Co-authored-by: Jean-Philippe Zolesio <[email protected]>
1 parent 0c9695a commit 1d90dea

File tree

8 files changed

+616
-514
lines changed

8 files changed

+616
-514
lines changed

History.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
4.3.2 / 2023-11-28
2+
==================
3+
4+
* Fix redos vulnerability with specific crafted css string - CVE-2023-48631
5+
* Fix Problem parsing with :is() and nested :nth-child() #211
6+
7+
18
4.3.1 / 2023-03-14
29
==================
310

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@adobe/css-tools",
3-
"version": "4.3.1",
3+
"version": "4.3.2",
44
"description": "CSS parser / stringifier",
55
"source": "src/index.ts",
66
"main": "./dist/index.cjs",
@@ -16,8 +16,8 @@
1616
"Readme.md"
1717
],
1818
"devDependencies": {
19-
"@parcel/packager-ts": "2.9.3",
20-
"@parcel/transformer-typescript-types": "2.9.3",
19+
"@parcel/packager-ts": "2.10.3",
20+
"@parcel/transformer-typescript-types": "2.10.3",
2121
"@types/benchmark": "^2.1.1",
2222
"@types/bytes": "^3.1.1",
2323
"@types/jest": "^29.5.3",
@@ -26,7 +26,7 @@
2626
"bytes": "^3.1.0",
2727
"gts": "^5.0.0",
2828
"jest": "^29.6.2",
29-
"parcel": "^2.9.3",
29+
"parcel": "^2.10.3",
3030
"ts-jest": "^29.1.1",
3131
"typescript": "^5.0.2"
3232
},

src/parse/index.ts

Lines changed: 70 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,35 @@ export const parse = (
197197
});
198198
}
199199

200+
function findClosingParenthese(
201+
str: string,
202+
start: number,
203+
depth: number
204+
): number {
205+
let ptr = start + 1;
206+
let found = false;
207+
let closeParentheses = str.indexOf(')', ptr);
208+
while (!found && closeParentheses !== -1) {
209+
const nextParentheses = str.indexOf('(', ptr);
210+
if (nextParentheses !== -1 && nextParentheses < closeParentheses) {
211+
const nextSearch = findClosingParenthese(
212+
str,
213+
nextParentheses + 1,
214+
depth + 1
215+
);
216+
ptr = nextSearch + 1;
217+
closeParentheses = str.indexOf(')', ptr);
218+
} else {
219+
found = true;
220+
}
221+
}
222+
if (found && closeParentheses !== -1) {
223+
return closeParentheses;
224+
} else {
225+
return -1;
226+
}
227+
}
228+
200229
/**
201230
* Parse selector.
202231
*/
@@ -207,35 +236,54 @@ export const parse = (
207236
}
208237

209238
// remove comment in selector;
210-
const res = trim(m[0]).replace(commentre, '');
239+
let res = trim(m[0]).replace(commentre, '');
211240

212241
// Optimisation: If there is no ',' no need to split or post-process (this is less costly)
213242
if (res.indexOf(',') === -1) {
214243
return [res];
215244
}
216245

246+
// Replace all the , in the parentheses by \u200C
247+
let ptr = 0;
248+
let startParentheses = res.indexOf('(', ptr);
249+
while (startParentheses !== -1) {
250+
const closeParentheses = findClosingParenthese(res, startParentheses, 0);
251+
if (closeParentheses === -1) {
252+
break;
253+
}
254+
ptr = closeParentheses + 1;
255+
res =
256+
res.substring(0, startParentheses) +
257+
res
258+
.substring(startParentheses, closeParentheses)
259+
.replace(/,/g, '\u200C') +
260+
res.substring(closeParentheses);
261+
startParentheses = res.indexOf('(', ptr);
262+
}
263+
264+
// Replace all the , in ' and " by \u200C
265+
res = res
266+
/**
267+
* replace ',' by \u200C for data selector (div[data-lang="fr,de,us"])
268+
*
269+
* Examples:
270+
* div[data-lang="fr,\"de,us"]
271+
* div[data-lang='fr,\'de,us']
272+
*
273+
* Regex logic:
274+
* ("|')(?:\\\1|.)*?\1 => Handle the " and '
275+
*
276+
* Optimization 1:
277+
* No greedy capture (see docs about the difference between .* and .*?)
278+
*
279+
* Optimization 2:
280+
* ("|')(?:\\\1|.)*?\1 this use reference to capture group, it work faster.
281+
*/
282+
.replace(/("|')(?:\\\1|.)*?\1/g, m => m.replace(/,/g, '\u200C'));
283+
284+
// Split all the left , and replace all the \u200C by ,
217285
return (
218286
res
219-
/**
220-
* replace ',' by \u200C for data selector (div[data-lang="fr,de,us"])
221-
* replace ',' by \u200C for nthChild and other selector (div:nth-child(2,3,4))
222-
*
223-
* Examples:
224-
* div[data-lang="fr,\"de,us"]
225-
* div[data-lang='fr,\'de,us']
226-
* div:matches(.toto, .titi:matches(.toto, .titi))
227-
*
228-
* Regex logic:
229-
* ("|')(?:\\\1|.)*?\1 => Handle the " and '
230-
* \(.*?\) => Handle the ()
231-
*
232-
* Optimization 1:
233-
* No greedy capture (see docs about the difference between .* and .*?)
234-
*
235-
* Optimization 2:
236-
* ("|')(?:\\\1|.)*?\1 this use reference to capture group, it work faster.
237-
*/
238-
.replace(/("|')(?:\\\1|.)*?\1|\(.*?\)/g, m => m.replace(/,/g, '\u200C'))
239287
// Split the selector by ','
240288
.split(',')
241289
// Replace back \u200C by ','
@@ -522,7 +570,7 @@ export const parse = (
522570
*/
523571
function atcustommedia(): CssCustomMediaAST | void {
524572
const pos = position();
525-
const m = match(/^@custom-media\s+(--[^\s]+)\s*([^{;]+);/);
573+
const m = match(/^@custom-media\s+(--\S+)\s*([^{;\s][^{;]*);/);
526574
if (!m) {
527575
return;
528576
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"type": "stylesheet",
3+
"stylesheet": {
4+
"rules": [
5+
{
6+
"type": "rule",
7+
"selectors": [
8+
".klass:is(:nth-child(1), :nth-child(2))"
9+
],
10+
"declarations": [
11+
{
12+
"type": "declaration",
13+
"property": "margin",
14+
"value": "0 !important",
15+
"position": {
16+
"start": {
17+
"line": 1,
18+
"column": 42
19+
},
20+
"end": {
21+
"line": 1,
22+
"column": 62
23+
},
24+
"source": "input.css"
25+
}
26+
}
27+
],
28+
"position": {
29+
"start": {
30+
"line": 1,
31+
"column": 1
32+
},
33+
"end": {
34+
"line": 1,
35+
"column": 63
36+
},
37+
"source": "input.css"
38+
}
39+
}
40+
]
41+
}
42+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.klass:is(:nth-child(1), :nth-child(2)){margin:0 !important;}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.klass:is(:nth-child(1), :nth-child(2)) {margin: 0 !important}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.klass:is(:nth-child(1), :nth-child(2)) {
2+
margin: 0 !important;
3+
}

0 commit comments

Comments
 (0)