


























1+// Google Meet node.invoke policy tests cover caller-controlled command sanitization.
2+import type { OpenClawPluginNodeInvokePolicyContext } from "openclaw/plugin-sdk/plugin-entry";
3+import { describe, expect, it, vi } from "vitest";
4+import { resolveGoogleMeetConfig } from "./config.js";
5+import {
6+createGoogleMeetChromeNodeInvokePolicy,
7+GOOGLE_MEET_CHROME_NODE_COMMAND,
8+} from "./node-invoke-policy.js";
9+10+function createContext(params: unknown, pluginConfig: Record<string, unknown> = {}) {
11+const invokeNode = vi.fn<OpenClawPluginNodeInvokePolicyContext["invokeNode"]>(async () => ({
12+ok: true,
13+payload: { ok: true },
14+}));
15+const ctx: OpenClawPluginNodeInvokePolicyContext = {
16+nodeId: "node-1",
17+command: GOOGLE_MEET_CHROME_NODE_COMMAND,
18+ params,
19+config: {} as never,
20+ pluginConfig,
21+ invokeNode,
22+};
23+return { ctx, invokeNode };
24+}
25+26+describe("Google Meet node invoke policy", () => {
27+it("rewrites start executable fields from trusted config", async () => {
28+const policy = createGoogleMeetChromeNodeInvokePolicy(
29+resolveGoogleMeetConfig({
30+chrome: {
31+launch: false,
32+browserProfile: "Trusted Profile",
33+joinTimeoutMs: 45_000,
34+audioInputCommand: ["trusted-capture", "--raw"],
35+audioOutputCommand: ["trusted-play", "--raw"],
36+},
37+}),
38+);
39+const { ctx, invokeNode } = createContext({
40+action: "start",
41+url: "https://meet.google.com/abc-defg-hij",
42+mode: "bidi",
43+launch: true,
44+browserProfile: "Attacker Profile",
45+joinTimeoutMs: 1,
46+audioBridgeCommand: ["node", "-e", "process.exit(99)"],
47+audioBridgeHealthCommand: ["node", "-e", "process.exit(98)"],
48+audioInputCommand: ["malicious-capture"],
49+audioOutputCommand: ["malicious-play"],
50+});
51+52+await expect(policy.handle(ctx)).resolves.toEqual({ ok: true, payload: { ok: true } });
53+54+expect(invokeNode).toHaveBeenCalledTimes(1);
55+expect(invokeNode).toHaveBeenCalledWith({
56+params: {
57+action: "start",
58+url: "https://meet.google.com/abc-defg-hij",
59+mode: "bidi",
60+launch: false,
61+browserProfile: "Trusted Profile",
62+joinTimeoutMs: 45_000,
63+audioInputCommand: ["trusted-capture", "--raw"],
64+audioOutputCommand: ["trusted-play", "--raw"],
65+},
66+});
67+});
68+69+it("uses trusted configured external bridge commands for start", async () => {
70+const policy = createGoogleMeetChromeNodeInvokePolicy(
71+resolveGoogleMeetConfig({
72+chrome: {
73+audioBridgeHealthCommand: ["trusted-bridge", "status"],
74+audioBridgeCommand: ["trusted-bridge", "start"],
75+},
76+}),
77+);
78+const { ctx, invokeNode } = createContext({
79+action: "start",
80+url: "https://meet.google.com/abc-defg-hij",
81+mode: "bidi",
82+audioBridgeHealthCommand: ["node", "-e", "process.exit(98)"],
83+audioBridgeCommand: ["node", "-e", "process.exit(99)"],
84+});
85+86+await policy.handle(ctx);
87+88+const call = invokeNode.mock.calls[0]?.[0];
89+expect(call?.params).toMatchObject({
90+action: "start",
91+audioBridgeHealthCommand: ["trusted-bridge", "status"],
92+audioBridgeCommand: ["trusted-bridge", "start"],
93+});
94+});
95+96+it("rejects direct start for non-Meet URLs before node dispatch", async () => {
97+const policy = createGoogleMeetChromeNodeInvokePolicy(resolveGoogleMeetConfig({}));
98+const { ctx, invokeNode } = createContext({
99+action: "start",
100+url: "https://example.com/private",
101+mode: "bidi",
102+});
103+104+await expect(policy.handle(ctx)).resolves.toMatchObject({
105+ok: false,
106+code: "GOOGLE_MEET_NODE_POLICY_DENIED",
107+message: "url must be an explicit https://meet.google.com/... URL",
108+});
109+expect(invokeNode).not.toHaveBeenCalled();
110+});
111+112+it("keeps direct setup diagnostics but strips extra fields", async () => {
113+const policy = createGoogleMeetChromeNodeInvokePolicy(resolveGoogleMeetConfig({}));
114+const { ctx, invokeNode } = createContext({
115+action: "setup",
116+audioBridgeCommand: ["node", "-e", "process.exit(99)"],
117+});
118+119+await policy.handle(ctx);
120+121+expect(invokeNode).toHaveBeenCalledWith({ params: { action: "setup" } });
122+});
123+124+it("rejects unsupported googlemeet.chrome actions before node dispatch", async () => {
125+const policy = createGoogleMeetChromeNodeInvokePolicy(resolveGoogleMeetConfig({}));
126+const { ctx, invokeNode } = createContext({ action: "exec", command: ["id"] });
127+128+await expect(policy.handle(ctx)).resolves.toMatchObject({
129+ok: false,
130+code: "GOOGLE_MEET_NODE_POLICY_DENIED",
131+});
132+expect(invokeNode).not.toHaveBeenCalled();
133+});
134+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。