From 8d46b92e3004ced7882cf10a923f018519f3eb88 Mon Sep 17 00:00:00 2001 From: s4na Date: Fri, 30 Jan 2026 17:34:13 +0900 Subject: [PATCH] fix(security): sanitize error responses in OpenAI-compatible APIs ## Why The OpenAI-compatible API endpoints (`openai-http.ts`, `openresponses-http.ts`) were returning raw `String(err)` in error responses. This can leak sensitive internal information to HTTP clients: - Stack traces revealing code structure - File paths exposing server directory layout - Internal error messages from dependencies This is the same class of vulnerability that was fixed in #2387 for `server-http.ts`, but these OpenAI-compatible endpoints were missed. ## What Replace `String(err)` with safe error messages: - **500 errors (api_error)**: Return generic "Internal server error" - **400 errors (invalid_request_error)**: Return `err.message` only (no stack trace), with fallback to "Invalid request" ## Changes - `src/gateway/openai-http.ts`: 2 catch blocks sanitized - `src/gateway/openresponses-http.ts`: 4 catch blocks sanitized --- src/gateway/openai-http.ts | 8 ++++---- src/gateway/openresponses-http.ts | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/gateway/openai-http.ts b/src/gateway/openai-http.ts index 2cbcb7c1f..68d7152a6 100644 --- a/src/gateway/openai-http.ts +++ b/src/gateway/openai-http.ts @@ -239,9 +239,9 @@ export async function handleOpenAiHttpRequest( ], usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }, }); - } catch (err) { + } catch { sendJson(res, 500, { - error: { message: String(err), type: "api_error" }, + error: { message: "Internal server error", type: "api_error" }, }); } return true; @@ -361,7 +361,7 @@ export async function handleOpenAiHttpRequest( ], }); } - } catch (err) { + } catch { if (closed) return; writeSse(res, { id: runId, @@ -371,7 +371,7 @@ export async function handleOpenAiHttpRequest( choices: [ { index: 0, - delta: { content: `Error: ${String(err)}` }, + delta: { content: "Error: Internal server error" }, finish_reason: "stop", }, ], diff --git a/src/gateway/openresponses-http.ts b/src/gateway/openresponses-http.ts index 82fef800e..e3d5e55e8 100644 --- a/src/gateway/openresponses-http.ts +++ b/src/gateway/openresponses-http.ts @@ -435,7 +435,7 @@ export async function handleOpenResponsesHttpRequest( } } catch (err) { sendJson(res, 400, { - error: { message: String(err), type: "invalid_request_error" }, + error: { message: err instanceof Error ? err.message : "Invalid request", type: "invalid_request_error" }, }); return true; } @@ -452,7 +452,7 @@ export async function handleOpenResponsesHttpRequest( toolChoicePrompt = toolChoiceResult.extraSystemPrompt; } catch (err) { sendJson(res, 400, { - error: { message: String(err), type: "invalid_request_error" }, + error: { message: err instanceof Error ? err.message : "Invalid request", type: "invalid_request_error" }, }); return true; } @@ -565,13 +565,13 @@ export async function handleOpenResponsesHttpRequest( }); sendJson(res, 200, response); - } catch (err) { + } catch { const response = createResponseResource({ id: responseId, model, status: "failed", output: [], - error: { code: "api_error", message: String(err) }, + error: { code: "api_error", message: "Internal server error" }, }); sendJson(res, 500, response); } @@ -844,7 +844,7 @@ export async function handleOpenResponsesHttpRequest( delta: content, }); } - } catch (err) { + } catch { if (closed) return; finalUsage = finalUsage ?? createEmptyUsage(); @@ -853,7 +853,7 @@ export async function handleOpenResponsesHttpRequest( model, status: "failed", output: [], - error: { code: "api_error", message: String(err) }, + error: { code: "api_error", message: "Internal server error" }, usage: finalUsage, });