mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-16 11:52:26 +03:00
GHSA-2jm2-mpx8-6523 and GHSA-m3hp-hq9g-fpmv, one root cause.
`getApiKeyRequestScope()` never rejects: with the default REQUIRE_API_KEY=false
the client-api policy admits both a missing and an invalid bearer as anonymous,
and the scope comes back `{ apiKeyId: null, isSessionAuth: false }`. The
`/v1/files` and `/v1/batches` routes then treated "null" as permissive in two
different ways:
- GHSA-m3hp — the list routes coerced `apiKeyId || undefined`, and the DB layer
reads `undefined` as "no owner filter", so an anonymous or invalid-bearer
caller got every tenant's file and batch metadata, the same unfiltered view as
the operator's dashboard.
- GHSA-2jm2 — the single-record checks were `record.apiKeyId !== null && …`, so
a record with no owner short-circuited to "allowed" for any caller: read,
download raw content, delete, cancel, or use as a batch input. Null-owner
records are common — every dashboard-session upload, and every batch output
file inheriting a session batch's owner, which carries model responses.
`api_key_id` has existed since the table was created (migration 028), so a null
owner is not a legacy row; it is an unattributable write. No doc described it
as shared — API_REFERENCE says files are scoped per key — and batch_api.test.ts
pinned the by-id exposure as expected behaviour.
One rule now, in `_helpers/apiKeyScope.ts`:
- `canAccessOwnedRecord(scope, owner)`: a dashboard session is the instance
operator and may act on any record; an API key acts on its own records only;
a null owner is denied to every non-session caller. Applied to files
GET / DELETE / content, batches GET / DELETE / cancel, and the batch-create
input-file check.
- `resolveListScope(scope)`: an explicit union for list/count reads — scoped to
the presented key (a key wins even alongside a session cookie), instance-wide
only for a session without a key, and 401 otherwise, including for a bearer
that does not resolve to a key. There is no default that widens a read.
This follows the GHSA-wvxc shape already used by the delete-completed sweep.
Behaviour change: the anonymous upload → batch → download flow no longer works
without an API key, because a null owner cannot be attributed.
Subsumes #13683: it moved `scopeCheck` into the shared helper so a session can
cancel any batch — kept, and its test ported — but it also kept null-owner
records open on the premise they predate ownership tracking, which migration
028 contradicts.
Tests are red-first. batch_api's by-id case is flipped to 404 with a negative
assertion; batch-deletion-route-logic now imports the real helper instead of a
local copy that had silently diverged from production; the two integration
tests present a real key, since their subject is limits and rate logging, not
auth.
Co-authored-by: Markus Hartung <mail@hartmark.se>