From b01c9e42b9ee479419dd004095a24b7d8a8906e2 Mon Sep 17 00:00:00 2001 From: Michael Nguyen Date: Thu, 23 Jul 2026 11:44:40 -0700 Subject: [PATCH] test(plugins): cover the never-a-wildcard invariant for proactive scope resolution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes the Greptile P2 on #10103: referencedCompanyId's null path had no coverage. Two cases, one per derivation branch: - direct-companyId branch: a proactive call referencing no company reaches the gate scope-less (handler sees no injected companyId) - scopeKind:company branch: empty scopeId resolves to null, not to an authorized company — denied with 'company context is required', proving no grant occurred (a wrong grant would fail with a different message) Refs LOOA-629 / PR #10103 (LOOA-693 security review flagged the same gap). Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01J4onvauR8cQq9uo9iJbcZ7 --- .../__tests__/plugin-worker-manager.test.ts | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/server/src/__tests__/plugin-worker-manager.test.ts b/server/src/__tests__/plugin-worker-manager.test.ts index 0100af5f083..f84f3eefcb7 100644 --- a/server/src/__tests__/plugin-worker-manager.test.ts +++ b/server/src/__tests__/plugin-worker-manager.test.ts @@ -617,6 +617,51 @@ describe("plugin proactive company scope (LOOA-629)", () => { } }); + it("never injects a scope into a call that references no company (direct-companyId branch)", async () => { + // The "never a wildcard" invariant (#10103 review gap), direct-companyId + // derivation branch: a proactive call with no company reference must reach + // the gate scope-less. The gate classifies it as instance-level + // (kind "none") and passes params through verbatim — so the handler seeing + // NO companyId proves the manager did not inject one of its authorized + // companies into the call. + const { handle, companiesGet } = makeHandle(); + try { + await handle.start(); + handle.setProactiveCompanyScopes(["company-1"]); + await handle.call("getData", { + params: { mode: "omit", hostMethod: "companies.get" }, + } as unknown as HostToWorkerMethods["getData"][0]); + expect(companiesGet).toHaveBeenCalledTimes(1); + const receivedParams = companiesGet.mock.calls[0]?.[0] as Record | undefined; + expect(receivedParams?.companyId).toBeUndefined(); + } finally { + await handle.stop().catch(() => undefined); + } + }); + + it("never grants a wildcard: company-scoped state.get with an empty scopeId stays denied scope-less", async () => { + // Same invariant via the scopeKind:"company" derivation branch — an empty + // scopeId must resolve to null (no proactive grant). The gate then + // escalates the empty-scopeId request to kind "all", which is denied. The + // message assertion is load-bearing: "company context is required" proves + // NO scope was granted, whereas a wrongly-injected grant would fail with + // 'the current invocation is scoped to company "company-1"' instead. + const { handle, stateGet } = makeHandle(); + try { + await handle.start(); + handle.setProactiveCompanyScopes(["company-1"]); + await expect(handle.call("getData", { + params: { mode: "omit", hostMethod: "state.get" }, + } as unknown as HostToWorkerMethods["getData"][0])).rejects.toMatchObject({ + code: PLUGIN_RPC_ERROR_CODES.INVOCATION_SCOPE_DENIED, + message: expect.stringContaining("company context is required"), + }); + expect(stateGet).not.toHaveBeenCalled(); + } finally { + await handle.stop().catch(() => undefined); + } + }); + it("revokes proactive access when the authorized set is cleared", async () => { const { handle, companiesGet } = makeHandle(); try {