diff --git a/src/gateway/server.device-token-rotate-authz.test.ts b/src/gateway/server.device-token-rotate-authz.test.ts index 554dd5f3d3d..9b02f80770a 100644 --- a/src/gateway/server.device-token-rotate-authz.test.ts +++ b/src/gateway/server.device-token-rotate-authz.test.ts @@ -128,7 +128,7 @@ async function issuePairingScopedTokenForAdminApprovedDevice(name: string): Prom } describe("gateway device.token.rotate/revoke ownership guard (IDOR)", () => { - test("rejects a device-token caller rotating another device's token", async () => { + test("rejects a device-token caller rotating or revoking another device's token", async () => { const started = await startServer("secret"); const deviceA = await issuePairingScopedTokenForAdminApprovedDevice("idor-device-a"); const deviceB = await issuePairingScopedTokenForAdminApprovedDevice("idor-device-b"); @@ -151,6 +151,16 @@ describe("gateway device.token.rotate/revoke ownership guard (IDOR)", () => { const pairedB = await getPairedDevice(deviceB.deviceId); expect(pairedB?.tokens?.operator?.token).toBe(deviceB.pairingToken); + + const revoke = await rpcReq(pairingWs, "device.token.revoke", { + deviceId: deviceB.deviceId, + role: "operator", + }); + expect(revoke.ok).toBe(false); + expect(revoke.error?.message).toBe("device token revocation denied"); + + const pairedBAfterRevoke = await getPairedDevice(deviceB.deviceId); + expect(pairedBAfterRevoke?.tokens?.operator?.revokedAtMs).toBeUndefined(); } finally { pairingWs?.close(); await started.server.close(); @@ -158,9 +168,9 @@ describe("gateway device.token.rotate/revoke ownership guard (IDOR)", () => { } }); - test("allows an admin-scoped caller to rotate another device's token", async () => { + test("allows an admin-scoped caller to rotate and revoke another device's token", async () => { const started = await startServerWithClient("secret"); - const device = await issuePairingScopedTokenForAdminApprovedDevice("idor-admin-rotate"); + const device = await issuePairingScopedTokenForAdminApprovedDevice("idor-admin-rotate-revoke"); try { await connectOk(started.ws); @@ -172,48 +182,6 @@ describe("gateway device.token.rotate/revoke ownership guard (IDOR)", () => { }); expect(rotate.ok).toBe(true); expect(rotate.payload?.token).toBeTruthy(); - } finally { - started.ws.close(); - await started.server.close(); - started.envSnapshot.restore(); - } - }); - - test("rejects a device-token caller revoking another device's token", async () => { - const started = await startServer("secret"); - const deviceA = await issuePairingScopedTokenForAdminApprovedDevice("idor-revoke-a"); - const deviceB = await issuePairingScopedTokenForAdminApprovedDevice("idor-revoke-b"); - - let pairingWs: WebSocket | undefined; - try { - pairingWs = await connectPairingScopedOperator({ - port: started.port, - identityPath: deviceA.identityPath, - deviceToken: deviceA.pairingToken, - }); - - const revoke = await rpcReq(pairingWs, "device.token.revoke", { - deviceId: deviceB.deviceId, - role: "operator", - }); - expect(revoke.ok).toBe(false); - expect(revoke.error?.message).toBe("device token revocation denied"); - - const pairedB = await getPairedDevice(deviceB.deviceId); - expect(pairedB?.tokens?.operator?.revokedAtMs).toBeUndefined(); - } finally { - pairingWs?.close(); - await started.server.close(); - started.envSnapshot.restore(); - } - }); - - test("allows an admin-scoped caller to revoke another device's token", async () => { - const started = await startServerWithClient("secret"); - const device = await issuePairingScopedTokenForAdminApprovedDevice("idor-admin-revoke"); - - try { - await connectOk(started.ws); const revoke = await rpcReq<{ revokedAtMs?: number }>(started.ws, "device.token.revoke", { deviceId: device.deviceId,