Skip to content

fix(agents): add strict format validation to sessions_spawn for agentId#31381

Merged
steipete merged 3 commits intoopenclaw:mainfrom
openperf:fix/issue-31311-subagent-spawn-validation
Mar 2, 2026
Merged

fix(agents): add strict format validation to sessions_spawn for agentId#31381
steipete merged 3 commits intoopenclaw:mainfrom
openperf:fix/issue-31311-subagent-spawn-validation

Conversation

@openperf
Copy link
Contributor

@openperf openperf commented Mar 2, 2026

This PR resolves #31311 by implementing a strict format validation for the agentId parameter in sessions_spawn, preventing the creation of ghost workspace directories from malformed inputs.

The Problem

When sessions_spawn receives an error message string (e.g., from a failed upstream tool call) as the agentId, normalizeAgentId mangles it into a seemingly valid ID (e.g., "Agent not found: xyz" becomes "agent-not-found--xyz"). This leads to the creation of an unexpected workspace directory, causing issues like cascading cron failures.

The Solution

This fix introduces a strict regex format gate (/^[a-z0-9][a-z0-9_-]{0,63}$/i) applied to the raw agentId input before normalizeAgentId is called. This immediately rejects any malformed strings containing spaces, colons, path separators, or other invalid characters, fixing the bug at its root.

Key Design Decisions

  • Minimal and targeted: The fix addresses the true source of the problem (improper input sanitization) with a single, focused check — no unnecessary complexity.
  • No regressions: The validation is placed before normalization but does not interfere with existing workflows, including delegating to agents that are allowlisted but not globally configured (configured: false in agents_list).
  • Robust test coverage: Five non-redundant tests have been added to the existing sessions-spawn.allowlist.test.ts to validate the format gate and ensure no regressions in allowlist behavior.

Changes

File Change
src/agents/subagent-spawn.ts Add STRICT_AGENT_ID_RE constant and format validation
...sessions-spawn.allowlist.test.ts Add 5 test cases for format validation

Fixes #31311

@openclaw-barnacle openclaw-barnacle bot added agents Agent runtime and tooling size: S labels Mar 2, 2026
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 572b547cac

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 2, 2026

Greptile Summary

This PR implements a focused security fix that prevents malformed agentId inputs from creating ghost workspace directories. The implementation adds a strict regex validation gate (/^[a-z0-9][a-z0-9_-]{0,63}$/i) that rejects invalid inputs before they reach the normalizeAgentId function, which would otherwise mangle error messages like "Agent not found: xyz" into seemingly valid IDs like "agent-not-found--xyz".

Key changes:

  • Added STRICT_AGENT_ID_RE constant in src/agents/subagent-spawn.ts:42 that matches the canonical VALID_ID_RE from src/routing/session-key.ts:24
  • Validation occurs at src/agents/subagent-spawn.ts:267 before calling normalizeAgentId, returning a clear error message for invalid inputs
  • Five comprehensive test cases added covering error messages, path traversal attempts, length limits, valid formats, and allowlist behavior

Observations:

  • The regex is duplicated rather than imported from session-key.ts. This is acknowledged in the JSDoc comment (line 40) which states it "must stay in sync with the canonical VALID_ID_RE". The duplication is intentional since VALID_ID_RE is not exported, and the local constant includes specific context about its purpose as a "strict format gate"
  • The fix correctly prevents path traversal attempts (../../../etc/passwd), error message strings with spaces and colons, and overly long IDs
  • Existing workflows remain unaffected - the validation only rejects inputs that would have been mangled anyway, and valid IDs (including allowlisted-but-unconfigured agents) continue to work correctly

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk.
  • The implementation is clean, focused, and well-tested. The regex validation exactly matches the canonical pattern used elsewhere in the codebase, preventing the root cause (malformed inputs being normalized into valid-looking IDs) without affecting any legitimate use cases. The five test cases provide thorough coverage of edge cases including error messages, path traversal, length limits, and allowlist behavior. No regressions are introduced - all existing valid inputs continue to work, including the important case of allowlisted-but-unconfigured agents.
  • No files require special attention

Last reviewed commit: 572b547

@openperf openperf force-pushed the fix/issue-31311-subagent-spawn-validation branch from 572b547 to 06b30be Compare March 2, 2026 07:31
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 06b30be712

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

