Skip to content

Commit 108d6d7

Browse files
committed
fix(gateway): reject malformed restart request params
1 parent 984c8f6 commit 108d6d7

2 files changed

Lines changed: 51 additions & 2 deletions

File tree

src/gateway/server-methods/restart.test.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Restart method tests cover safe restart scheduling, deferral flags, and
22
// response payloads returned by gateway.restart.request.
3-
import { describe, expect, it, vi } from "vitest";
3+
import { beforeEach, describe, expect, it, vi } from "vitest";
44
import { restartHandlers } from "./restart.js";
55

66
const requestSafeGatewayRestart = vi.hoisted(() => vi.fn());
@@ -21,7 +21,7 @@ vi.mock("../../infra/restart-coordinator.js", () => ({
2121
requestSafeGatewayRestart: (opts: unknown) => requestSafeGatewayRestart(opts),
2222
}));
2323

24-
function invokeRestartRequest(params: Record<string, unknown>) {
24+
function invokeRestartRequest(params: unknown) {
2525
const respond = vi.fn();
2626
const handler = restartHandlers["gateway.restart.request"];
2727
return Promise.resolve(
@@ -59,6 +59,10 @@ function expectRestartRequest(skipDeferral: boolean) {
5959
}
6060

6161
describe("gateway.restart.request handler", () => {
62+
beforeEach(() => {
63+
requestSafeGatewayRestart.mockClear();
64+
});
65+
6266
it("defaults to skipDeferral: false when the param is absent", async () => {
6367
mockScheduledRestart({ safe: true, summary: "safe to restart now" });
6468

@@ -90,4 +94,36 @@ describe("gateway.restart.request handler", () => {
9094

9195
expectRestartRequest(false);
9296
});
97+
98+
it("rejects non-object params without scheduling a restart", async () => {
99+
const respond = await invokeRestartRequest("operator");
100+
101+
expect(requestSafeGatewayRestart).not.toHaveBeenCalled();
102+
expect(respond.mock.calls).toEqual([
103+
[
104+
false,
105+
undefined,
106+
{
107+
code: "INVALID_REQUEST",
108+
message: "invalid gateway.restart.request params",
109+
},
110+
],
111+
]);
112+
});
113+
114+
it("rejects array params without scheduling a restart", async () => {
115+
const respond = await invokeRestartRequest([]);
116+
117+
expect(requestSafeGatewayRestart).not.toHaveBeenCalled();
118+
expect(respond.mock.calls).toEqual([
119+
[
120+
false,
121+
undefined,
122+
{
123+
code: "INVALID_REQUEST",
124+
message: "invalid gateway.restart.request params",
125+
},
126+
],
127+
]);
128+
});
93129
});

src/gateway/server-methods/restart.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
// Gateway RPC handlers for safe gateway restart requests and preflight state.
2+
import { ErrorCodes, errorShape } from "../../../packages/gateway-protocol/src/index.js";
23
import {
34
createSafeGatewayRestartPreflight,
45
requestSafeGatewayRestart,
56
} from "../../infra/restart-coordinator.js";
67
import type { GatewayRequestHandlers } from "./types.js";
78

9+
function isRestartRequestParams(value: unknown): value is Record<string, unknown> {
10+
return typeof value === "object" && value !== null && !Array.isArray(value);
11+
}
12+
813
function normalizeReason(value: unknown): string | undefined {
914
// Restart reasons are operator-visible log context, not payload storage.
1015
// Trim and cap them before passing through to the coordinator.
@@ -20,6 +25,14 @@ function normalizeSkipDeferral(value: unknown): boolean {
2025
/** Gateway request handlers for safe restart coordination. */
2126
export const restartHandlers: GatewayRequestHandlers = {
2227
"gateway.restart.request": async ({ respond, params }) => {
28+
if (!isRestartRequestParams(params)) {
29+
respond(
30+
false,
31+
undefined,
32+
errorShape(ErrorCodes.INVALID_REQUEST, "invalid gateway.restart.request params"),
33+
);
34+
return;
35+
}
2336
const result = requestSafeGatewayRestart({
2437
reason: normalizeReason(params.reason),
2538
delayMs: 0,

0 commit comments

Comments
 (0)