From 32f76a2f20d602a845cafe2904a4fab80aeffb9f Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN Date: Wed, 19 Aug 2026 23:59:16 -0400 Subject: [PATCH] Cover every owner-op client method's denial, completing the agent sweep Extends the denied-request test to a table over all nine owner-op methods rather than the three that happened to have surviving mutants, so a method added later is covered the day it appears. Each one's dropped check produces a different false success: shutdown() -> void, reading as "the daemon stopped" while it is still running and still holding seeds. The test now pings the socket afterwards to prove it did not. status() -> [], reading as "no profiles are held" tokenIssue() -> the STRING "undefined" handed back as a token tokenRevoke()-> void, "token revoked", while the token stays live tokenList() -> undefined put() -> NaN agent.ts now sweeps to zero survivors across all 86 mutants, up from 39 under the old vocabulary rule. Every mutation target is clean: 234 mutants, none surviving. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Vtx94nXDmNgXEeThUZP4QN --- js/packages/cli/test/agentSecurity.test.ts | 29 +++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/js/packages/cli/test/agentSecurity.test.ts b/js/packages/cli/test/agentSecurity.test.ts index 82c7e88a..e538bbc8 100644 --- a/js/packages/cli/test/agentSecurity.test.ts +++ b/js/packages/cli/test/agentSecurity.test.ts @@ -732,9 +732,32 @@ describe("a denied request is not a quiet zero", () => { process.env["SEEDPASS_AGENT_CAP"] = "not-the-capability"; try { const client = new AgentClient(sock); - await expect(client.lock()).rejects.toThrow(/owner capability/); - await expect(client.highRiskLock(FINGERPRINT)).rejects.toThrow(/owner capability/); - await expect(client.highRiskStatus(FINGERPRINT)).rejects.toThrow(/owner capability/); + // Every owner-op method, so a new one added later is covered too. + const calls: [string, () => Promise][] = [ + ["lock", () => client.lock()], + ["highRiskLock", () => client.highRiskLock(FINGERPRINT)], + ["highRiskStatus", () => client.highRiskStatus(FINGERPRINT)], + // shutdown() returns void, so a dropped check reads as "the daemon + // stopped" while it is still running and still holding seeds. + ["shutdown", () => client.shutdown()], + // status() falls back to [], which reads as "no profiles are held". + ["status", () => client.status()], + // tokenIssue() would hand back the STRING "undefined" as a token. + ["tokenIssue", () => client.tokenIssue({ fingerprint: FINGERPRINT, scopes: ["read"] })], + ["tokenList", () => client.tokenList(FINGERPRINT)], + // tokenRevoke() returns void: "token revoked", while it stays live. + ["tokenRevoke", () => client.tokenRevoke("some-token-id")], + ["put", () => client.put(FINGERPRINT, MNEMONIC, 900)], + ]; + for (const [name, call] of calls) { + await expect(call(), `${name} must throw when denied`).rejects.toThrow( + /owner capability/, + ); + } + + // The daemon is genuinely still up — proof that `shutdown` above did + // not quietly succeed while reporting nothing. + expect(await AgentClient.ping(sock)).toBe(true); } finally { if (previous === undefined) delete process.env["SEEDPASS_AGENT_CAP"]; else process.env["SEEDPASS_AGENT_CAP"] = previous;