From 075e2f47adc997587afd7ed6504d7dfc09a8d434 Mon Sep 17 00:00:00 2001 From: can1357 Date: Tue, 12 May 2026 11:15:36 +0200 Subject: [PATCH] fix(coding-agent/export): prevented HTML template `$` substitution expansions - Switched template CSS/JS inlining replacements to callback form in generation scripts to avoid `$` replacement expansion semantics. - Updated HTML export generation to use callback-based replacements for theme variables and session data injection to prevent accidental `$` substitution parsing. - Added regression tests for the inlined script ensuring literal `$'` regex tokens remain intact, no closing HTML tags are injected, and the script parses via `new Function`. --- .../coding-agent/scripts/generate-template.ts | 7 ++-- .../coding-agent/src/export/html/index.ts | 7 +++- .../src/export/html/template.generated.ts | 2 +- .../src/export/html/template.macro.ts | 7 ++-- .../html-template-script-substitution.test.ts | 41 +++++++++++++++++++ 5 files changed, 55 insertions(+), 9 deletions(-) create mode 100644 packages/coding-agent/test/export/html-template-script-substitution.test.ts diff --git a/packages/coding-agent/scripts/generate-template.ts b/packages/coding-agent/scripts/generate-template.ts index 2bc95be57..8d2ae3c4f 100644 --- a/packages/coding-agent/scripts/generate-template.ts +++ b/packages/coding-agent/scripts/generate-template.ts @@ -18,10 +18,11 @@ const minifiedCss = css .replace(/\s*([{}:;,])\s*/g, "$1") .trim(); -// Inline everything +// Inline everything; use function replacements so `$'`, `$&`, `$$`, etc. inside +// the embedded CSS/JS are not interpreted as substitution patterns. const template = html - .replace("", ``) - .replace("", ``); + .replace("", () => ``) + .replace("", () => ``); // Write generated file const output = `// Auto-generated by scripts/generate-template.ts - DO NOT EDIT diff --git a/packages/coding-agent/src/export/html/index.ts b/packages/coding-agent/src/export/html/index.ts index 71f367188..5f1b010ff 100644 --- a/packages/coding-agent/src/export/html/index.ts +++ b/packages/coding-agent/src/export/html/index.ts @@ -103,9 +103,12 @@ async function generateHtml(sessionData: SessionData, themeName?: string): Promi const themeVars = await generateThemeVars(themeName); const sessionDataBase64 = Buffer.from(JSON.stringify(sessionData)).toBase64(); - return TEMPLATE.replace("", ``).replace( + // Use function replacements so `$'`, `$&`, `$$`, `$n`, etc. in the + // substituted CSS/base64 are not interpreted as substitution patterns + // (see https://mdn.io/String.replace). + return TEMPLATE.replace("", () => ``).replace( "{{SESSION_DATA}}", - sessionDataBase64, + () => sessionDataBase64, ); } diff --git a/packages/coding-agent/src/export/html/template.generated.ts b/packages/coding-agent/src/export/html/template.generated.ts index 530451d87..954a176b0 100644 --- a/packages/coding-agent/src/export/html/template.generated.ts +++ b/packages/coding-agent/src/export/html/template.generated.ts @@ -1,2 +1,2 @@ // Auto-generated by scripts/generate-template.ts - DO NOT EDIT -export const TEMPLATE = "\n\n\n \n \n Session Export\n \n \n\n\n \n
\n
\n \n
\n
\n
\n
\n
\n
\n \"\"\n
\n
\n\n \n \n \n \n\n\n"; +export const TEMPLATE = "\n\n\n \n \n Session Export\n \n \n\n\n \n
\n
\n \n
\n
\n
\n
\n
\n
\n \"\"\n
\n
\n\n \n \n \n \n\n\n"; diff --git a/packages/coding-agent/src/export/html/template.macro.ts b/packages/coding-agent/src/export/html/template.macro.ts index 86bef1258..26a532189 100644 --- a/packages/coding-agent/src/export/html/template.macro.ts +++ b/packages/coding-agent/src/export/html/template.macro.ts @@ -17,8 +17,9 @@ export async function getTemplate(): Promise { .replace(/\s*([{}:;,])\s*/g, "$1") .trim(); - // Inline everything + // Inline everything; use function replacements so `$'`, `$&`, `$$`, etc. + // inside the embedded CSS/JS are not interpreted as substitution patterns. return html - .replace("", ``) - .replace("", ``); + .replace("", () => ``) + .replace("", () => ``); } diff --git a/packages/coding-agent/test/export/html-template-script-substitution.test.ts b/packages/coding-agent/test/export/html-template-script-substitution.test.ts new file mode 100644 index 000000000..dbdf2f9ca --- /dev/null +++ b/packages/coding-agent/test/export/html-template-script-substitution.test.ts @@ -0,0 +1,41 @@ +import { describe, expect, it } from "bun:test"; +import { TEMPLATE } from "../../src/export/html/template.generated"; + +// Regression: `String.prototype.replace(string, string)` treats `$'`, `$&`, +// `$$`, `$n`, etc. as substitution patterns. The inlined `