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
This commit is contained in:
s4na 2026-01-30 17:34:13 +09:00
parent 6af205a13a
commit 8d46b92e30
2 changed files with 10 additions and 10 deletions

View File

@ -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",
},
],

View File

@ -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,
});