Skip to content

Commit 36617bd

Browse files
authored
fix(sanitizer): match attribute quotes by type to avoid mangling content (#1371)
stripHiddenAttributes used the pattern `["'][^"']*["']` for each quoted attribute, which matches an opening quote of either type and stops at the first quote of either type. When a value contained the other quote character — e.g. an apostrophe inside a double-quoted attribute like `title="We'll do it"` — the match terminated at the apostrophe, so the wrong span was removed and the surrounding text was corrupted (e.g. `<Tooltip title="We'll do it" placement="top">` became `<Tooltipll do it" placement="top">`). This surfaced via the github_inline_comment MCP tool: suggestion blocks whose code lines contain quotes were mangled before posting (#1366). Match each quoted form per quote type (`"[^"]*"` and `'[^']*'`), mirroring stripMarkdownLinkTitles, so a value may freely contain the other quote character. The unquoted fallback is unchanged. Closes #1366 Co-authored-by: bymle <229636660+bymle@users.noreply.github.com>
1 parent 24b9156 commit 36617bd

2 files changed

Lines changed: 28 additions & 5 deletions

File tree

src/github/utils/sanitizer.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,23 @@ export function stripMarkdownLinkTitles(content: string): string {
2020
}
2121

2222
export function stripHiddenAttributes(content: string): string {
23-
content = content.replace(/\salt\s*=\s*["'][^"']*["']/gi, "");
23+
// Quoted values are matched per quote type so that a value containing the
24+
// other quote character (e.g. an apostrophe inside a double-quoted value)
25+
// does not terminate the match early and mangle surrounding content (#1366).
26+
content = content.replace(/\salt\s*=\s*"[^"]*"/gi, "");
27+
content = content.replace(/\salt\s*=\s*'[^']*'/gi, "");
2428
content = content.replace(/\salt\s*=\s*[^\s>]+/gi, "");
25-
content = content.replace(/\stitle\s*=\s*["'][^"']*["']/gi, "");
29+
content = content.replace(/\stitle\s*=\s*"[^"]*"/gi, "");
30+
content = content.replace(/\stitle\s*=\s*'[^']*'/gi, "");
2631
content = content.replace(/\stitle\s*=\s*[^\s>]+/gi, "");
27-
content = content.replace(/\saria-label\s*=\s*["'][^"']*["']/gi, "");
32+
content = content.replace(/\saria-label\s*=\s*"[^"]*"/gi, "");
33+
content = content.replace(/\saria-label\s*=\s*'[^']*'/gi, "");
2834
content = content.replace(/\saria-label\s*=\s*[^\s>]+/gi, "");
29-
content = content.replace(/\sdata-[a-zA-Z0-9-]+\s*=\s*["'][^"']*["']/gi, "");
35+
content = content.replace(/\sdata-[a-zA-Z0-9-]+\s*=\s*"[^"]*"/gi, "");
36+
content = content.replace(/\sdata-[a-zA-Z0-9-]+\s*=\s*'[^']*'/gi, "");
3037
content = content.replace(/\sdata-[a-zA-Z0-9-]+\s*=\s*[^\s>]+/gi, "");
31-
content = content.replace(/\splaceholder\s*=\s*["'][^"']*["']/gi, "");
38+
content = content.replace(/\splaceholder\s*=\s*"[^"]*"/gi, "");
39+
content = content.replace(/\splaceholder\s*=\s*'[^']*'/gi, "");
3240
content = content.replace(/\splaceholder\s*=\s*[^\s>]+/gi, "");
3341
return content;
3442
}

test/sanitizer.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,21 @@ describe("stripHiddenAttributes", () => {
131131
),
132132
).toBe('<img src="pic.jpg" class="image">');
133133
});
134+
135+
it("should not corrupt content when an attribute value contains the other quote type", () => {
136+
// Regression for #1366: an apostrophe inside a double-quoted attribute
137+
// (or a double quote inside a single-quoted attribute) must not cause the
138+
// closing quote to be mismatched, which previously mangled later text.
139+
expect(
140+
stripHiddenAttributes(`<Tooltip title="We'll do it" placement="top">`),
141+
).toBe('<Tooltip placement="top">');
142+
expect(
143+
stripHiddenAttributes(`<img alt="Bob's avatar" src="pic.jpg">`),
144+
).toBe('<img src="pic.jpg">');
145+
expect(stripHiddenAttributes(`<div title='say "hi"'>Content</div>`)).toBe(
146+
"<div>Content</div>",
147+
);
148+
});
134149
});
135150

136151
describe("normalizeHtmlEntities", () => {

0 commit comments

Comments
 (0)