Skip to content

fix(exec): reject invalid host targets#74468

Merged
steipete merged 2 commits intoopenclaw:mainfrom
vyctorbrzezowski:contrib/exec-invalid-host-fail-closed
Apr 29, 2026
Merged

fix(exec): reject invalid host targets#74468
steipete merged 2 commits intoopenclaw:mainfrom
vyctorbrzezowski:contrib/exec-invalid-host-fail-closed

Conversation

@vyctorbrzezowski
Copy link
Copy Markdown
Contributor

Summary

  • Problem: per-call exec host values outside auto, sandbox, gateway, and node were normalized away, so hostname-like input could silently fall back to the default target.
  • Why it matters: a malformed direct exec request should fail closed before any command launches instead of relying on fallback routing.
  • What changed: validate per-call exec host values before target resolution, tighten the exec tool schema enum, add regression coverage for invalid string and non-string host values, and document the allowed host set.
  • What did NOT change (scope boundary): no new config keys, no approval/elevated routing changes, and no change to default host=auto behavior.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

Root Cause (if applicable)

  • Root cause: direct exec calls used the permissive target normalizer, which returned null for unknown host values and allowed target resolution to fall back to the configured/default target.
  • Missing detection / guardrail: regression coverage did not assert that malformed per-call host input fails before command launch.
  • Contributing context (if known): N/A

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: src/infra/exec-approvals-policy.test.ts, src/agents/bash-tools.exec-foreground-failures.test.ts
  • Scenario the test should lock in: invalid string and non-string per-call host values throw before the exec command can launch.
  • Why this is the smallest reliable guardrail: one policy helper test locks the closed host set, and one exec tool test verifies runtime behavior at the command boundary.
  • Existing test that already covers this (if any): N/A
  • If no new test is added, why not: N/A

User-visible / Behavior Changes

Direct exec calls with malformed host values now fail with an explicit validation error instead of falling back to the configured/default exec target. Valid auto, sandbox, gateway, and node behavior is unchanged.

Diagram (if applicable)

Before:
exec({ command, host: "spark-ff13" }) -> invalid host normalizes to null -> default target runs command

After:
exec({ command, host: "spark-ff13" }) -> validation error -> command is not launched

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? Yes
  • Data access scope changed? No
  • If any Yes, explain risk + mitigation: command execution input validation is stricter; malformed per-call host values now fail closed before launch.

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: Node.js v24.14.0, pnpm
  • Model/provider: N/A
  • Integration/channel (if any): exec tool
  • Relevant config (redacted): N/A

Steps

  1. Call the exec tool with host: "spark-ff13" and a command.
  2. Call the exec tool with a non-string host value.

Expected

  • The exec tool rejects the malformed host before launching the command.

Actual

  • Before this change, invalid host-like values could be treated like no per-call host override and fall back to the default target.

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

New regression tests cover invalid string and non-string host values, and the focused test lane passes.

Human Verification (required)

What I personally verified (not just CI), and how:

  • Verified scenarios: invalid string host, non-string host, blank/undefined host, valid trimmed host, existing exec target runtime tests.
  • Edge cases checked: blank host remains equivalent to no override; gateway with whitespace still normalizes correctly; non-string input fails explicitly.
  • What I did not verify: full pnpm build && pnpm check && pnpm test; this PR was validated with focused tests plus check:changed for the touched surface.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No
  • If yes, exact upgrade steps: N/A

Risks and Mitigations

  • Risk: callers relying on malformed host values silently falling back will now get an error.
    • Mitigation: documented the allowed host set and preserved fallback behavior for missing/blank host values.

AI-assisted disclosure

This PR was AI-assisted. I reviewed the diff, understand the changed exec validation path, and ran the validation listed above, including local codex review --base origin/main.

@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation agents Agent runtime and tooling size: S labels Apr 29, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 29, 2026

Greptile Summary

This PR adds requireValidExecTarget to validate per-call host values against the closed set {auto, sandbox, gateway, node} before resolving an exec target, replacing the silent-fallback behavior of normalizeExecTarget. The schema enum is tightened in parallel, and regression tests cover invalid-string and non-string inputs. The logic and test coverage are sound; two minor P2 observations are noted inline.

Confidence Score: 4/5

Safe to merge; only P2 style and maintainability findings present.

