fix(gateway): classify pairing rejection events

This commit is contained in:
Vincent Koc
2026-06-17 12:45:22 +08:00
parent 81df1b239b
commit 3c64a575dd
2 changed files with 25 additions and 4 deletions
+22 -1
View File
@@ -1193,8 +1193,13 @@ describe("deviceHandlers", () => {
}),
},
);
const captured = captureSecurityEvents();
await deviceHandlers["device.pair.reject"](opts);
try {
await deviceHandlers["device.pair.reject"](opts);
} finally {
captured.stop();
}
expect(rejectDevicePairingMock).toHaveBeenCalledWith("req-2");
expect(opts.respond).toHaveBeenCalledWith(
@@ -1202,5 +1207,21 @@ describe("deviceHandlers", () => {
{ requestId: "req-2", deviceId: "device-2", rejectedAtMs: 456 },
undefined,
);
expect(captured.events).toHaveLength(1);
expect(captured.events[0]).toMatchObject({
action: "device.pairing.rejected",
outcome: "success",
severity: "low",
actor: {
kind: "operator",
role: "admin",
},
target: { kind: "device", idHash: expect.stringMatching(/^sha256:[a-f0-9]{12}$/u) },
policy: { id: "gateway.device-pairing", decision: "allow" },
control: { id: "device.pair.reject", family: "auth" },
});
const serialized = JSON.stringify(captured.events);
expect(serialized).not.toContain("device-1");
expect(serialized).not.toContain("device-2");
});
});
+3 -3
View File
@@ -256,7 +256,7 @@ function emitDevicePairingDeniedSecurityEvent(params: {
}
function emitDevicePairingLifecycleSecurityEvent(params: {
action: "device.pairing.approved" | "device.pairing.removed";
action: "device.pairing.approved" | "device.pairing.rejected" | "device.pairing.removed";
severity: DiagnosticSecurityEventInput["severity"];
authz: DeviceSessionAuthz;
targetDeviceId: string;
@@ -513,11 +513,11 @@ export const deviceHandlers: GatewayRequestHandlers = {
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "unknown requestId"));
return;
}
emitDevicePairingDeniedSecurityEvent({
emitDevicePairingLifecycleSecurityEvent({
action: "device.pairing.rejected",
authz,
targetDeviceId: rejected.deviceId,
controlId: "device.pair.reject",
reason: "operator-rejected",
severity: "low",
});
context.broadcast(