Skip to content

fix: enforce focus subagent scope#73613

Merged
drobison00 merged 2 commits intoopenclaw:mainfrom
drobison00:fix-focus-scope
Apr 29, 2026
Merged

fix: enforce focus subagent scope#73613
drobison00 merged 2 commits intoopenclaw:mainfrom
drobison00:fix-focus-scope

Conversation

@drobison00
Copy link
Copy Markdown
Contributor

fix: enforce focus subagent scope

Summary

  • Problem: /focus could resolve and bind a target session before applying the subagent control-scope boundary used by other subagent-control commands.
  • Why it matters: A leaf subagent should not be able to persistently redirect a conversation to sessions outside its allowed control boundary.
  • What changed: /focus now resolves the requester controller, rejects leaf subagent callers, and passes a requester key into fallback target resolution so gateway resolution is restricted to child sessions for subagent callers.
  • What did NOT change (scope boundary): No channel binding policy, session binding storage, gateway schema, or CI/automation behavior changed.

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- Related NVIDIA-dev/openclaw-tracking#525

  • UI / DX

  • CI/CD / infra

Linked Issue/PR

  • Closes #
  • This PR fixes a bug or regression

Root Cause (if applicable)

  • Root cause: The /focus handler did not call resolveCommandSubagentController before target resolution and binding, and fallback gateway target resolution omitted the requester's spawnedBy visibility filter.
  • Missing detection / guardrail: Existing focused-command tests covered binding behavior but not leaf-subagent rejection or gateway fallback scoping.
  • Contributing context (if known): Similar subagent-control commands already enforce this boundary.

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/auto-reply/reply/commands-subagents-focus.test.ts and src/auto-reply/reply/commands-subagents-shared-focus.test.ts
  • Scenario the test should lock in: Leaf subagent /focus requests are rejected before binding, and subagent fallback target resolution passes spawnedBy.
  • Why this is the smallest reliable guardrail: The affected behavior is contained in the focus action and its shared target resolver.
  • Existing test that already covers this (if any): Existing focus binding tests covered normal binding paths, not this boundary.
  • If no new test is added, why not: N/A

User-visible / Behavior Changes

Leaf subagents can no longer use /focus to bind conversations to other sessions.

Diagram (if applicable)

Before:
[leaf subagent /focus] -> [resolve any gateway-visible target] -> [bind conversation]

After:
[leaf subagent /focus] -> [control-scope check] -> [rejected]

Security Impact (required)

  • New permissions/capabilities? (Yes/No) No
  • Secrets/tokens handling changed? (Yes/No) No
  • New/changed network calls? (Yes/No) No
  • Command/tool execution surface changed? (Yes/No) Yes
  • Data access scope changed? (Yes/No) Yes
  • If any Yes, explain risk + mitigation: The /focus command now rejects leaf subagent control attempts and narrows fallback target resolution for subagent callers. This reduces session-control and conversation-routing scope.

Repro + Verification

Environment

  • OS: Linux 6.8.0-110-generic x86_64
  • Runtime/container: Node v22.14.0, pnpm 10.33.0
  • Model/provider: N/A
  • Integration/channel (if any): auto-reply focused conversation bindings
  • Relevant config (redacted): default unit-test config

Steps

  1. Run the focused auto-reply tests for /focus and shared focus target resolution.
  2. Verify leaf-subagent /focus rejects before target resolution and binding.
  3. Verify subagent fallback target resolution includes spawnedBy.

Expected

  • Leaf subagent focus requests are rejected.
  • Fallback target resolution for subagent requesters is scoped to the requester's children.

Actual

  • pnpm exec vitest run --config test/vitest/vitest.auto-reply-reply.config.ts src/auto-reply/reply/commands-subagents-focus.test.ts src/auto-reply/reply/commands-subagents-shared-focus.test.ts --reporter=verbose passed.

Evidence

Attach at least one:

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

Focused validation after the fix:

Test Files  2 passed (2)
Tests  13 passed (13)

Human Verification (required)

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

  • Verified scenarios: Leaf-subagent focus rejection, normal focus binding tests, and spawnedBy propagation in fallback resolution.
  • Edge cases checked: Existing current-thread, room-thread, topic-thread, ACP metadata, rebind-owner, unsupported-channel, and unfocus tests still pass.
  • What you did not verify: Full repository test suite and live channel integrations.

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.

If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.

Compatibility / Migration

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

Risks and Mitigations

  • Risk: A legitimate subagent caller may no longer focus arbitrary non-child sessions.
    • Mitigation: This matches the existing subagent control-scope boundary and keeps non-subagent callers on the existing path.

@openclaw-barnacle openclaw-barnacle Bot added size: S maintainer Maintainer-authored PR labels Apr 28, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 28, 2026

Greptile Summary

This PR fixes a security boundary gap where /focus could bind conversations to sessions outside a leaf subagent's allowed control scope. It adds a resolveCommandSubagentController call at the top of handleSubagentsFocusAction to reject callers with controlScope !== "children", and threads a requesterKey into resolveFocusTargetSession so the gateway sessions.resolve fallback is filtered via spawnedBy for subagent callers.