No P0 or P1 issues found. The validation logic is correct, error paths are well-tested, and the behavioral change (fail-closed on invalid host) is intentional and consistent with the PR description. Score is 4 (not 5) due to the duplicated host-value list in the schema file, which creates a future synchronization risk.

src/agents/bash-tools.schemas.ts — the locally-defined EXEC_TOOL_HOST_VALUES is a duplicate of EXEC_TARGET_VALUES and will need manual updates if the allowed host set ever changes.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/agents/bash-tools.schemas.ts
Line: 4

Comment:
**Duplicate host-value list diverges from `EXEC_TARGET_VALUES`**

`EXEC_TOOL_HOST_VALUES` is a separate hardcoded copy of the same four strings already exported as `EXEC_TARGET_VALUES` from `src/infra/exec-approvals.ts`. If a new target (e.g. `"worker"`) is added to the runtime constant but this schema-side array is not updated, the schema will silently reject the new value before `requireValidExecTarget` ever sees it. Since `exec-approvals.ts` is in a lower-level `infra` layer, importing from it in `bash-tools.schemas.ts` avoids the circular-dependency concern while keeping the two in sync.

```suggestion
import { EXEC_TARGET_VALUES } from "../infra/exec-approvals.js";
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/infra/exec-approvals.ts
Line: 62-64

Comment:
**Error message quotes raw (potentially whitespace-padded) value**

The throw uses the original `value` rather than `normalized`, so `requireValidExecTarget(" spark-ff13 ")` produces `Invalid exec host " spark-ff13 "` with embedded spaces inside the quotes. Using `normalized` instead makes the displayed value match what was actually looked up.

```suggestion
  throw new Error(
    `Invalid exec host "${normalized}". Allowed values: ${EXEC_TARGET_VALUES.join(", ")}.`,
  );
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(exec): reject invalid host targets" | Re-trigger Greptile

import { Type } from "typebox";
import { optionalStringEnum } from "./schema/typebox.js";

const EXEC_TOOL_HOST_VALUES = ["auto", "sandbox", "gateway", "node"] as const;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Duplicate host-value list diverges from EXEC_TARGET_VALUES

EXEC_TOOL_HOST_VALUES is a separate hardcoded copy of the same four strings already exported as EXEC_TARGET_VALUES from src/infra/exec-approvals.ts. If a new target (e.g. "worker") is added to the runtime constant but this schema-side array is not updated, the schema will silently reject the new value before requireValidExecTarget ever sees it. Since exec-approvals.ts is in a lower-level infra layer, importing from it in bash-tools.schemas.ts avoids the circular-dependency concern while keeping the two in sync.

Suggested change
const EXEC_TOOL_HOST_VALUES = ["auto", "sandbox", "gateway", "node"] as const;
import { EXEC_TARGET_VALUES } from "../infra/exec-approvals.js";
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/bash-tools.schemas.ts
Line: 4

Comment:
**Duplicate host-value list diverges from `EXEC_TARGET_VALUES`**

`EXEC_TOOL_HOST_VALUES` is a separate hardcoded copy of the same four strings already exported as `EXEC_TARGET_VALUES` from `src/infra/exec-approvals.ts`. If a new target (e.g. `"worker"`) is added to the runtime constant but this schema-side array is not updated, the schema will silently reject the new value before `requireValidExecTarget` ever sees it. Since `exec-approvals.ts` is in a lower-level `infra` layer, importing from it in `bash-tools.schemas.ts` avoids the circular-dependency concern while keeping the two in sync.

```suggestion
import { EXEC_TARGET_VALUES } from "../infra/exec-approvals.js";
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +62 to +64
throw new Error(
`Invalid exec host "${value}". Allowed values: ${EXEC_TARGET_VALUES.join(", ")}.`,
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Error message quotes raw (potentially whitespace-padded) value

The throw uses the original value rather than normalized, so requireValidExecTarget(" spark-ff13 ") produces Invalid exec host " spark-ff13 " with embedded spaces inside the quotes. Using normalized instead makes the displayed value match what was actually looked up.

Suggested change
throw new Error(
`Invalid exec host "${value}". Allowed values: ${EXEC_TARGET_VALUES.join(", ")}.`,
);
throw new Error(
`Invalid exec host "${normalized}". Allowed values: ${EXEC_TARGET_VALUES.join(", ")}.`,
);
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/infra/exec-approvals.ts
Line: 62-64

Comment:
**Error message quotes raw (potentially whitespace-padded) value**

The throw uses the original `value` rather than `normalized`, so `requireValidExecTarget(" spark-ff13 ")` produces `Invalid exec host " spark-ff13 "` with embedded spaces inside the quotes. Using `normalized` instead makes the displayed value match what was actually looked up.

```suggestion
  throw new Error(
    `Invalid exec host "${normalized}". Allowed values: ${EXEC_TARGET_VALUES.join(", ")}.`,
  );
