From 866f9fec00e3de6d246cd446f647aa8a90944c21 Mon Sep 17 00:00:00 2001 From: jackwener Date: Fri, 10 Apr 2026 15:27:00 +0800 Subject: [PATCH] fix(security): escape codegen strings and redact diagnostic body 1. candidateToJs: escape single quotes in site, name, domain, and arg name/type fields to prevent syntax errors in generated JS adapters. Previously only description and help fields were escaped. 2. diagnostic: pass network request body through redactText() to prevent sensitive data (JWT, bearer tokens) from leaking into repair context. responseBody/responsePreview already used sanitizeCapturedValue which calls redactText, but the body field only had truncation. --- src/diagnostic.ts | 4 ++-- src/generate-verified.ts | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/diagnostic.ts b/src/diagnostic.ts index 02ea6422..f762ecb3 100644 --- a/src/diagnostic.ts +++ b/src/diagnostic.ts @@ -183,9 +183,9 @@ function redactNetworkRequest(req: unknown): unknown { redacted.responseHeaders = redactHeaders(redacted.responseHeaders as Record); } - // Truncate response body + // Redact and truncate response body if (typeof redacted.body === 'string') { - redacted.body = truncate(redacted.body, MAX_REQUEST_BODY_CHARS); + redacted.body = redactText(truncate(redacted.body, MAX_REQUEST_BODY_CHARS)); } if ('responseBody' in redacted) { redacted.responseBody = sanitizeCapturedValue(redacted.responseBody); diff --git a/src/generate-verified.ts b/src/generate-verified.ts index 232c06fe..e92a4da6 100644 --- a/src/generate-verified.ts +++ b/src/generate-verified.ts @@ -488,8 +488,8 @@ function candidateToJs(candidate: CandidateYaml): string { const browser = detectBrowserFlag(candidate); const argsArray = Object.entries(candidate.args ?? {}).map(([name, def]) => { - const parts: string[] = [`name: '${name}'`]; - if (def.type && def.type !== 'str') parts.push(`type: '${def.type}'`); + const parts: string[] = [`name: '${name.replace(/'/g, "\\'")}'`]; + if (def.type && def.type !== 'str') parts.push(`type: '${def.type.replace(/'/g, "\\'")}'`); if (def.required) parts.push('required: true'); if (def.default !== undefined) parts.push(`default: ${JSON.stringify(def.default)}`); if (def.description) parts.push(`help: '${def.description.replace(/'/g, "\\'")}'`); @@ -527,10 +527,10 @@ function candidateToJs(candidate: CandidateYaml): string { lines.push("import { cli, Strategy } from '@jackwener/opencli/registry';"); lines.push(''); lines.push('cli({'); - lines.push(` site: '${candidate.site}',`); - lines.push(` name: '${candidate.name}',`); + lines.push(` site: '${candidate.site.replace(/'/g, "\\'")}',`); + lines.push(` name: '${candidate.name.replace(/'/g, "\\'")}',`); if (candidate.description) lines.push(` description: '${candidate.description.replace(/'/g, "\\'")}',`); - if (candidate.domain) lines.push(` domain: '${candidate.domain}',`); + if (candidate.domain) lines.push(` domain: '${candidate.domain.replace(/'/g, "\\'")}',`); lines.push(` strategy: ${stratEnum},`); lines.push(` browser: ${browser},`); if (argsArray.length > 0) {