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:
parent
6af205a13a
commit
8d46b92e30
@ -239,9 +239,9 @@ export async function handleOpenAiHttpRequest(
|
|||||||
],
|
],
|
||||||
usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
|
usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch {
|
||||||
sendJson(res, 500, {
|
sendJson(res, 500, {
|
||||||
error: { message: String(err), type: "api_error" },
|
error: { message: "Internal server error", type: "api_error" },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -361,7 +361,7 @@ export async function handleOpenAiHttpRequest(
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch {
|
||||||
if (closed) return;
|
if (closed) return;
|
||||||
writeSse(res, {
|
writeSse(res, {
|
||||||
id: runId,
|
id: runId,
|
||||||
@ -371,7 +371,7 @@ export async function handleOpenAiHttpRequest(
|
|||||||
choices: [
|
choices: [
|
||||||
{
|
{
|
||||||
index: 0,
|
index: 0,
|
||||||
delta: { content: `Error: ${String(err)}` },
|
delta: { content: "Error: Internal server error" },
|
||||||
finish_reason: "stop",
|
finish_reason: "stop",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@ -435,7 +435,7 @@ export async function handleOpenResponsesHttpRequest(
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
sendJson(res, 400, {
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
@ -452,7 +452,7 @@ export async function handleOpenResponsesHttpRequest(
|
|||||||
toolChoicePrompt = toolChoiceResult.extraSystemPrompt;
|
toolChoicePrompt = toolChoiceResult.extraSystemPrompt;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
sendJson(res, 400, {
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
@ -565,13 +565,13 @@ export async function handleOpenResponsesHttpRequest(
|
|||||||
});
|
});
|
||||||
|
|
||||||
sendJson(res, 200, response);
|
sendJson(res, 200, response);
|
||||||
} catch (err) {
|
} catch {
|
||||||
const response = createResponseResource({
|
const response = createResponseResource({
|
||||||
id: responseId,
|
id: responseId,
|
||||||
model,
|
model,
|
||||||
status: "failed",
|
status: "failed",
|
||||||
output: [],
|
output: [],
|
||||||
error: { code: "api_error", message: String(err) },
|
error: { code: "api_error", message: "Internal server error" },
|
||||||
});
|
});
|
||||||
sendJson(res, 500, response);
|
sendJson(res, 500, response);
|
||||||
}
|
}
|
||||||
@ -844,7 +844,7 @@ export async function handleOpenResponsesHttpRequest(
|
|||||||
delta: content,
|
delta: content,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch {
|
||||||
if (closed) return;
|
if (closed) return;
|
||||||
|
|
||||||
finalUsage = finalUsage ?? createEmptyUsage();
|
finalUsage = finalUsage ?? createEmptyUsage();
|
||||||
@ -853,7 +853,7 @@ export async function handleOpenResponsesHttpRequest(
|
|||||||
model,
|
model,
|
||||||
status: "failed",
|
status: "failed",
|
||||||
output: [],
|
output: [],
|
||||||
error: { code: "api_error", message: String(err) },
|
error: { code: "api_error", message: "Internal server error" },
|
||||||
usage: finalUsage,
|
usage: finalUsage,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user