```

How can I resolve this? If you propose a fix, please make it concise.

@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented Apr 29, 2026

Codex review: needs maintainer review before merge.

What this changes:

The PR adds runtime validation for per-call exec host values, tightens the exec tool schema enum, documents the allowed host set, adds regression tests, and updates the changelog.

Maintainer follow-up before merge:

This is an open implementation PR that appears valid; the remaining action is maintainer review and CI validation, not an automated replacement or repair branch.

Review details

Best possible solution:

Land a narrow fail-closed exec host validation fix with schema, docs, changelog, and regression coverage, then let the linked bug close through the normal PR merge flow.

Acceptance criteria:

  • pnpm test src/infra/exec-approvals-policy.test.ts src/agents/bash-tools.exec-foreground-failures.test.ts
  • pnpm check:changed

What I checked:

  • Current main silently drops unknown exec targets: normalizeExecTarget returns null for values outside auto, sandbox, gateway, and node, which makes unknown host strings indistinguishable from no per-call override. (src/infra/exec-approvals.ts:33, e46dccb35374)
  • Current exec path uses the permissive normalizer: createExecTool currently passes normalizeExecTarget(params.host) into resolveExecTarget, so an invalid host-like string can fall back to configured/default routing instead of failing before launch. (src/agents/bash-tools.exec.ts:1548, e46dccb35374)
  • Current schema advertises host as a free string: The current TypeBox schema describes allowed host values in text but exposes host as Type.String, so the schema itself does not present a closed enum. (src/agents/bash-tools.schemas.ts:29, e46dccb35374)
  • Current docs lack the new hostname rejection note: The docs already type host as auto | sandbox | gateway | node, but current main does not state that hostname-like values are rejected before execution; the PR adds that note. Public docs: docs/tools/exec.md. (docs/tools/exec.md:43, e46dccb35374)
  • PR targets the linked bug with runtime and test coverage: The provided PR diff adds requireValidExecTarget, calls it before resolveExecTarget, tightens the schema with optionalStringEnum, and adds invalid string/non-string host tests in src/infra/exec-approvals-policy.test.ts and src/agents/bash-tools.exec-foreground-failures.test.ts. (src/infra/exec-approvals.ts, be6341cc9aec)
  • Security review: The PR touches exec validation/schema/tests/docs/changelog only; it does not change dependencies, workflows, lockfiles, package scripts, release metadata, secret handling, or downloaded code paths. The command-execution surface change is fail-closed validation before launch. (be6341cc9aec)

Likely related people:

  • steipete: Current-main blame for the central exec target normalizer, exec routing call site, schema, and docs points to commit 34d11d5 authored by Peter Steinberger; the PR context also shows a maintainer changelog credit commit by steipete on this branch. (role: recent maintainer / adjacent owner; confidence: medium; commits: 34d11d57579d, be6341cc9aec; files: src/infra/exec-approvals.ts, src/agents/bash-tools.exec.ts, src/agents/bash-tools.exec-runtime.ts)

Remaining risk / open question:

  • The PR duplicates the allowed host list in the schema instead of reusing EXEC_TARGET_VALUES; that is not a current correctness blocker, but it could drift if maintainers add another exec target later.

Codex review notes: model gpt-5.5, reasoning high; reviewed against e46dccb35374.

@steipete steipete merged commit df00747 into openclaw:main Apr 29, 2026
66 of 68 checks passed
comradecowboy9 pushed a commit to comradecowboy9/excited-llama that referenced this pull request Apr 30, 2026
* fix(exec): reject invalid host targets

* docs(changelog): credit exec host validation contributor

---------

Co-authored-by: Peter Steinberger <[email protected]>
lxe pushed a commit to lxe/openclaw that referenced this pull request May 6, 2026
* fix(exec): reject invalid host targets

* docs(changelog): credit exec host validation contributor

---------

Co-authored-by: Peter Steinberger <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling docs Improvements or additions to documentation size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Exec tool: invalid host: values silently coerce to sandbox, causing agents to confabulate host-level facts

2 participants