diff --git a/changelog.d/fixes/12969-batches-delete-completed-ownership.md b/changelog.d/fixes/12969-batches-delete-completed-ownership.md index da4081d555..e602446524 100644 --- a/changelog.d/fixes/12969-batches-delete-completed-ownership.md +++ b/changelog.d/fixes/12969-batches-delete-completed-ownership.md @@ -1 +1 @@ -- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure and an atomic sweep so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) diff --git a/src/lib/db/batches.ts b/src/lib/db/batches.ts index bcdf31ed1a..b981c511d0 100644 --- a/src/lib/db/batches.ts +++ b/src/lib/db/batches.ts @@ -421,6 +421,9 @@ export function deleteBatch(id: string): boolean { */ export type DeleteCompletedBatchesScope = { apiKeyId: string } | { allTenants: true }; +/** Instance-wide sweeps commit in chunks of this many batches (SEC-D). */ +export const INSTANCE_SWEEP_CHUNK = 200; + /** * Delete completed batches and the files they reference. * @@ -457,9 +460,6 @@ export type DeleteCompletedBatchesScope = { apiKeyId: string } | { allTenants: t * list is bounded by that key's completed batches — should a single key ever * own more than ~32k completed batches, chunk key mode the same way. */ -/** Instance-wide sweeps commit in chunks of this many batches (SEC-D). */ -export const INSTANCE_SWEEP_CHUNK = 200; - export function deleteCompletedBatches(scope: DeleteCompletedBatchesScope): { deletedBatches: number; deletedFiles: number; diff --git a/tests/unit/batches-delete-completed-route-scope.test.ts b/tests/unit/batches-delete-completed-route-scope.test.ts index 5fdf9c7755..421db2064d 100644 --- a/tests/unit/batches-delete-completed-route-scope.test.ts +++ b/tests/unit/batches-delete-completed-route-scope.test.ts @@ -49,7 +49,7 @@ const ROUTE_URL = "http://localhost/api/v1/batches/delete-completed"; async function sessionCookie(): Promise { const secret = new TextEncoder().encode(process.env.JWT_SECRET); - const jwt = await new SignJWT({ sub: "admin" }) + const jwt = await new SignJWT({ authenticated: true, sub: "admin" }) .setProtectedHeader({ alg: "HS256" }) .setExpirationTime("1h") .sign(secret);