fix(security): restrict subagent toolsets to parent's enabled set - #3269
Merged
Conversation
The delegate_task tool accepts a toolsets parameter directly from the LLM's function call arguments. When provided, these toolsets are passed through _strip_blocked_tools but never intersected with the parent agent's enabled_toolsets. A model can request toolsets the parent does not have (e.g., web, browser, rl), granting the subagent tools that were explicitly disabled for the parent. Intersect LLM-requested toolsets with the parent's enabled set before applying the blocked-tool filter, so subagents can only receive a subset of the parent's tools.
BongSuCHOI
added a commit
to BongSuCHOI/hermes-agent
that referenced
this pull request
Apr 6, 2026
When parent_agent.enabled_toolsets is None (the default, meaning all tools are enabled), subagents incorrectly fell back to DEFAULT_TOOLSETS (['terminal', 'file', 'web']) instead of inheriting the parent's full toolset. Root cause: - Line 188 used 'or' fallback: None or DEFAULT_TOOLSETS evaluates to DEFAULT_TOOLSETS - Line 192 checked truthiness: None is falsy, falling through to else Fix: - Use 'is not None' checks instead of truthiness - When enabled_toolsets is None, derive effective toolsets from parent_agent.valid_tool_names via the tool registry Fixes the bug introduced in f75b1d2 and repeated in e5d1444 (PR NousResearch#3269).
teknium1
pushed a commit
that referenced
this pull request
Apr 6, 2026
When parent_agent.enabled_toolsets is None (the default, meaning all tools are enabled), subagents incorrectly fell back to DEFAULT_TOOLSETS (['terminal', 'file', 'web']) instead of inheriting the parent's full toolset. Root cause: - Line 188 used 'or' fallback: None or DEFAULT_TOOLSETS evaluates to DEFAULT_TOOLSETS - Line 192 checked truthiness: None is falsy, falling through to else Fix: - Use 'is not None' checks instead of truthiness - When enabled_toolsets is None, derive effective toolsets from parent_agent.valid_tool_names via the tool registry Fixes the bug introduced in f75b1d2 and repeated in e5d1444 (PR #3269).
teknium1
pushed a commit
that referenced
this pull request
Apr 6, 2026
When parent_agent.enabled_toolsets is None (the default, meaning all tools are enabled), subagents incorrectly fell back to DEFAULT_TOOLSETS (['terminal', 'file', 'web']) instead of inheriting the parent's full toolset. Root cause: - Line 188 used 'or' fallback: None or DEFAULT_TOOLSETS evaluates to DEFAULT_TOOLSETS - Line 192 checked truthiness: None is falsy, falling through to else Fix: - Use 'is not None' checks instead of truthiness - When enabled_toolsets is None, derive effective toolsets from parent_agent.valid_tool_names via the tool registry Fixes the bug introduced in f75b1d2 and repeated in e5d1444 (PR #3269).
Tommyeds
pushed a commit
to Tommyeds/hermes-agent
that referenced
this pull request
Apr 12, 2026
When parent_agent.enabled_toolsets is None (the default, meaning all tools are enabled), subagents incorrectly fell back to DEFAULT_TOOLSETS (['terminal', 'file', 'web']) instead of inheriting the parent's full toolset. Root cause: - Line 188 used 'or' fallback: None or DEFAULT_TOOLSETS evaluates to DEFAULT_TOOLSETS - Line 192 checked truthiness: None is falsy, falling through to else Fix: - Use 'is not None' checks instead of truthiness - When enabled_toolsets is None, derive effective toolsets from parent_agent.valid_tool_names via the tool registry Fixes the bug introduced in f75b1d2 and repeated in e5d1444 (PR NousResearch#3269).
angelburgosrosado
pushed a commit
to angelburgosrosado/hermes-agent
that referenced
this pull request
Apr 27, 2026
…usResearch#3269) The delegate_task tool accepts a toolsets parameter directly from the LLM's function call arguments. When provided, these toolsets are passed through _strip_blocked_tools but never intersected with the parent agent's enabled_toolsets. A model can request toolsets the parent does not have (e.g., web, browser, rl), granting the subagent tools that were explicitly disabled for the parent. Intersect LLM-requested toolsets with the parent's enabled set before applying the blocked-tool filter, so subagents can only receive a subset of the parent's tools. Co-authored-by: dieutx <dangtc94@gmail.com>
angelburgosrosado
pushed a commit
to angelburgosrosado/hermes-agent
that referenced
this pull request
Apr 27, 2026
When parent_agent.enabled_toolsets is None (the default, meaning all tools are enabled), subagents incorrectly fell back to DEFAULT_TOOLSETS (['terminal', 'file', 'web']) instead of inheriting the parent's full toolset. Root cause: - Line 188 used 'or' fallback: None or DEFAULT_TOOLSETS evaluates to DEFAULT_TOOLSETS - Line 192 checked truthiness: None is falsy, falling through to else Fix: - Use 'is not None' checks instead of truthiness - When enabled_toolsets is None, derive effective toolsets from parent_agent.valid_tool_names via the tool registry Fixes the bug introduced in 9f5702d and repeated in 4fe09e8 (PR NousResearch#3269).
02356abc
pushed a commit
to 02356abc/hermes-agent
that referenced
this pull request
May 14, 2026
…usResearch#3269) The delegate_task tool accepts a toolsets parameter directly from the LLM's function call arguments. When provided, these toolsets are passed through _strip_blocked_tools but never intersected with the parent agent's enabled_toolsets. A model can request toolsets the parent does not have (e.g., web, browser, rl), granting the subagent tools that were explicitly disabled for the parent. Intersect LLM-requested toolsets with the parent's enabled set before applying the blocked-tool filter, so subagents can only receive a subset of the parent's tools. Co-authored-by: dieutx <dangtc94@gmail.com>
02356abc
pushed a commit
to 02356abc/hermes-agent
that referenced
this pull request
May 14, 2026
When parent_agent.enabled_toolsets is None (the default, meaning all tools are enabled), subagents incorrectly fell back to DEFAULT_TOOLSETS (['terminal', 'file', 'web']) instead of inheriting the parent's full toolset. Root cause: - Line 188 used 'or' fallback: None or DEFAULT_TOOLSETS evaluates to DEFAULT_TOOLSETS - Line 192 checked truthiness: None is falsy, falling through to else Fix: - Use 'is not None' checks instead of truthiness - When enabled_toolsets is None, derive effective toolsets from parent_agent.valid_tool_names via the tool registry Fixes the bug introduced in f75b1d2 and repeated in e5d1444 (PR NousResearch#3269).
gweeteve
pushed a commit
to gweeteve/hermes-agent
that referenced
this pull request
Jun 2, 2026
…usResearch#3269) The delegate_task tool accepts a toolsets parameter directly from the LLM's function call arguments. When provided, these toolsets are passed through _strip_blocked_tools but never intersected with the parent agent's enabled_toolsets. A model can request toolsets the parent does not have (e.g., web, browser, rl), granting the subagent tools that were explicitly disabled for the parent. Intersect LLM-requested toolsets with the parent's enabled set before applying the blocked-tool filter, so subagents can only receive a subset of the parent's tools. Co-authored-by: dieutx <dangtc94@gmail.com>
gweeteve
pushed a commit
to gweeteve/hermes-agent
that referenced
this pull request
Jun 2, 2026
When parent_agent.enabled_toolsets is None (the default, meaning all tools are enabled), subagents incorrectly fell back to DEFAULT_TOOLSETS (['terminal', 'file', 'web']) instead of inheriting the parent's full toolset. Root cause: - Line 188 used 'or' fallback: None or DEFAULT_TOOLSETS evaluates to DEFAULT_TOOLSETS - Line 192 checked truthiness: None is falsy, falling through to else Fix: - Use 'is not None' checks instead of truthiness - When enabled_toolsets is None, derive effective toolsets from parent_agent.valid_tool_names via the tool registry Fixes the bug introduced in f75b1d2 and repeated in e5d1444 (PR NousResearch#3269).
waefrebeorn
pushed a commit
to waefrebeorn/slermes
that referenced
this pull request
Jul 2, 2026
…usResearch#3269) The delegate_task tool accepts a toolsets parameter directly from the LLM's function call arguments. When provided, these toolsets are passed through _strip_blocked_tools but never intersected with the parent agent's enabled_toolsets. A model can request toolsets the parent does not have (e.g., web, browser, rl), granting the subagent tools that were explicitly disabled for the parent. Intersect LLM-requested toolsets with the parent's enabled set before applying the blocked-tool filter, so subagents can only receive a subset of the parent's tools. Co-authored-by: dieutx <dangtc94@gmail.com>
waefrebeorn
pushed a commit
to waefrebeorn/slermes
that referenced
this pull request
Jul 2, 2026
When parent_agent.enabled_toolsets is None (the default, meaning all tools are enabled), subagents incorrectly fell back to DEFAULT_TOOLSETS (['terminal', 'file', 'web']) instead of inheriting the parent's full toolset. Root cause: - Line 188 used 'or' fallback: None or DEFAULT_TOOLSETS evaluates to DEFAULT_TOOLSETS - Line 192 checked truthiness: None is falsy, falling through to else Fix: - Use 'is not None' checks instead of truthiness - When enabled_toolsets is None, derive effective toolsets from parent_agent.valid_tool_names via the tool registry Fixes the bug introduced in ff89ef3 and repeated in eb12e9d (PR NousResearch#3269).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Salvaged from PR #3188 by @dieutx — cherry-picked onto current main with original authorship preserved.
Root cause
_build_child_agent()passes LLM-provided toolsets through_strip_blocked_tools()which only removes a hardcoded blocklist (delegation,clarify,memory,code_execution). It never intersects with the parent'senabled_toolsets. A model callingdelegate_task(toolsets=["terminal", "file", "web", "browser"])when the parent only has["terminal", "file"]grants the subagentwebandbrowser— a privilege escalation.Fix
Intersect LLM-requested toolsets with the parent's enabled set before filtering:
3 lines of production code. The no-toolsets path (child inherits parent) and blocked-tools filter are unchanged.
Validation
python -m pytest tests/tools/test_delegate_toolset_scope.py tests/tools/test_delegate.py -n0 -q→ 54 passedCloses #3188
Co-authored-by: dieutx dangtc94@gmail.com