From b81fdef5d9179bfc179956c86aea936506331252 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Wed, 8 Apr 2026 13:10:06 +0900 Subject: [PATCH] fix(skill-mcp): redact sensitive data from connection errors Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../skill-mcp-manager/error-redaction.ts | 47 +++++++++++++++++++ .../skill-mcp-manager/stdio-client.ts | 8 +++- 2 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 src/features/skill-mcp-manager/error-redaction.ts diff --git a/src/features/skill-mcp-manager/error-redaction.ts b/src/features/skill-mcp-manager/error-redaction.ts new file mode 100644 index 000000000..d3a3cb0df --- /dev/null +++ b/src/features/skill-mcp-manager/error-redaction.ts @@ -0,0 +1,47 @@ +// Redacts sensitive tokens from error messages to prevent credential exposure +// Follows same patterns as env-cleaner.ts for consistency + +const SENSITIVE_PATTERNS: RegExp[] = [ + // API keys and tokens in common formats + /[a-zA-Z0-9_-]*(?:api[_-]?key|apikey)["\s]*[:=]["\s]*([a-zA-Z0-9_-]{16,})/gi, + /[a-zA-Z0-9_-]*(?:auth[_-]?token|authtoken)["\s]*[:=]["\s]*([a-zA-Z0-9_-]{16,})/gi, + /[a-zA-Z0-9_-]*(?:access[_-]?token|accesstoken)["\s]*[:=]["\s]*([a-zA-Z0-9_-]{16,})/gi, + /[a-zA-Z0-9_-]*(?:secret)["\s]*[:=]["\s]*([a-zA-Z0-9_-]{16,})/gi, + /[a-zA-Z0-9_-]*(?:password)["\s]*[:=]["\s]*([a-zA-Z0-9_-]{8,})/gi, + + // Bearer tokens + /bearer\s+([a-zA-Z0-9_-]{20,})/gi, + + // Common token prefixes + /sk-[a-zA-Z0-9]{20,}/g, // OpenAI-style secret keys + /gh[pousr]_[a-zA-Z0-9]{20,}/gi, // GitHub tokens + /glpat-[a-zA-Z0-9_-]{20,}/gi, // GitLab tokens + /[A-Za-z0-9_]{20,}-[A-Za-z0-9_]{10,}-[A-Za-z0-9_]{10,}/g, // Common JWT-like patterns +] + +const REDACTION_MARKER = "[REDACTED]" + +/** + * Redacts sensitive tokens from a string. + * Used for error messages that may contain command-line arguments or environment info. + */ +export function redactSensitiveData(input: string): string { + let result = input + + for (const pattern of SENSITIVE_PATTERNS) { + result = result.replace(pattern, REDACTION_MARKER) + } + + return result +} + +/** + * Redacts sensitive data from an Error object, returning a new Error. + * Preserves the stack trace but redacts the message. + */ +export function redactErrorSensitiveData(error: Error): Error { + const redactedMessage = redactSensitiveData(error.message) + const redactedError = new Error(redactedMessage) + redactedError.stack = error.stack ? redactSensitiveData(error.stack) : undefined + return redactedError +} diff --git a/src/features/skill-mcp-manager/stdio-client.ts b/src/features/skill-mcp-manager/stdio-client.ts index 0d3e9047c..3a5c796a4 100644 --- a/src/features/skill-mcp-manager/stdio-client.ts +++ b/src/features/skill-mcp-manager/stdio-client.ts @@ -3,6 +3,7 @@ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js" import type { ClaudeCodeMcpServer } from "../claude-code-mcp-loader/types" import { createCleanMcpEnvironment } from "./env-cleaner" import { registerProcessCleanup, startCleanupTimer } from "./cleanup" +import { redactSensitiveData } from "./error-redaction" import type { ManagedClient, SkillMcpClientConnectionParams } from "./types" function getStdioCommand(config: ClaudeCodeMcpServer, serverName: string): string { @@ -45,10 +46,13 @@ export async function createStdioClient(params: SkillMcpClientConnectionParams): } const errorMessage = error instanceof Error ? error.message : String(error) + const fullCommand = `${command} ${args.join(" ")}` + const safeCommand = redactSensitiveData(fullCommand) + const safeErrorMessage = redactSensitiveData(errorMessage) throw new Error( `Failed to connect to MCP server "${info.serverName}".\n\n` + - `Command: ${command} ${args.join(" ")}\n` + - `Reason: ${errorMessage}\n\n` + + `Command: ${safeCommand}\n` + + `Reason: ${safeErrorMessage}\n\n` + `Hints:\n` + ` - Ensure the command is installed and available in PATH\n` + ` - Check if the MCP server package exists\n` +