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) <noreply@anthropic.com>
This commit is contained in:
parent
4407c24522
commit
edda01e438
5 changed files with 24 additions and 20 deletions
|
|
@ -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 },
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<string, { answers: string[]
|
|||
return out;
|
||||
}
|
||||
|
||||
// Strip token-like strings from error messages before surfacing
|
||||
const TOKEN_PATTERNS = [
|
||||
/xoxb-[A-Za-z0-9\-]+/g, // Slack bot tokens
|
||||
/xoxp-[A-Za-z0-9\-]+/g, // Slack user tokens
|
||||
/xoxa-[A-Za-z0-9\-]+/g, // Slack app tokens
|
||||
/\d{8,10}:[A-Za-z0-9_-]{35}/g, // Telegram bot tokens
|
||||
/[A-Za-z0-9_\-.]{20,}/g, // Long opaque secrets (Discord tokens, etc.)
|
||||
];
|
||||
|
||||
export function sanitizeError(msg: string): string {
|
||||
let sanitized = msg;
|
||||
for (const pattern of TOKEN_PATTERNS) {
|
||||
sanitized = sanitized.replace(pattern, "[REDACTED]");
|
||||
}
|
||||
return sanitized;
|
||||
}
|
||||
|
||||
function errorResult(message: string, channel: string): ToolResult {
|
||||
return {
|
||||
content: [{ type: "text", text: sanitizeError(message) }],
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|||
import { dirname, join } from "node:path";
|
||||
import { getGlobalGSDPreferencesPath, loadEffectiveGSDPreferences } from "../gsd/preferences.js";
|
||||
import { getRemoteConfigStatus, isValidChannelId, resolveRemoteConfig } from "./config.js";
|
||||
import { sanitizeError } from "./manager.js";
|
||||
import { sanitizeError } from "../shared/sanitize.js";
|
||||
import { getLatestPromptSummary } from "./status.js";
|
||||
|
||||
export async function handleRemote(
|
||||
|
|
|
|||
19
src/resources/extensions/shared/sanitize.ts
Normal file
19
src/resources/extensions/shared/sanitize.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/**
|
||||
* Sanitize error messages by redacting token-like strings before surfacing.
|
||||
*/
|
||||
|
||||
const TOKEN_PATTERNS = [
|
||||
/xoxb-[A-Za-z0-9\-]+/g, // Slack bot tokens
|
||||
/xoxp-[A-Za-z0-9\-]+/g, // Slack user tokens
|
||||
/xoxa-[A-Za-z0-9\-]+/g, // Slack app tokens
|
||||
/\d{8,10}:[A-Za-z0-9_-]{35}/g, // Telegram bot tokens
|
||||
/[A-Za-z0-9_\-.]{20,}/g, // Long opaque secrets (Discord tokens, etc.)
|
||||
];
|
||||
|
||||
export function sanitizeError(msg: string): string {
|
||||
let sanitized = msg;
|
||||
for (const pattern of TOKEN_PATTERNS) {
|
||||
sanitized = sanitized.replace(pattern, "[REDACTED]");
|
||||
}
|
||||
return sanitized;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue