From 73cb408565e8bb30c0ee72152c5da83ac8a5beb7 Mon Sep 17 00:00:00 2001 From: Wynne668 Date: Tue, 14 Jul 2026 13:28:18 +0800 Subject: [PATCH] fix(google-meet): reject nondecimal transcript cursors (#106384) * fix(google-meet): reject nondecimal transcript cursors Use parseStrictNonNegativeInteger for --since validation so hex, exponent, and fractional spellings are rejected at the CLI boundary instead of silently coercing to different cursor offsets. Remove the redundant file header comment to stay within the oversized-file LOC ratchet. * test(google-meet): harden transcript cursor coverage * style(google-meet): format cursor tests --------- Co-authored-by: Peter Steinberger --- extensions/google-meet/src/cli.test.ts | 44 ++++++++++++++++++++++++++ extensions/google-meet/src/cli.ts | 6 ++-- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/extensions/google-meet/src/cli.test.ts b/extensions/google-meet/src/cli.test.ts index 5a638aecce07..d8097255b081 100644 --- a/extensions/google-meet/src/cli.test.ts +++ b/extensions/google-meet/src/cli.test.ts @@ -832,6 +832,50 @@ describe("google-meet CLI", () => { } }); + it.each([ + ["0", 0], + ["3", 3], + ["+3", 3], + [" 3 ", 3], + [String(Number.MAX_SAFE_INTEGER), Number.MAX_SAFE_INTEGER], + ] as const)("accepts base-10 safe transcript cursors: %s", async (since, expected) => { + const callGatewayFromCli = vi.fn(async () => ({ + found: true, + sessionId: "meet_gateway", + startIndex: expected, + nextIndex: expected, + lines: [], + })); + + await setupCli({ callGatewayFromCli }).parseAsync( + ["googlemeet", "transcript", "meet_gateway", "--since", since], + { from: "user" }, + ); + + expect(callGatewayFromCli).toHaveBeenCalledWith( + "googlemeet.transcript", + { json: true, timeout: "5000" }, + { sessionId: "meet_gateway", sinceIndex: expected }, + { progress: false }, + ); + }); + + it.each(["", " ", "-1", "0x10", "0o10", "0b10", "1e0", "1.5", "9007199254740992"])( + "rejects non-decimal transcript cursors before gateway delegation: %s", + async (since) => { + const callGatewayFromCli = vi.fn(); + + await expect( + setupCli({ callGatewayFromCli }).parseAsync( + ["googlemeet", "transcript", "meet_gateway", "--since", since], + { from: "user" }, + ), + ).rejects.toThrow("--since must be a non-negative safe integer"); + + expect(callGatewayFromCli).not.toHaveBeenCalled(); + }, + ); + it("delegates join to the gateway-owned runtime when available", async () => { const callGatewayFromCli = vi.fn(async () => ({ session: { diff --git a/extensions/google-meet/src/cli.ts b/extensions/google-meet/src/cli.ts index efac5d9499e0..1f5a091aceee 100644 --- a/extensions/google-meet/src/cli.ts +++ b/extensions/google-meet/src/cli.ts @@ -1,4 +1,3 @@ -// Google Meet plugin module implements cli behavior. import { mkdir, writeFile } from "node:fs/promises"; import path from "node:path"; import { createInterface } from "node:readline/promises"; @@ -9,6 +8,7 @@ import { expectDefined } from "openclaw/plugin-sdk/expect-runtime"; import { callGatewayFromCli } from "openclaw/plugin-sdk/gateway-runtime"; import { clampTimerTimeoutMs, + parseStrictNonNegativeInteger, parseStrictPositiveInteger, } from "openclaw/plugin-sdk/number-runtime"; import prettyMilliseconds from "pretty-ms"; @@ -2263,8 +2263,8 @@ export function registerGoogleMeetCli(params: { .option("--since ", "Resume from the previous response's nextIndex") .option("--json", "Print JSON output", false) .action(async (sessionId: string, options: { since?: string; json?: boolean }) => { - const sinceIndex = options.since === undefined ? undefined : Number(options.since); - if (sinceIndex !== undefined && (!Number.isSafeInteger(sinceIndex) || sinceIndex < 0)) { + const sinceIndex = parseStrictNonNegativeInteger(options.since); + if (options.since !== undefined && sinceIndex === undefined) { throw new Error("--since must be a non-negative safe integer"); } const delegated = await callGoogleMeetGateway({