8000 fix: fix crash when attribute selector `[..]` contains escaped charac… · html-validate/html-validate@0c19b82 · GitHub
[go: up one dir, main page]

Skip to content

Commit 0c19b82

Browse files
committed
fix: fix crash when attribute selector [..] contains escaped characters
1 parent fb91b7c commit 0c19b82

File tree

2 files changed

+43
-3
lines changed

2 files changed

+43
-3
lines changed

src/dom/selector/condition.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,15 +59,15 @@ export class IdCondition extends Condition {
5959
*/
6060
export class AttributeCondition extends Condition {
6161
private readonly key: string;
62-
private readonly op: string;
63-
private readonly value: string;
62+
private readonly op: string | undefined;
63+
private readonly value: string | undefined;
6464

6565
public constructor(attr: string) {
6666
super();
6767
const [, key, op, value] = /^(.+?)(?:([~^$*|]?=)"([^"]+?)")?$/.exec(attr)!; // eslint-disable-line @typescript-eslint/no-non-null-assertion -- will always match
6868
this.key = key;
6969
this.op = op;
70-
this.value = value;
70+
this.value = typeof value === "string" ? stripslashes(value) : value;
7171
}
7272

7373
public match(node: HtmlElement): boolean {
@@ -77,6 +77,7 @@ export class AttributeCondition extends Condition {
7777
case undefined:
7878
return true; /* attribute exists */
7979
case "=":
80+
/* eslint-disable-next-line sonarjs/different-types-comparison -- false positive */
8081
return cur.value === this.value;
8182
default:
8283
throw new Error(`Attribute selector operator ${this.op} is not implemented yet`);

src/dom/selector/selector.spec.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,12 @@ describe("generateIdSelector()", () => {
174174
const id = "foo,bar";
175175
expect(generateIdSelector(id)).toBe("#foo\\,bar");
176176
});
177+
178+
it("should handle leading digits and comma", () => {
179+
expect.assertions(1);
180+
const id = "1foo,bar";
181+
expect(generateIdSelector(id)).toBe('[id="1foo\\,bar"]');
182+
});
177183
});
178184

179185
describe("Selector", () => {
@@ -318,6 +324,39 @@ describe("Selector", () => {
318324
expect(fetch(selector.match(document))).toEqual([expect.objectContaining({ tagName: "div" })]);
319325
});
320326

327+
it("should match id with leading number", async () => {
328+
expect.assertions(2);
329+
const resolvedConfig = await Config.empty().resolve();
330+
const parser = new Parser(resolvedConfig);
331+
const document = parser.parseHtml(`<div id="1foo"></div>`);
332+
const text = generateIdSelector("1foo");
333+
const selector = new Selector(text);
334+
expect(text).toBe('[id="1foo"]');
335+
expect(fetch(selector.match(document))).toEqual([expect.objectContaining({ tagName: "div" })]);
336+
});
337+
338+
it("should match id with comma", async () => {
339+
expect.assertions(2);
340+
const resolvedConfig = await Config.empty().resolve();
341+
const parser = new Parser(resolvedConfig);
342+
const document = parser.parseHtml(`<div id="foo,bar"></div>`);
343+
const text = generateIdSelector("foo,bar");
344+
const selector = new Selector(text);
345+
expect(text).toBe("#foo\\,bar");
346+
expect(fetch(selector.match(document))).toEqual([expect.objectContaining({ tagName: "div" })]);
347+
});
348+
349+
it("should match id with leading number and comma", async () => {
350+
expect.assertions(2);
351+
const resolvedConfig = await Config.empty().resolve();
352+
const parser = new Parser(resolvedConfig);
353+
const document = parser.parseHtml(`<div id="1foo,bar"></div>`);
354+
const text = generateIdSelector("1foo,bar");
355+
const selector = new Selector(text);
356+
expect(text).toBe('[id="1foo\\,bar"]');
357+
expect(fetch(selector.match(document))).toEqual([expect.objectContaining({ tagName: "div" })]);
358+
});
359+
321360
it("should match having attribute ([wilma])", () => {
322361
expect.assertions(1);
323362
const selector = new Selector("[wilma]");

0 commit comments

Comments
 (0)
0