diff --git a/src/github/utils/sanitizer.ts b/src/github/utils/sanitizer.ts
index 83ee096b..0cb8408a 100644
--- a/src/github/utils/sanitizer.ts
+++ b/src/github/utils/sanitizer.ts
@@ -20,15 +20,23 @@ export function stripMarkdownLinkTitles(content: string): string {
}
export function stripHiddenAttributes(content: string): string {
- content = content.replace(/\salt\s*=\s*["'][^"']*["']/gi, "");
+ // Quoted values are matched per quote type so that a value containing the
+ // other quote character (e.g. an apostrophe inside a double-quoted value)
+ // does not terminate the match early and mangle surrounding content (#1366).
+ content = content.replace(/\salt\s*=\s*"[^"]*"/gi, "");
+ content = content.replace(/\salt\s*=\s*'[^']*'/gi, "");
content = content.replace(/\salt\s*=\s*[^\s>]+/gi, "");
- content = content.replace(/\stitle\s*=\s*["'][^"']*["']/gi, "");
+ content = content.replace(/\stitle\s*=\s*"[^"]*"/gi, "");
+ content = content.replace(/\stitle\s*=\s*'[^']*'/gi, "");
content = content.replace(/\stitle\s*=\s*[^\s>]+/gi, "");
- content = content.replace(/\saria-label\s*=\s*["'][^"']*["']/gi, "");
+ content = content.replace(/\saria-label\s*=\s*"[^"]*"/gi, "");
+ content = content.replace(/\saria-label\s*=\s*'[^']*'/gi, "");
content = content.replace(/\saria-label\s*=\s*[^\s>]+/gi, "");
- content = content.replace(/\sdata-[a-zA-Z0-9-]+\s*=\s*["'][^"']*["']/gi, "");
+ content = content.replace(/\sdata-[a-zA-Z0-9-]+\s*=\s*"[^"]*"/gi, "");
+ content = content.replace(/\sdata-[a-zA-Z0-9-]+\s*=\s*'[^']*'/gi, "");
content = content.replace(/\sdata-[a-zA-Z0-9-]+\s*=\s*[^\s>]+/gi, "");
- content = content.replace(/\splaceholder\s*=\s*["'][^"']*["']/gi, "");
+ content = content.replace(/\splaceholder\s*=\s*"[^"]*"/gi, "");
+ content = content.replace(/\splaceholder\s*=\s*'[^']*'/gi, "");
content = content.replace(/\splaceholder\s*=\s*[^\s>]+/gi, "");
return content;
}
diff --git a/test/sanitizer.test.ts b/test/sanitizer.test.ts
index a89353b7..e69a3995 100644
--- a/test/sanitizer.test.ts
+++ b/test/sanitizer.test.ts
@@ -131,6 +131,21 @@ describe("stripHiddenAttributes", () => {
),
).toBe('
');
});
+
+ it("should not corrupt content when an attribute value contains the other quote type", () => {
+ // Regression for #1366: an apostrophe inside a double-quoted attribute
+ // (or a double quote inside a single-quoted attribute) must not cause the
+ // closing quote to be mismatched, which previously mangled later text.
+ expect(
+ stripHiddenAttributes(``),
+ ).toBe('');
+ expect(
+ stripHiddenAttributes(`
`),
+ ).toBe('
');
+ expect(stripHiddenAttributes(`Content
`)).toBe(
+ "Content
",
+ );
+ });
});
describe("normalizeHtmlEntities", () => {