feat: add sandbox browser control allowlists
This commit is contained in:
@@ -19,7 +19,7 @@
|
|||||||
## 2026.1.11-1
|
## 2026.1.11-1
|
||||||
|
|
||||||
### New Features and Changes
|
### New Features and Changes
|
||||||
- Agents/Browser: add `browser.target` (sandbox/host/custom) with sandbox host-control gating via `agents.defaults.sandbox.browser.allowHostControl`, and expand browser tool docs (remote control, profiles, internals).
|
- Agents/Browser: add `browser.target` (sandbox/host/custom) with sandbox host-control gating via `agents.defaults.sandbox.browser.allowHostControl`, allowlists for custom control URLs/hosts/ports, and expand browser tool docs (remote control, profiles, internals).
|
||||||
|
|
||||||
## 2026.1.10-4
|
## 2026.1.10-4
|
||||||
|
|
||||||
|
|||||||
@@ -1377,6 +1377,9 @@ Legacy: `perSession` is still supported (`true` → `scope: "session"`,
|
|||||||
headless: false,
|
headless: false,
|
||||||
enableNoVnc: true,
|
enableNoVnc: true,
|
||||||
allowHostControl: false,
|
allowHostControl: false,
|
||||||
|
allowedControlUrls: ["http://10.0.0.42:18791"],
|
||||||
|
allowedControlHosts: ["browser.lab.local", "10.0.0.42"],
|
||||||
|
allowedControlPorts: [18791],
|
||||||
autoStart: true,
|
autoStart: true,
|
||||||
autoStartTimeoutMs: 12000
|
autoStartTimeoutMs: 12000
|
||||||
},
|
},
|
||||||
@@ -1424,6 +1427,11 @@ sandboxed sessions to explicitly target the **host** browser control server
|
|||||||
via the browser tool (`target: "host"`). Leave this off if you want strict
|
via the browser tool (`target: "host"`). Leave this off if you want strict
|
||||||
sandbox isolation.
|
sandbox isolation.
|
||||||
|
|
||||||
|
Allowlists for remote control:
|
||||||
|
- `allowedControlUrls`: exact control URLs permitted for `target: "custom"`.
|
||||||
|
- `allowedControlHosts`: hostnames permitted (hostname only, no port).
|
||||||
|
- `allowedControlPorts`: ports permitted (defaults: http=80, https=443).
|
||||||
|
|
||||||
### `models` (custom providers + base URLs)
|
### `models` (custom providers + base URLs)
|
||||||
|
|
||||||
Clawdbot uses the **pi-coding-agent** model catalog. You can add custom providers
|
Clawdbot uses the **pi-coding-agent** model catalog. You can add custom providers
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ and process access when the model does something dumb.
|
|||||||
- By default, the sandbox browser auto-starts (ensures CDP is reachable) when the browser tool needs it.
|
- By default, the sandbox browser auto-starts (ensures CDP is reachable) when the browser tool needs it.
|
||||||
Configure via `agents.defaults.sandbox.browser.autoStart` and `agents.defaults.sandbox.browser.autoStartTimeoutMs`.
|
Configure via `agents.defaults.sandbox.browser.autoStart` and `agents.defaults.sandbox.browser.autoStartTimeoutMs`.
|
||||||
- `agents.defaults.sandbox.browser.allowHostControl` lets sandboxed sessions target the host browser explicitly.
|
- `agents.defaults.sandbox.browser.allowHostControl` lets sandboxed sessions target the host browser explicitly.
|
||||||
|
- Optional allowlists gate `target: "custom"`: `allowedControlUrls`, `allowedControlHosts`, `allowedControlPorts`.
|
||||||
|
|
||||||
Not sandboxed:
|
Not sandboxed:
|
||||||
- The Gateway process itself.
|
- The Gateway process itself.
|
||||||
|
|||||||
@@ -244,5 +244,6 @@ How it maps:
|
|||||||
- `controlUrl` sets `target: "custom"` implicitly (remote control server).
|
- `controlUrl` sets `target: "custom"` implicitly (remote control server).
|
||||||
- In sandboxed sessions, `target: "host"` requires `agents.defaults.sandbox.browser.allowHostControl=true`.
|
- In sandboxed sessions, `target: "host"` requires `agents.defaults.sandbox.browser.allowHostControl=true`.
|
||||||
- If `target` is omitted: sandboxed sessions default to `sandbox`, non-sandbox sessions default to `host`.
|
- If `target` is omitted: sandboxed sessions default to `sandbox`, non-sandbox sessions default to `host`.
|
||||||
|
- Sandbox allowlists can restrict `target: "custom"` to specific URLs/hosts/ports.
|
||||||
|
|
||||||
This keeps the agent deterministic and avoids brittle selectors.
|
This keeps the agent deterministic and avoids brittle selectors.
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ import { createSessionsSpawnTool } from "./tools/sessions-spawn-tool.js";
|
|||||||
export function createClawdbotTools(options?: {
|
export function createClawdbotTools(options?: {
|
||||||
browserControlUrl?: string;
|
browserControlUrl?: string;
|
||||||
allowHostBrowserControl?: boolean;
|
allowHostBrowserControl?: boolean;
|
||||||
|
allowedControlUrls?: string[];
|
||||||
|
allowedControlHosts?: string[];
|
||||||
|
allowedControlPorts?: number[];
|
||||||
agentSessionKey?: string;
|
agentSessionKey?: string;
|
||||||
agentProvider?: GatewayMessageProvider;
|
agentProvider?: GatewayMessageProvider;
|
||||||
agentAccountId?: string;
|
agentAccountId?: string;
|
||||||
@@ -41,6 +44,9 @@ export function createClawdbotTools(options?: {
|
|||||||
createBrowserTool({
|
createBrowserTool({
|
||||||
defaultControlUrl: options?.browserControlUrl,
|
defaultControlUrl: options?.browserControlUrl,
|
||||||
allowHostControl: options?.allowHostBrowserControl,
|
allowHostControl: options?.allowHostBrowserControl,
|
||||||
|
allowedControlUrls: options?.allowedControlUrls,
|
||||||
|
allowedControlHosts: options?.allowedControlHosts,
|
||||||
|
allowedControlPorts: options?.allowedControlPorts,
|
||||||
}),
|
}),
|
||||||
createCanvasTool(),
|
createCanvasTool(),
|
||||||
createNodesTool(),
|
createNodesTool(),
|
||||||
|
|||||||
@@ -480,6 +480,9 @@ type EmbeddedSandboxInfo = {
|
|||||||
browserControlUrl?: string;
|
browserControlUrl?: string;
|
||||||
browserNoVncUrl?: string;
|
browserNoVncUrl?: string;
|
||||||
hostBrowserAllowed?: boolean;
|
hostBrowserAllowed?: boolean;
|
||||||
|
allowedControlUrls?: string[];
|
||||||
|
allowedControlHosts?: string[];
|
||||||
|
allowedControlPorts?: number[];
|
||||||
elevated?: {
|
elevated?: {
|
||||||
allowed: boolean;
|
allowed: boolean;
|
||||||
defaultLevel: "on" | "off";
|
defaultLevel: "on" | "off";
|
||||||
@@ -572,6 +575,9 @@ export function buildEmbeddedSandboxInfo(
|
|||||||
browserControlUrl: sandbox.browser?.controlUrl,
|
browserControlUrl: sandbox.browser?.controlUrl,
|
||||||
browserNoVncUrl: sandbox.browser?.noVncUrl,
|
browserNoVncUrl: sandbox.browser?.noVncUrl,
|
||||||
hostBrowserAllowed: sandbox.browserAllowHostControl,
|
hostBrowserAllowed: sandbox.browserAllowHostControl,
|
||||||
|
allowedControlUrls: sandbox.browserAllowedControlUrls,
|
||||||
|
allowedControlHosts: sandbox.browserAllowedControlHosts,
|
||||||
|
allowedControlPorts: sandbox.browserAllowedControlPorts,
|
||||||
...(elevatedAllowed
|
...(elevatedAllowed
|
||||||
? {
|
? {
|
||||||
elevated: {
|
elevated: {
|
||||||
|
|||||||
@@ -627,6 +627,9 @@ export function createClawdbotCodingTools(options?: {
|
|||||||
...createClawdbotTools({
|
...createClawdbotTools({
|
||||||
browserControlUrl: sandbox?.browser?.controlUrl,
|
browserControlUrl: sandbox?.browser?.controlUrl,
|
||||||
allowHostBrowserControl: sandbox ? sandbox.browserAllowHostControl : true,
|
allowHostBrowserControl: sandbox ? sandbox.browserAllowHostControl : true,
|
||||||
|
allowedControlUrls: sandbox?.browserAllowedControlUrls,
|
||||||
|
allowedControlHosts: sandbox?.browserAllowedControlHosts,
|
||||||
|
allowedControlPorts: sandbox?.browserAllowedControlPorts,
|
||||||
agentSessionKey: options?.sessionKey,
|
agentSessionKey: options?.sessionKey,
|
||||||
agentProvider: resolveGatewayMessageProvider(options?.messageProvider),
|
agentProvider: resolveGatewayMessageProvider(options?.messageProvider),
|
||||||
agentAccountId: options?.agentAccountId,
|
agentAccountId: options?.agentAccountId,
|
||||||
|
|||||||
@@ -79,6 +79,9 @@ export type SandboxBrowserConfig = {
|
|||||||
headless: boolean;
|
headless: boolean;
|
||||||
enableNoVnc: boolean;
|
enableNoVnc: boolean;
|
||||||
allowHostControl: boolean;
|
allowHostControl: boolean;
|
||||||
|
allowedControlUrls?: string[];
|
||||||
|
allowedControlHosts?: string[];
|
||||||
|
allowedControlPorts?: number[];
|
||||||
autoStart: boolean;
|
autoStart: boolean;
|
||||||
autoStartTimeoutMs: number;
|
autoStartTimeoutMs: number;
|
||||||
};
|
};
|
||||||
@@ -140,6 +143,9 @@ export type SandboxContext = {
|
|||||||
docker: SandboxDockerConfig;
|
docker: SandboxDockerConfig;
|
||||||
tools: SandboxToolPolicy;
|
tools: SandboxToolPolicy;
|
||||||
browserAllowHostControl: boolean;
|
browserAllowHostControl: boolean;
|
||||||
|
browserAllowedControlUrls?: string[];
|
||||||
|
browserAllowedControlHosts?: string[];
|
||||||
|
browserAllowedControlPorts?: number[];
|
||||||
browser?: SandboxBrowserContext;
|
browser?: SandboxBrowserContext;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -310,6 +316,12 @@ export function resolveSandboxBrowserConfig(params: {
|
|||||||
const agentBrowser =
|
const agentBrowser =
|
||||||
params.scope === "shared" ? undefined : params.agentBrowser;
|
params.scope === "shared" ? undefined : params.agentBrowser;
|
||||||
const globalBrowser = params.globalBrowser;
|
const globalBrowser = params.globalBrowser;
|
||||||
|
const allowedControlUrls =
|
||||||
|
agentBrowser?.allowedControlUrls ?? globalBrowser?.allowedControlUrls;
|
||||||
|
const allowedControlHosts =
|
||||||
|
agentBrowser?.allowedControlHosts ?? globalBrowser?.allowedControlHosts;
|
||||||
|
const allowedControlPorts =
|
||||||
|
agentBrowser?.allowedControlPorts ?? globalBrowser?.allowedControlPorts;
|
||||||
return {
|
return {
|
||||||
enabled: agentBrowser?.enabled ?? globalBrowser?.enabled ?? false,
|
enabled: agentBrowser?.enabled ?? globalBrowser?.enabled ?? false,
|
||||||
image:
|
image:
|
||||||
@@ -339,6 +351,18 @@ export function resolveSandboxBrowserConfig(params: {
|
|||||||
agentBrowser?.allowHostControl ??
|
agentBrowser?.allowHostControl ??
|
||||||
globalBrowser?.allowHostControl ??
|
globalBrowser?.allowHostControl ??
|
||||||
false,
|
false,
|
||||||
|
allowedControlUrls:
|
||||||
|
Array.isArray(allowedControlUrls) && allowedControlUrls.length > 0
|
||||||
|
? allowedControlUrls
|
||||||
|
: undefined,
|
||||||
|
allowedControlHosts:
|
||||||
|
Array.isArray(allowedControlHosts) && allowedControlHosts.length > 0
|
||||||
|
? allowedControlHosts
|
||||||
|
: undefined,
|
||||||
|
allowedControlPorts:
|
||||||
|
Array.isArray(allowedControlPorts) && allowedControlPorts.length > 0
|
||||||
|
? allowedControlPorts
|
||||||
|
: undefined,
|
||||||
autoStart: agentBrowser?.autoStart ?? globalBrowser?.autoStart ?? true,
|
autoStart: agentBrowser?.autoStart ?? globalBrowser?.autoStart ?? true,
|
||||||
autoStartTimeoutMs:
|
autoStartTimeoutMs:
|
||||||
agentBrowser?.autoStartTimeoutMs ??
|
agentBrowser?.autoStartTimeoutMs ??
|
||||||
@@ -1331,6 +1355,9 @@ export async function resolveSandboxContext(params: {
|
|||||||
docker: cfg.docker,
|
docker: cfg.docker,
|
||||||
tools: cfg.tools,
|
tools: cfg.tools,
|
||||||
browserAllowHostControl: cfg.browser.allowHostControl,
|
browserAllowHostControl: cfg.browser.allowHostControl,
|
||||||
|
browserAllowedControlUrls: cfg.browser.allowedControlUrls,
|
||||||
|
browserAllowedControlHosts: cfg.browser.allowedControlHosts,
|
||||||
|
browserAllowedControlPorts: cfg.browser.allowedControlPorts,
|
||||||
browser: browser ?? undefined,
|
browser: browser ?? undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -249,6 +249,21 @@ export function buildAgentSystemPrompt(params: {
|
|||||||
: params.sandboxInfo.hostBrowserAllowed === false
|
: params.sandboxInfo.hostBrowserAllowed === false
|
||||||
? "Host browser control: blocked."
|
? "Host browser control: blocked."
|
||||||
: "",
|
: "",
|
||||||
|
params.sandboxInfo.allowedControlUrls?.length
|
||||||
|
? `Browser control URL allowlist: ${params.sandboxInfo.allowedControlUrls.join(
|
||||||
|
", ",
|
||||||
|
)}`
|
||||||
|
: "",
|
||||||
|
params.sandboxInfo.allowedControlHosts?.length
|
||||||
|
? `Browser control host allowlist: ${params.sandboxInfo.allowedControlHosts.join(
|
||||||
|
", ",
|
||||||
|
)}`
|
||||||
|
: "",
|
||||||
|
params.sandboxInfo.allowedControlPorts?.length
|
||||||
|
? `Browser control port allowlist: ${params.sandboxInfo.allowedControlPorts.join(
|
||||||
|
", ",
|
||||||
|
)}`
|
||||||
|
: "",
|
||||||
params.sandboxInfo.elevated?.allowed
|
params.sandboxInfo.elevated?.allowed
|
||||||
? "Elevated bash is available for this session."
|
? "Elevated bash is available for this session."
|
||||||
: "",
|
: "",
|
||||||
|
|||||||
@@ -136,6 +136,9 @@ function resolveBrowserBaseUrl(params: {
|
|||||||
controlUrl?: string;
|
controlUrl?: string;
|
||||||
defaultControlUrl?: string;
|
defaultControlUrl?: string;
|
||||||
allowHostControl?: boolean;
|
allowHostControl?: boolean;
|
||||||
|
allowedControlUrls?: string[];
|
||||||
|
allowedControlHosts?: string[];
|
||||||
|
allowedControlPorts?: number[];
|
||||||
}) {
|
}) {
|
||||||
const cfg = loadConfig();
|
const cfg = loadConfig();
|
||||||
const resolved = resolveBrowserConfig(cfg.browser);
|
const resolved = resolveBrowserConfig(cfg.browser);
|
||||||
@@ -145,6 +148,51 @@ function resolveBrowserBaseUrl(params: {
|
|||||||
params.target ??
|
params.target ??
|
||||||
(normalizedControlUrl ? "custom" : normalizedDefault ? "sandbox" : "host");
|
(normalizedControlUrl ? "custom" : normalizedDefault ? "sandbox" : "host");
|
||||||
|
|
||||||
|
const assertAllowedControlUrl = (url: string) => {
|
||||||
|
const allowedUrls = params.allowedControlUrls?.map((entry) =>
|
||||||
|
entry.trim().replace(/\/$/, ""),
|
||||||
|
);
|
||||||
|
const allowedHosts = params.allowedControlHosts?.map((entry) =>
|
||||||
|
entry.trim().toLowerCase(),
|
||||||
|
);
|
||||||
|
const allowedPorts = params.allowedControlPorts;
|
||||||
|
if (
|
||||||
|
!allowedUrls?.length &&
|
||||||
|
!allowedHosts?.length &&
|
||||||
|
!allowedPorts?.length
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let parsed: URL;
|
||||||
|
try {
|
||||||
|
parsed = new URL(url);
|
||||||
|
} catch {
|
||||||
|
throw new Error(`Invalid browser controlUrl: ${url}`);
|
||||||
|
}
|
||||||
|
const normalizedUrl = parsed.toString().replace(/\/$/, "");
|
||||||
|
if (allowedUrls?.length && !allowedUrls.includes(normalizedUrl)) {
|
||||||
|
throw new Error("Browser controlUrl is not in the allowed URL list.");
|
||||||
|
}
|
||||||
|
if (allowedHosts?.length && !allowedHosts.includes(parsed.hostname)) {
|
||||||
|
throw new Error(
|
||||||
|
"Browser controlUrl hostname is not in the allowed host list.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (allowedPorts?.length) {
|
||||||
|
const port =
|
||||||
|
parsed.port?.trim() !== ""
|
||||||
|
? Number(parsed.port)
|
||||||
|
: parsed.protocol === "https:"
|
||||||
|
? 443
|
||||||
|
: 80;
|
||||||
|
if (!Number.isFinite(port) || !allowedPorts.includes(port)) {
|
||||||
|
throw new Error(
|
||||||
|
"Browser controlUrl port is not in the allowed port list.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (target !== "custom" && params.target && normalizedControlUrl) {
|
if (target !== "custom" && params.target && normalizedControlUrl) {
|
||||||
throw new Error('controlUrl is only supported with target="custom".');
|
throw new Error('controlUrl is only supported with target="custom".');
|
||||||
}
|
}
|
||||||
@@ -153,7 +201,9 @@ function resolveBrowserBaseUrl(params: {
|
|||||||
if (!normalizedControlUrl) {
|
if (!normalizedControlUrl) {
|
||||||
throw new Error("Custom browser target requires controlUrl.");
|
throw new Error("Custom browser target requires controlUrl.");
|
||||||
}
|
}
|
||||||
return normalizedControlUrl.replace(/\/$/, "");
|
const normalized = normalizedControlUrl.replace(/\/$/, "");
|
||||||
|
assertAllowedControlUrl(normalized);
|
||||||
|
return normalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target === "sandbox") {
|
if (target === "sandbox") {
|
||||||
@@ -173,18 +223,40 @@ function resolveBrowserBaseUrl(params: {
|
|||||||
"Browser control is disabled. Set browser.enabled=true in ~/.clawdbot/clawdbot.json.",
|
"Browser control is disabled. Set browser.enabled=true in ~/.clawdbot/clawdbot.json.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return resolved.controlUrl.replace(/\/$/, "");
|
const normalized = resolved.controlUrl.replace(/\/$/, "");
|
||||||
|
assertAllowedControlUrl(normalized);
|
||||||
|
return normalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createBrowserTool(opts?: {
|
export function createBrowserTool(opts?: {
|
||||||
defaultControlUrl?: string;
|
defaultControlUrl?: string;
|
||||||
allowHostControl?: boolean;
|
allowHostControl?: boolean;
|
||||||
|
allowedControlUrls?: string[];
|
||||||
|
allowedControlHosts?: string[];
|
||||||
|
allowedControlPorts?: number[];
|
||||||
}): AnyAgentTool {
|
}): AnyAgentTool {
|
||||||
|
const targetDefault = opts?.defaultControlUrl ? "sandbox" : "host";
|
||||||
|
const hostHint =
|
||||||
|
opts?.allowHostControl === false
|
||||||
|
? "Host target blocked by policy."
|
||||||
|
: "Host target allowed.";
|
||||||
|
const allowlistHint =
|
||||||
|
opts?.allowedControlUrls?.length ||
|
||||||
|
opts?.allowedControlHosts?.length ||
|
||||||
|
opts?.allowedControlPorts?.length
|
||||||
|
? "Custom targets are restricted by sandbox allowlists."
|
||||||
|
: "Custom targets are unrestricted.";
|
||||||
return {
|
return {
|
||||||
label: "Browser",
|
label: "Browser",
|
||||||
name: "browser",
|
name: "browser",
|
||||||
description:
|
description: [
|
||||||
"Control clawd's dedicated browser (status/start/stop/tabs/open/snapshot/screenshot/actions). Use snapshot+act for UI automation. Avoid act:wait by default; use only in exceptional cases when no reliable UI state exists.",
|
"Control clawd's dedicated browser (status/start/stop/tabs/open/snapshot/screenshot/actions).",
|
||||||
|
"Use snapshot+act for UI automation. Avoid act:wait by default; use only in exceptional cases when no reliable UI state exists.",
|
||||||
|
`target selects browser location (sandbox|host|custom). Default: ${targetDefault}.`,
|
||||||
|
"controlUrl implies target=custom (remote control server).",
|
||||||
|
hostHint,
|
||||||
|
allowlistHint,
|
||||||
|
].join(" "),
|
||||||
parameters: BrowserToolSchema,
|
parameters: BrowserToolSchema,
|
||||||
execute: async (_toolCallId, args) => {
|
execute: async (_toolCallId, args) => {
|
||||||
const params = args as Record<string, unknown>;
|
const params = args as Record<string, unknown>;
|
||||||
@@ -201,6 +273,9 @@ export function createBrowserTool(opts?: {
|
|||||||
controlUrl,
|
controlUrl,
|
||||||
defaultControlUrl: opts?.defaultControlUrl,
|
defaultControlUrl: opts?.defaultControlUrl,
|
||||||
allowHostControl: opts?.allowHostControl,
|
allowHostControl: opts?.allowHostControl,
|
||||||
|
allowedControlUrls: opts?.allowedControlUrls,
|
||||||
|
allowedControlHosts: opts?.allowedControlHosts,
|
||||||
|
allowedControlPorts: opts?.allowedControlPorts,
|
||||||
});
|
});
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
|
|||||||
@@ -865,6 +865,21 @@ export type SandboxBrowserSettings = {
|
|||||||
* Default: false.
|
* Default: false.
|
||||||
*/
|
*/
|
||||||
allowHostControl?: boolean;
|
allowHostControl?: boolean;
|
||||||
|
/**
|
||||||
|
* Allowlist of exact control URLs for target="custom".
|
||||||
|
* When set, any custom controlUrl must match this list.
|
||||||
|
*/
|
||||||
|
allowedControlUrls?: string[];
|
||||||
|
/**
|
||||||
|
* Allowlist of hostnames for control URLs (hostname only, no ports).
|
||||||
|
* When set, controlUrl hostname must match.
|
||||||
|
*/
|
||||||
|
allowedControlHosts?: string[];
|
||||||
|
/**
|
||||||
|
* Allowlist of ports for control URLs.
|
||||||
|
* When set, controlUrl port must match (defaults: http=80, https=443).
|
||||||
|
*/
|
||||||
|
allowedControlPorts?: number[];
|
||||||
/**
|
/**
|
||||||
* When true (default), sandboxed browser control will try to start/reattach to
|
* When true (default), sandboxed browser control will try to start/reattach to
|
||||||
* the sandbox browser container when a tool call needs it.
|
* the sandbox browser container when a tool call needs it.
|
||||||
|
|||||||
@@ -746,6 +746,9 @@ const SandboxBrowserSchema = z
|
|||||||
headless: z.boolean().optional(),
|
headless: z.boolean().optional(),
|
||||||
enableNoVnc: z.boolean().optional(),
|
enableNoVnc: z.boolean().optional(),
|
||||||
allowHostControl: z.boolean().optional(),
|
allowHostControl: z.boolean().optional(),
|
||||||
|
allowedControlUrls: z.array(z.string()).optional(),
|
||||||
|
allowedControlHosts: z.array(z.string()).optional(),
|
||||||
|
allowedControlPorts: z.array(z.number().int().positive()).optional(),
|
||||||
autoStart: z.boolean().optional(),
|
autoStart: z.boolean().optional(),
|
||||||
autoStartTimeoutMs: z.number().int().positive().optional(),
|
autoStartTimeoutMs: z.number().int().positive().optional(),
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user