fix(wiki): escape embedded JSON in HTML viewer and handle LLM token param

- Unicode-escape <, >, & in serialised JSON to prevent the HTML parser
  from misinterpreting tag-like sequences inside <script> blocks.
  Add a Content-Security-Policy meta tag to restrict resource origins.
- Auto-switch max_tokens → max_completion_tokens when the LLM API
  rejects the former with a 400, retrying transparently.
This commit is contained in:
Jobin Kurian 2026-03-31 11:04:30 +05:30
parent c72890d59d
commit ae93a902de
2 changed files with 22 additions and 3 deletions

View file

@ -68,9 +68,15 @@ function buildHTML(
meta: Record<string, unknown> | null,
): string {
// Embed data as JSON inside the HTML
const pagesJSON = JSON.stringify(pages);
const treeJSON = JSON.stringify(moduleTree);
const metaJSON = JSON.stringify(meta);
// Unicode-escape HTML-significant chars so the HTML parser can't misinterpret them
const safeJSON = (v: unknown) =>
JSON.stringify(v)
.replace(/</g, '\\u003c')
.replace(/>/g, '\\u003e')
.replace(/&/g, '\\u0026');
const pagesJSON = safeJSON(pages);
const treeJSON = safeJSON(moduleTree);
const metaJSON = safeJSON(meta);
const parts: string[] = [];
@ -80,6 +86,7 @@ function buildHTML(
parts.push('<head>');
parts.push('<meta charset="UTF-8">');
parts.push('<meta name="viewport" content="width=device-width, initial-scale=1.0">');
parts.push('<meta http-equiv="Content-Security-Policy" content="default-src \'none\'; script-src \'unsafe-inline\' https://cdn.jsdelivr.net; style-src \'unsafe-inline\'; img-src data: https:;">');
parts.push('<title>' + esc(projectName) + ' — Wiki</title>');
parts.push('<script src="https://cdn.jsdelivr.net/npm/marked@11.0.0/marked.min.js"><\/script>');
parts.push(

View file

@ -210,6 +210,18 @@ export async function callLLM(
continue;
}
// Auto-switch max_tokens → max_completion_tokens when the model rejects max_tokens
if (
response.status === 400 &&
errorText.includes('max_completion_tokens') &&
body.max_tokens !== undefined
) {
body.max_completion_tokens = body.max_tokens;
delete body.max_tokens;
// Retry immediately with the corrected parameter
continue;
}
throw new Error(`LLM API error (${response.status}): ${errorText.slice(0, 500)}`);
}