Repository navigation
fix(api): sanitize /v1/batches create error body (Hard Rule #12) - #14115
Merged
diegosouzapw merged 1 commit intoSep 22, 2026
Merged
diegosouzapw merged 1 commit into
diegosouzapw merged 1 commit into
Conversation
…apw#12) POST /api/v1/batches returned the raw error.message from its catch block, so a DB-layer failure could ship file paths, SQL fragments or internal identifiers to the client. Route it through sanitizeErrorMessage() from open-sse/utils/error.ts, the same shared sanitizer every other /v1 route already uses, keeping the existing 400 + invalid_request_error shape and a non-empty message fallback. Adds route-level coverage driving the real POST handler with a real API key and seeded input file: the batch INSERT is made to throw a path-and-stack laden SQLITE_CANTOPEN through the repo's own setDbInstance seam (no module mocking), asserting the path/filename/stack never reach the body, plus a control that a clean message survives sanitization intact. Closes diegosouzapw#14089
diegosouzapw
added a commit
to arashashrafii/OmniRoute
that referenced
this pull request
Sep 19, 2026
Removes src/shared/contracts/openai.ts and its test — the module was never imported by any route/handler (verified via grep across open-sse/, src/, tests/), so it added maintenance surface with zero runtime effect. Fixes the two OPENAI_COMPATIBILITY.md doc-links that pointed at the removed test file to point at existing coverage instead (chat-completions-route-shape-gate.test.ts, audio-transcription-handler.test.ts, audio-speech-handler.test.ts). Also drops the src/app/api/v1/batches/route.ts sanitizeErrorMessage() hunk from this PR: it duplicates the same Hard Rule diegosouzapw#12 fix already proposed standalone in diegosouzapw#14115 and diegosouzapw#14145, so it is left for whichever of those lands first rather than triple-implemented here. The real fix in this PR — DSML tool-call normalization for DeepSeek-shaped web providers in webTools.ts/deepseekWebTools.ts — is unaffected and remains the mergeable core. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Owner
|
Thanks @xiechimon — merging via the release merge-train. Validated in local merge-train (mt-train10c) on the devbox @ train tip 4d841aa1c740bbaa03868dc0a403c62099a99a42 with the 72 sibling PRs of this batch: typecheck:core, file-size, complexity, cognitive-complexity, changelog-integrity green; changed-area node:test 831/831 (0 failing) and vitest 480/482 — the two reds are |
diegosouzapw
merged commit Sep 22, 2026
6cb3402
into
diegosouzapw:release/v3.8.51
7 of 16 checks passed
arashashrafii
pushed a commit
to arashashrafii/OmniRoute
that referenced
this pull request
Oct 3, 2026
Removes src/shared/contracts/openai.ts and its test — the module was never imported by any route/handler (verified via grep across open-sse/, src/, tests/), so it added maintenance surface with zero runtime effect. Fixes the two OPENAI_COMPATIBILITY.md doc-links that pointed at the removed test file to point at existing coverage instead (chat-completions-route-shape-gate.test.ts, audio-transcription-handler.test.ts, audio-speech-handler.test.ts). Also drops the src/app/api/v1/batches/route.ts sanitizeErrorMessage() hunk from this PR: it duplicates the same Hard Rule diegosouzapw#12 fix already proposed standalone in diegosouzapw#14115 and diegosouzapw#14145, so it is left for whichever of those lands first rather than triple-implemented here. The real fix in this PR — DSML tool-call normalization for DeepSeek-shaped web providers in webTools.ts/deepseekWebTools.ts — is unaffected and remains the mergeable core. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
POST /api/v1/batchesnow routes its catch-block error message throughsanitizeErrorMessage()before it reaches the client, instead of forwarding the rawerror.message.In my own words: a DB-layer failure during batch create could ship file paths, SQL fragments or internal identifiers to the caller verbatim; this puts the response through the same shared sanitizer every other
/v1route already uses, and the 400 shape stays exactly as before.Why
Closes #14089. Hard Rule #12 (AGENTS.md): every HTTP / SSE / executor error response must go through
buildErrorBody()orsanitizeErrorMessage()fromopen-sse/utils/error.ts. This route's catch block was the one/v1/*spot that skipped it.The one-line core fix:
The
|| "Create failed"keeps the message non-empty when sanitization fully redacts (the sanitizer returns""/[REDACTED]shapes depending on input), matching theagents/credentials/route.tspattern. I left the Zodvalidation.error.messagepath (line 30) alone on purpose: that is a schema-generated message with no runtime data, and every sibling route (audio/speech,classify,embeddings,images/generations) passes it through unchanged.Also checked the other handlers in the file per the issue:
GEThas no catch block andparseBatchListLimit's message is a static string, so the POST catch was the only leak path.Testing
New
tests/unit/14089-batches-route-error-sanitization.test.tsdrives the realPOSThandler with a real API key and a real seeded input file. The harness does not supportmock.module()andmock.methodcannot redefine the sealed ESM namespace export, so the batch INSERT is made to throw through the repo's ownsetDbInstanceDI seam: a thin wrapper delegates every read (auth lookup, file ownership, validation) to the real adapter and only theINSERT INTO batchesstatement throws a path-and-stack-ladenSQLITE_CANTOPEN.storage.sqlitefilename, nor a stack frame (\n at);typestaysinvalid_request_error.Batch endpoint is not enabled on this instance) survives sanitization intact, so the fix does not turn every error into[REDACTED].Red on base: the leak test fails with
response leaked the DB path: SQLITE_CANTOPEN: unable to open database file /tmp/.../storage.sqlite; after the fix, 2 pass.Adjacent regression: all six existing batch test files green (88 pass / 0 fail, incl. the GHSA-2jm2/GHSA-m3hp ownership suite that exercises the same route).
npx eslintandnpx prettier --checkclean on both touched files;npm run typecheck:coreexits 0.