// Without this gate, error-message strings like "Agent not found: xyz" pass
// through normalizeAgentId and become "agent-not-found--xyz", which later
// creates ghost workspace directories and triggers cascading cron loops (#31311).
if (requestedAgentId && !STRICT_AGENT_ID_RE.test(requestedAgentId)) {

Choose a reason for hiding this comment

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

P2 Badge Accept allowlisted normalized IDs before hard-failing

This pre-normalization regex gate rejects some agent IDs that the rest of the codebase already treats as valid normalized IDs, so sessions_spawn can now fail on IDs users are explicitly told to use from agents_list. In particular, IDs like _worker are still producible and persisted via normalization, but this check returns Invalid agentId before allowlist resolution, which is a behavior regression for existing configs. Fresh evidence: normalizeAgentId fallback can preserve leading underscores (src/routing/session-key.ts:91-99), agents add stores that normalized value (src/commands/agents.commands.add.ts:87-95), and agents_list returns normalized IDs from config/allowlists (src/agents/tools/agents-list-tool.ts:57-88).

Useful? React with 👍 / 👎.

root and others added 3 commits March 2, 2026 15:57
Implements a strict format validation for the agentId parameter in
sessions_spawn to fully resolve the ghost workspace creation bug reported
in openclaw#31311.

This fix introduces a regex format gate at the entry point to
immediately reject malformed agentId strings. This prevents error
messages (e.g., 'Agent not found: xyz') or path traversals from being
mangled by normalizeAgentId into seemingly valid IDs (e.g.,
'agent-not-found--xyz'), which was the root cause of the bug.

The validation is placed before normalization and does not interfere
with existing workflows, including delegating to agents that are
allowlisted but not globally configured.

New, non-redundant tests are added to
sessions-spawn.allowlist.test.ts to cover format validation and
ensure no regressions in allowlist behavior.

Fixes openclaw#31311
@steipete steipete force-pushed the fix/issue-31311-subagent-spawn-validation branch from 06b30be to 4c4c76a Compare March 2, 2026 15:59
@steipete steipete merged commit 64c443a into openclaw:main Mar 2, 2026
9 checks passed
@steipete
Copy link
Contributor

steipete commented Mar 2, 2026

Landed via temp rebase onto main.

  • Gate: pnpm test src/agents/openclaw-tools.subagents.sessions-spawn.allowlist.test.ts src/routing/session-key.test.ts (full gate blocked by pre-existing main failure in extensions/zalouser/src/monitor.ts:141)
  • Land commit: 4c4c76a
  • Merge commit: 64c443a

Thanks @openperf!

Linux2010 pushed a commit to Linux2010/openclaw that referenced this pull request Mar 2, 2026
Linux2010 pushed a commit to Linux2010/openclaw that referenced this pull request Mar 2, 2026
mrosmarin added a commit to mrosmarin/openclaw that referenced this pull request Mar 2, 2026
* main: (174 commits)
  refactor(gateway): unify control-ui and plugin webhook routing
  fix(exec): resolve PATH key case-insensitively for Windows pathPrepend (openclaw#25399) (openclaw#31879)
  fix(tsgo): unblock baseline type errors (openclaw#31873)
  fix(security): harden sms.send dangerous-node defaults
  fix(gateway): let POST requests pass through root-mounted Control UI to plugin handlers
  fix(browser): fail closed navigation guard with env proxy
  test(perf): reduce timer teardown overhead in cron issue regressions
  refactor: split browser context/actions and unify CDP timeout policy
  test(perf): cache redact hints and tune guardrail scan concurrency
  docs(changelog): credit sessions_spawn agentId validation fix (openclaw#31381)
  fix(agents): validate sessions_spawn agentId format (openclaw#31381)
  fix(agents): add strict format validation to sessions_spawn for agentId
  fix(logging): log timestamps use local time instead of UTC (openclaw#28434)
  test(perf): remove redundant module reset in system presence version tests
  test(perf): avoid module reload churn in config guard tests
  fix(gateway): fail closed plugin auth path canonicalization
  docs(changelog): credit sandbox mkdirp boundary fix (openclaw#31547)
  fix(sandbox): allow mkdirp boundary checks on existing directories (openclaw#31547)
  fix(sandbox): allow mkdirp boundary check on existing directories
  fix: preserve dns pinning for strict web SSRF fetches
  ...
execute008 pushed a commit to execute008/openclaw that referenced this pull request Mar 2, 2026
execute008 pushed a commit to execute008/openclaw that referenced this pull request Mar 2, 2026
dawi369 pushed a commit to dawi369/davis that referenced this pull request Mar 3, 2026
OWALabuy pushed a commit to kcinzgg/openclaw that referenced this pull request Mar 4, 2026
OWALabuy pushed a commit to kcinzgg/openclaw that referenced this pull request Mar 4, 2026
zooqueen pushed a commit to hanzoai/bot that referenced this pull request Mar 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Failed sessions_spawn creates workspace directory from error message string

2 participants