The previously flagged concern about the runs-based resolution path bypassing the new spawnedBy filter is mitigated: ctx.runs is populated by listControlledSubagentRuns(requesterKey), which already filters to direct children of the requester, so entries in that list cannot refer to non-child sessions. No P0/P1 issues found.

Confidence Score: 5/5

Safe to merge — the fix correctly enforces the subagent control-scope boundary on /focus, matching the enforcement already present on other subagent-control commands.

No P0 or P1 findings. The runs-based path that was flagged in a prior review is already scoped by listControlledSubagentRuns, which filters to direct children. The gateway fallback is now correctly guarded by spawnedBy. All existing and new tests pass.

No files require special attention.

Reviews (2): Last reviewed commit: "docs: add changelog for focus scope fix" | Re-trigger Greptile

@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented Apr 28, 2026

Codex review: needs maintainer review before merge.

What this changes:

The PR branch adds a /focus subagent controller-scope check, threads requester scope into focus target resolution for spawnedBy-filtered gateway fallback, and adds focused tests plus a changelog entry.

Maintainer follow-up before merge:

This is an open security-hardening implementation PR with a protected label and pending/relevant review context, so the next action is maintainer/security review rather than an automated replacement or cleanup close.

Best possible solution:

Land a reviewed fix that applies the same subagent control-scope boundary to /focus, confirms both local-runs and gateway fallback resolution paths cannot target non-child sessions, keeps focused regression coverage, and retains the user-facing changelog entry if the behavior change ships.

Acceptance criteria:

  • pnpm test src/auto-reply/reply/commands-subagents-focus.test.ts src/auto-reply/reply/commands-subagents-shared-focus.test.ts
  • pnpm check:changed in Testbox before merge if the PR remains code/test/changelog only and the changed lane stays appropriate

What I checked:

  • Protected label: The provided GitHub context shows this open PR carries the maintainer label, which is protected by the cleanup policy and requires explicit maintainer handling.
  • Current main focus handler lacks the new scope check: On current main, handleSubagentsFocusAction resolves the binding context and binding service before target resolution, and calls resolveFocusTargetSession({ runs, token }) without resolveCommandSubagentController or a requester key. (src/auto-reply/reply/commands-subagents/action-focus.ts:74, b92d14525262)
  • Current main target resolver fallback is unscoped: resolveFocusTargetSession on current main accepts only runs and token; its gateway fallback calls sessions.resolve with the raw attempt params and no spawnedBy visibility filter. (src/auto-reply/reply/commands-subagents/shared.ts:307, b92d14525262)
  • Existing control boundary pattern: Other subagent-control commands already resolve a controller and delegate to control helpers that reject non-children control scope with Leaf subagents cannot control other sessions.. (src/auto-reply/reply/commands-subagents/action-send.ts:37, b92d14525262)
  • Runs path context: Current main builds command ctx.runs from listControlledSubagentRuns(requesterKey), and that helper filters latest runs by controller session key, which is relevant to the Greptile review question about the runs-based path. (src/auto-reply/reply/commands-subagents.ts:125, b92d14525262)
  • Gateway spawnedBy contract exists: sessions.resolve already supports spawnedBy visibility filtering for key, sessionId, and label resolution, with tests covering key-based visibility filtering. (src/gateway/sessions-resolve.ts:55, b92d14525262)

Likely related people:

  • steipete: Local current-main blame attributes the affected subagent command/control files to Peter Steinberger's compact import commit, and recent adjacent subagent registry maintenance is also by Peter. The local history is compact, so this is routing evidence rather than root-cause attribution. (role: current-main maintainer / likely follow-up owner; confidence: medium; commits: 7a69069bfca1, b7db63751b99; files: src/auto-reply/reply/commands-subagents/action-focus.ts, src/auto-reply/reply/commands-subagents/shared.ts, src/agents/subagent-control.ts)
  • samzong: Recent shipped work surfaced spawnedBy in gateway chat/agent event metadata, which is adjacent to the visibility and routing metadata used by this PR, though not the /focus handler itself. (role: adjacent spawnedBy metadata contributor; confidence: low; commits: 443ca4865d61; files: src/gateway/protocol/schema/agent.ts, src/gateway/protocol/schema/logs-chat.ts, src/gateway/server-chat.ts)

Remaining risk / open question:

  • Protected maintainer labeling and security-hardening scope require explicit maintainer/security judgment before merge or cleanup closure.
  • Aisle's security review was still in progress in the provided comments, and Greptile's runs-based resolution question should be answered explicitly before landing.
  • This read-only review did not execute tests; validation remains part of PR review.

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

@drobison00
Copy link
Copy Markdown
Contributor Author

@greptile review
@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@drobison00 drobison00 merged commit c1a42dc into openclaw:main Apr 29, 2026
72 of 75 checks passed
lxe pushed a commit to lxe/openclaw that referenced this pull request May 6, 2026
* fix: enforce focus subagent scope

* docs: add changelog for focus scope fix
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
* fix: enforce focus subagent scope

* docs: add changelog for focus scope fix
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

maintainer Maintainer-authored PR size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant