mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-16 11:52:26 +03:00
`DELETE /api/v1/batches/delete-completed` accepted any valid ordinary
inference key — including one with `scopes: []` — and then called
`deleteCompletedBatches()` with no ownership predicate. The helper ran
DELETE FROM batches WHERE status = 'completed'
instance-wide, and passed every referenced file through `deleteFile()`,
which nulls `content`. One tenant could therefore destroy every other
tenant's completed batches and their stored file contents, with no victim
batch id, file id or key id needed (GHSA-wvxc-jp3v-5mg5, CWE-862).
Every sibling operation already keeps this boundary: `listBatches` and
`countBatches` take an optional `apiKeyId` and scope the SQL to
`api_key_id = ?`, falling back to instance-wide only when the caller is an
authenticated dashboard session. `deleteCompletedBatches` was the one
operation that dropped it.
The fix follows that same shape rather than inventing a new one: the helper
takes an optional `apiKeyId` and appends `AND api_key_id = ?` to the file
collection, the checkpoint delete and the batch delete; the route passes
`scope.apiKeyId || undefined`, so a dashboard session keeps the
instance-wide sweep the UI relies on and an API key only ever clears its
own batches.
Regression guard: tests/unit/batches-delete-completed-ownership-wvxc.test.ts
pins all three halves of the contract — a foreign key's batch and file
survive, a non-completed batch is never swept, and the session-wide sweep
still clears every key. The first assertion fails on the pre-fix helper.