mirror of https://github.com/openclaw/openclaw.git
fix(exec): clarify auto routing semantics (#58897) (thanks @vincentkoc)
This commit is contained in:
parent
938541999e
commit
45c8207ef2
|
|
@ -61,6 +61,7 @@ Docs: https://docs.openclaw.ai
|
|||
- Agents/subagents: pin admin-only subagent gateway calls to `operator.admin` while keeping `agent` at least privilege, so `sessions_spawn` no longer dies on loopback scope-upgrade pairing with `close(1008) "pairing required"`. (#59555) Thanks @openperf.
|
||||
- Exec approvals/config: strip invalid `security`, `ask`, and `askFallback` values from `~/.openclaw/exec-approvals.json` during normalization so malformed policy enums fall back cleanly to the documented defaults instead of corrupting runtime policy resolution. (#59112) Thanks @openperf.
|
||||
- Exec approvals/doctor: report host policy sources from the real approvals file path and ignore malformed host override values when attributing effective policy conflicts. (#59367) Thanks @gumadeiras.
|
||||
- Exec/runtime: treat `tools.exec.host=auto` as routing-only, keep implicit no-config exec on sandbox when available or gateway otherwise, and reject per-call host overrides that would bypass the configured sandbox or host target. (#58897) Thanks @vincentkoc.
|
||||
- Slack/mrkdwn formatting: add built-in Slack mrkdwn guidance in inbound context so Slack replies stop falling back to generic Markdown patterns that render poorly in Slack. (#59100) Thanks @jadewon.
|
||||
- WhatsApp/presence: send `unavailable` presence on connect in self-chat mode so personal-phone users stop losing all push notifications while the gateway is running. (#59410) Thanks @mcaxtr.
|
||||
- WhatsApp/media: add HTML, XML, and CSS to the MIME map and fall back gracefully for unknown media types instead of dropping the attachment. (#51562) Thanks @bobbyt74.
|
||||
|
|
|
|||
|
|
@ -85,6 +85,12 @@ openclaw config set tools.exec.security full
|
|||
openclaw config set tools.exec.ask off
|
||||
```
|
||||
|
||||
Why `tools.exec.host=gateway` in this example:
|
||||
|
||||
- `host=auto` still means "sandbox when available, otherwise gateway".
|
||||
- YOLO is about approvals, not routing.
|
||||
- If you want host exec even when a sandbox is configured, make the host choice explicit with `gateway` or `/exec host=gateway`.
|
||||
|
||||
This matches the current host-default YOLO behavior. Tighten it if you want approvals.
|
||||
|
||||
## Allowlist helpers
|
||||
|
|
|
|||
|
|
@ -278,6 +278,7 @@ flowchart TD
|
|||
|
||||
- If `tools.exec.host` is unset, the default is `auto`.
|
||||
- `host=auto` resolves to `sandbox` when a sandbox runtime is active, `gateway` otherwise.
|
||||
- `host=auto` is routing only; the no-prompt "YOLO" behavior comes from `security=full` plus `ask=off` on gateway/node.
|
||||
- On `gateway` and `node`, unset `tools.exec.security` defaults to `full`.
|
||||
- Unset `tools.exec.ask` defaults to `off`.
|
||||
- Result: if you are seeing approvals, some host-local or per-session policy tightened exec away from the current defaults.
|
||||
|
|
|
|||
|
|
@ -157,6 +157,8 @@ Or per session:
|
|||
Once set, any `exec` call with `host=node` runs on the node host (subject to the
|
||||
node allowlist/approvals).
|
||||
|
||||
`host=auto` will not silently hop to the node just because a tool call requests it. If you want node exec, set `tools.exec.host=node` or `/exec host=node ...` explicitly.
|
||||
|
||||
Related:
|
||||
|
||||
- [Node host CLI](/cli/node)
|
||||
|
|
|
|||
|
|
@ -104,6 +104,12 @@ This is now the default host behavior unless you tighten it explicitly:
|
|||
- `tools.exec.ask`: `off`
|
||||
- host `askFallback`: `full`
|
||||
|
||||
Important distinction:
|
||||
|
||||
- `tools.exec.host=auto` chooses where exec runs: sandbox when available, otherwise gateway.
|
||||
- YOLO chooses how host exec is approved: `security=full` plus `ask=off`.
|
||||
- `auto` does not let a tool call override a sandboxed session to `gateway` or `node`. If you want a different host, set `tools.exec.host` or use `/exec host=...` explicitly.
|
||||
|
||||
If you want a more conservative setup, tighten either layer back to `allowlist` / `on-miss`
|
||||
or `deny`.
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ Background sessions are scoped per agent; `process` only sees sessions from the
|
|||
Notes:
|
||||
|
||||
- `host` defaults to `auto`: sandbox when sandbox runtime is active for the session, otherwise gateway.
|
||||
- `auto` is only the default routing strategy. It is not a wildcard override that lets a tool call jump from sandbox to gateway or node.
|
||||
- With no extra config, `host=auto` still "just works": no sandbox means it resolves to `gateway`; a live sandbox means it stays in the sandbox.
|
||||
- `elevated` forces `host=gateway`; it is only available when elevated access is enabled for the current session/provider.
|
||||
- `gateway`/`node` approvals are controlled by `~/.openclaw/exec-approvals.json`.
|
||||
- `node` requires a paired node (companion app or headless node host).
|
||||
|
|
@ -57,6 +59,7 @@ Notes:
|
|||
- `tools.exec.security` (default: `deny` for sandbox, `full` for gateway + node when unset)
|
||||
- `tools.exec.ask` (default: `off`)
|
||||
- No-approval host exec is the default for gateway + node. If you want approvals/allowlist behavior, tighten both `tools.exec.*` and the host `~/.openclaw/exec-approvals.json`; see [Exec approvals](/tools/exec-approvals#no-approval-yolo-mode).
|
||||
- YOLO comes from the host-policy defaults (`security=full`, `ask=off`), not from `host=auto`. If you want to force gateway or node routing, set `tools.exec.host` or use `/exec host=...`.
|
||||
- `tools.exec.node` (default: unset)
|
||||
- `tools.exec.strictInlineEval` (default: false): when true, inline interpreter eval forms such as `python -c`, `node -e`, `ruby -e`, `perl -e`, `php -r`, `lua -e`, and `osascript -e` always require explicit approval. `allow-always` can still persist benign interpreter/script invocations, but inline-eval forms still prompt each time.
|
||||
- `tools.exec.pathPrepend`: list of directories to prepend to `PATH` for exec runs (gateway + sandbox only).
|
||||
|
|
|
|||
|
|
@ -47,6 +47,36 @@ describe("resolveExecTarget", () => {
|
|||
({ resolveExecTarget } = await import("./bash-tools.exec-runtime.js"));
|
||||
});
|
||||
|
||||
it("keeps implicit auto on sandbox when a sandbox runtime is available", () => {
|
||||
expect(
|
||||
resolveExecTarget({
|
||||
configuredTarget: "auto",
|
||||
elevatedRequested: false,
|
||||
sandboxAvailable: true,
|
||||
}),
|
||||
).toMatchObject({
|
||||
configuredTarget: "auto",
|
||||
requestedTarget: null,
|
||||
selectedTarget: "auto",
|
||||
effectiveHost: "sandbox",
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps implicit auto on gateway when no sandbox runtime is available", () => {
|
||||
expect(
|
||||
resolveExecTarget({
|
||||
configuredTarget: "auto",
|
||||
elevatedRequested: false,
|
||||
sandboxAvailable: false,
|
||||
}),
|
||||
).toMatchObject({
|
||||
configuredTarget: "auto",
|
||||
requestedTarget: null,
|
||||
selectedTarget: "auto",
|
||||
effectiveHost: "gateway",
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects host overrides when configured host is auto", () => {
|
||||
expect(() =>
|
||||
resolveExecTarget({
|
||||
|
|
@ -84,6 +114,49 @@ describe("resolveExecTarget", () => {
|
|||
effectiveHost: "sandbox",
|
||||
});
|
||||
});
|
||||
|
||||
it("requires an exact match for non-auto configured targets", () => {
|
||||
expect(() =>
|
||||
resolveExecTarget({
|
||||
configuredTarget: "gateway",
|
||||
requestedTarget: "auto",
|
||||
elevatedRequested: false,
|
||||
sandboxAvailable: true,
|
||||
}),
|
||||
).toThrow("exec host not allowed");
|
||||
});
|
||||
|
||||
it("allows exact node matches", () => {
|
||||
expect(
|
||||
resolveExecTarget({
|
||||
configuredTarget: "node",
|
||||
requestedTarget: "node",
|
||||
elevatedRequested: false,
|
||||
sandboxAvailable: true,
|
||||
}),
|
||||
).toMatchObject({
|
||||
configuredTarget: "node",
|
||||
requestedTarget: "node",
|
||||
selectedTarget: "node",
|
||||
effectiveHost: "node",
|
||||
});
|
||||
});
|
||||
|
||||
it("still forces elevated requests onto the gateway host", () => {
|
||||
expect(
|
||||
resolveExecTarget({
|
||||
configuredTarget: "auto",
|
||||
requestedTarget: "sandbox",
|
||||
elevatedRequested: true,
|
||||
sandboxAvailable: true,
|
||||
}),
|
||||
).toMatchObject({
|
||||
configuredTarget: "auto",
|
||||
requestedTarget: "sandbox",
|
||||
selectedTarget: "gateway",
|
||||
effectiveHost: "gateway",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("emitExecSystemEvent", () => {
|
||||
|
|
|
|||
|
|
@ -221,6 +221,9 @@ export function isRequestedExecTargetAllowed(params: {
|
|||
configuredTarget: ExecTarget;
|
||||
requestedTarget: ExecTarget;
|
||||
}) {
|
||||
// `auto` is a routing strategy, not a wildcard allowlist. Keep per-call host
|
||||
// selection pinned to the configured/session-selected target so a sandboxed
|
||||
// session cannot silently hop to gateway or node.
|
||||
return params.requestedTarget === params.configuredTarget;
|
||||
}
|
||||
|
||||
|
|
@ -253,6 +256,9 @@ export function resolveExecTarget(params: {
|
|||
);
|
||||
}
|
||||
const selectedTarget = requestedTarget ?? configuredTarget;
|
||||
// `auto` preserves the no-config "just work" default: sandbox when available,
|
||||
// otherwise gateway. The YOLO part comes from security/ask defaults, not from
|
||||
// `auto` itself.
|
||||
const effectiveHost =
|
||||
selectedTarget === "auto" ? (params.sandboxAvailable ? "sandbox" : "gateway") : selectedTarget;
|
||||
return {
|
||||
|
|
|
|||
Loading…
Reference in New Issue