From 4faa986e17cc4aca59d8fdc2f007498abd033e6f Mon Sep 17 00:00:00 2001 From: murashit Date: Wed, 15 Jul 2026 16:05:57 +0900 Subject: [PATCH] fix: address CodeQL string escaping and regex findings --- src/domain/threads/archive-markdown.ts | 9 ++++++++- tests/domain/threads/archive-markdown.test.ts | 2 ++ tests/styles.test.ts | 4 +++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/domain/threads/archive-markdown.ts b/src/domain/threads/archive-markdown.ts index 16bdfd7f..8b9d6d21 100644 --- a/src/domain/threads/archive-markdown.ts +++ b/src/domain/threads/archive-markdown.ts @@ -181,7 +181,7 @@ function normalizedExportedMarkdownLink(link: ParsedMarkdownLink, vaultPath: str return `[${link.label}](${markdownHref(`${vaultRelative}${parsed.subpath}`)})`; } - return isFilesystemAbsolutePath(normalizeFilePath(parsed.path)) ? `${link.label} (\`${href.replace(/`/g, "\\`")}\`)` : link.raw; + return isFilesystemAbsolutePath(normalizeFilePath(parsed.path)) ? `${link.label} (${markdownCodeSpan(href)})` : link.raw; } function archiveExportShouldKeepAbsolute(vaultRelativePath: string, vaultConfigDir: string | null | undefined): boolean { @@ -192,6 +192,13 @@ function markdownHref(value: string): string { return /[\s()]/.test(value) ? `<${value}>` : value; } +function markdownCodeSpan(value: string): string { + let fenceLength = 1; + for (const match of value.matchAll(/`+/g)) fenceLength = Math.max(fenceLength, match[0].length + 1); + const fence = "`".repeat(fenceLength); + return fenceLength === 1 ? `${fence}${value}${fence}` : `${fence} ${value} ${fence}`; +} + function isInsideInlineCode(line: string, offset: number): boolean { let inCode = false; for (let index = 0; index < offset; index += 1) { diff --git a/tests/domain/threads/archive-markdown.test.ts b/tests/domain/threads/archive-markdown.test.ts index f512163f..ca5d0477 100644 --- a/tests/domain/threads/archive-markdown.test.ts +++ b/tests/domain/threads/archive-markdown.test.ts @@ -151,6 +151,7 @@ describe("thread archive export", () => { "[Vault source line](/Users/showhey/Vault/src/main.ts:12:4#L12)", "[Vault config](/Users/showhey/Vault/vault-config/plugins/codex-panel/main.js)", "[External file](/Users/showhey/Repos/project/README.md)", + "[External file with backticks](/Users/showhey/Repos/project/a\\`b``c.md)", "[Relative](topics/Other.md)", "[Website](https://example.com/docs)", "![Image](/Users/showhey/Repos/project/image.png)", @@ -174,6 +175,7 @@ describe("thread archive export", () => { "[Vault source line](src/main.ts#L12)", "Vault config (`/Users/showhey/Vault/vault-config/plugins/codex-panel/main.js`)", "External file (`/Users/showhey/Repos/project/README.md`)", + "External file with backticks (``` /Users/showhey/Repos/project/a\\`b``c.md ```)", "[Relative](topics/Other.md)", "[Website](https://example.com/docs)", "![Image](/Users/showhey/Repos/project/image.png)", diff --git a/tests/styles.test.ts b/tests/styles.test.ts index fdc2a008..344d21f5 100644 --- a/tests/styles.test.ts +++ b/tests/styles.test.ts @@ -19,7 +19,9 @@ function standaloneRuleBody(selector: string): string { describe("panel CSS boundaries", () => { it("defines design tokens on every standalone UI root", () => { - const tokenScope = /^(?(?:\.[^{]+,\n)*\.[^{]+) \{/m.exec(styles)?.groups?.["selectors"] ?? ""; + const tokenScopeEnd = styles.indexOf(" {"); + expect(tokenScopeEnd).toBeGreaterThan(0); + const tokenScope = styles.slice(0, tokenScopeEnd); expect(tokenScope).toContain(".codex-panel"); expect(tokenScope).toContain(".codex-panel-turn-diff");