mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-20 22:02:19 +03:00
Follow-up to GHSA-6c7w-56xp-wpc6. `deduplicate()` shares ONE upstream call — and therefore one response object — between every concurrent caller landing on the same hash. `computeRequestHash()` canonicalized model, prompt, system and sampling params: what was being asked, but never who was asking. So two distinct OmniRoute API keys issuing the same request joined the same in-flight promise. The response was produced with the initiator's provider connection, under the initiator's per-key policy (allowedConnections / allowedModels / disableNonPublicModels), billed to the initiator, and handed to a different authenticated principal. Dedup is an optimization; it must not be a hole in the auth boundary. #10438 fixed the *prompt* half of this class — translated bodies whose prompt lives under `input`/`contents` hashed as `null`, so different prompts collided. This is the *identity* half, which that fix did not touch. The tenant id is a PLAINTEXT prefix rather than digest input, matching semanticCache.generateSignature (#3740): it is an internal namespace key, not a credential, and keeping it out of the digest avoids a false-positive CodeQL js/insufficient-password-hash on a dedup key. Omitting it keeps the un-namespaced hash: keyless local-first deployments have no tenant boundary to preserve and would otherwise silently lose dedup. That is pinned by a test, not left to chance. tests/unit/request-dedup-tenant-isolation.test.ts — 6 tests, 5 red before the fix: two keys do not share a hash, the same key still dedups, two concurrent identical requests each get their own response, the anonymous hash is unchanged, the namespace prefix cannot be forged from the body, and chatCore actually passes the key id through.