From edda01e438387fc79c64e72f61d4676a291c4f98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=82CHES?= Date: Tue, 17 Mar 2026 18:30:05 -0600 Subject: [PATCH] fix: extract sanitizeError to shared module and apply to ask-user-questions (#1033) Closes a security gap where ask-user-questions errorResult() could leak tokens in error messages. The sanitizeError function and TOKEN_PATTERNS are now in shared/sanitize.ts, imported by both manager.ts and ask-user-questions.ts. Co-authored-by: Claude Opus 4.6 (1M context) --- .../extensions/ask-user-questions.ts | 3 ++- .../gsd/tests/remote-questions.test.ts | 2 +- .../extensions/remote-questions/manager.ts | 18 +----------------- .../remote-questions/remote-command.ts | 2 +- src/resources/extensions/shared/sanitize.ts | 19 +++++++++++++++++++ 5 files changed, 24 insertions(+), 20 deletions(-) create mode 100644 src/resources/extensions/shared/sanitize.ts diff --git a/src/resources/extensions/ask-user-questions.ts b/src/resources/extensions/ask-user-questions.ts index 7e01b52fc..1a37b3cef 100644 --- a/src/resources/extensions/ask-user-questions.ts +++ b/src/resources/extensions/ask-user-questions.ts @@ -10,6 +10,7 @@ */ import type { ExtensionAPI } from "@gsd/pi-coding-agent"; +import { sanitizeError } from "./shared/sanitize.js"; import { Text } from "@gsd/pi-tui"; import { Type } from "@sinclair/typebox"; import { @@ -80,7 +81,7 @@ function errorResult( questions: Question[] = [], ): { content: { type: "text"; text: string }[]; details: AskUserQuestionsDetails } { return { - content: [{ type: "text", text: message }], + content: [{ type: "text", text: sanitizeError(message) }], details: { questions, response: null, cancelled: true }, }; } diff --git a/src/resources/extensions/gsd/tests/remote-questions.test.ts b/src/resources/extensions/gsd/tests/remote-questions.test.ts index d4b8ec734..f5cb815cb 100644 --- a/src/resources/extensions/gsd/tests/remote-questions.test.ts +++ b/src/resources/extensions/gsd/tests/remote-questions.test.ts @@ -5,7 +5,7 @@ import { join, dirname } from "node:path"; import { fileURLToPath } from "node:url"; import { parseSlackReply, parseDiscordResponse, formatForDiscord, formatForSlack, parseSlackReactionResponse, formatForTelegram, parseTelegramResponse } from "../../remote-questions/format.ts"; import { resolveRemoteConfig, isValidChannelId } from "../../remote-questions/config.ts"; -import { sanitizeError } from "../../remote-questions/manager.ts"; +import { sanitizeError } from "../../shared/sanitize.ts"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); diff --git a/src/resources/extensions/remote-questions/manager.ts b/src/resources/extensions/remote-questions/manager.ts index dd5b57bda..e9dd53cb7 100644 --- a/src/resources/extensions/remote-questions/manager.ts +++ b/src/resources/extensions/remote-questions/manager.ts @@ -9,6 +9,7 @@ import { DiscordAdapter } from "./discord-adapter.js"; import { SlackAdapter } from "./slack-adapter.js"; import { TelegramAdapter } from "./telegram-adapter.js"; import { createPromptRecord, writePromptRecord, markPromptAnswered, markPromptDispatched, markPromptStatus, updatePromptRecord } from "./store.js"; +import { sanitizeError } from "../shared/sanitize.js"; interface ToolResult { content: Array<{ type: "text"; text: string }>; @@ -177,23 +178,6 @@ function formatForTool(answer: RemoteAnswer): Record