mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-28 13:26:04 -06:00
fix(cli): validate webhooks gmail --tailscale mode at parse time (#120272)
* fix(cli): validate webhooks gmail --tailscale mode at parse time --tailscale <mode> was stored unchecked and cast to the tailscale union type, so a typo like --tailscale offf (truthy and !== "off") was treated as enabled and only failed deep inside tailscale setup with a raw CLI usage error. Reject modes other than funnel|serve|off in parseGmailCommonOptions, mirroring the strict numberOption pattern, and export the setup/run parsers for direct testing. * fix(cli): reject blank Gmail tailscale modes * fix(cli): preserve tailscale mode type --------- Co-authored-by: Dallin Romney <dallinromney@gmail.com>
This commit is contained in:
@@ -61,4 +61,57 @@ describe("webhooks cli", () => {
|
||||
expect(mocks.runGmailSetup).not.toHaveBeenCalled();
|
||||
expect(mocks.runGmailService).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each([
|
||||
["setup", "offf"],
|
||||
["setup", ""],
|
||||
["setup", " "],
|
||||
["run", "offf"],
|
||||
["run", ""],
|
||||
["run", " "],
|
||||
])("rejects invalid gmail %s --tailscale mode %j", async (command, mode) => {
|
||||
const program = createProgram();
|
||||
const args =
|
||||
command === "setup"
|
||||
? ["webhooks", "gmail", command, "--account", "default", "--tailscale", mode]
|
||||
: ["webhooks", "gmail", command, "--tailscale", mode];
|
||||
|
||||
await expect(program.parseAsync(args, { from: "user" })).rejects.toThrow("__exit__:1");
|
||||
|
||||
expect(runtimeErrors().join("\n")).toContain(
|
||||
"Invalid --tailscale (must be funnel, serve, or off).",
|
||||
);
|
||||
expect(mocks.runGmailSetup).not.toHaveBeenCalled();
|
||||
expect(mocks.runGmailService).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each([
|
||||
["setup", "funnel"],
|
||||
["setup", "serve"],
|
||||
["setup", "off"],
|
||||
["run", "funnel"],
|
||||
["run", "serve"],
|
||||
["run", "off"],
|
||||
])("accepts valid gmail %s --tailscale %s", async (command, mode) => {
|
||||
const program = createProgram();
|
||||
const args =
|
||||
command === "setup"
|
||||
? ["webhooks", "gmail", command, "--account", "default", "--tailscale", mode]
|
||||
: ["webhooks", "gmail", command, "--tailscale", mode];
|
||||
|
||||
await program.parseAsync(args, { from: "user" });
|
||||
|
||||
const runner = command === "setup" ? mocks.runGmailSetup : mocks.runGmailService;
|
||||
expect(runner).toHaveBeenCalledWith(expect.objectContaining({ tailscale: mode }));
|
||||
});
|
||||
|
||||
it("preserves an omitted gmail run --tailscale mode", async () => {
|
||||
const program = createProgram();
|
||||
|
||||
await program.parseAsync(["webhooks", "gmail", "run"], { from: "user" });
|
||||
|
||||
expect(mocks.runGmailService).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ tailscale: undefined }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
+13
-2
@@ -149,7 +149,7 @@ function parseGmailCommonOptions(raw: Record<string, unknown>) {
|
||||
includeBody: booleanOption(raw.includeBody),
|
||||
maxBytes: numberOption(raw.maxBytes, "--max-bytes"),
|
||||
renewEveryMinutes: numberOption(raw.renewMinutes, "--renew-minutes"),
|
||||
tailscaleRaw: normalizeOptionalString(raw.tailscale),
|
||||
tailscale: tailscaleModeOption(raw.tailscale),
|
||||
tailscalePath: normalizeOptionalString(raw.tailscalePath),
|
||||
tailscaleTarget: normalizeOptionalString(raw.tailscaleTarget),
|
||||
};
|
||||
@@ -171,12 +171,23 @@ function gmailOptionsFromCommon(
|
||||
includeBody: common.includeBody,
|
||||
maxBytes: common.maxBytes,
|
||||
renewEveryMinutes: common.renewEveryMinutes,
|
||||
tailscale: common.tailscaleRaw as GmailRunOptions["tailscale"],
|
||||
tailscale: common.tailscale,
|
||||
tailscalePath: common.tailscalePath,
|
||||
tailscaleTarget: common.tailscaleTarget,
|
||||
};
|
||||
}
|
||||
|
||||
function tailscaleModeOption(value: unknown): GmailRunOptions["tailscale"] {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
const mode = normalizeOptionalString(value);
|
||||
if (mode === "funnel" || mode === "serve" || mode === "off") {
|
||||
return mode;
|
||||
}
|
||||
throw new Error("Invalid --tailscale (must be funnel, serve, or off).");
|
||||
}
|
||||
|
||||
function numberOption(value: unknown, label: string): number | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
|
||||
Reference in New Issue
Block a user