Repository navigation
Conversation
An empty sub-agent tool allowlist was fail-OPEN: the registration predicate
`allowed.is_empty() || allowed.contains(name)` treated an empty set as "all
tools visible", so a `tools = []` archetype, a zero-match `skill_filter`, or a
`named` list of unresolvable names silently inherited the parent's FULL surface
(shell, file-write, spawn/delegate) — a prompt-injection privilege escalation
for a deliberately tool-less summarizer processing untrusted content.
Change the plumbed allowlist type from `HashSet<String>` to
`Option<HashSet<String>>` end-to-end (subagent_runner ops/runner.rs ->
run_subagent_via_graph / AgentTurnRequest -> assemble_turn_harness ->
run_turn_via_tinyagents_shared) so registration distinguishes:
* None -> no filter supplied, register every candidate (parent/channel/
chat turns);
* Some(set) -> register ONLY names in `set`;
* Some({}) -> register NOTHING (deny-all).
Sub-agents always resolve to `Some(set)` (never None): `ToolScope::Named([])`
and a zero-match `skill_filter` now produce `Some(empty)` = deny-all, matching
v0.58.7 semantics. Parent channel/chat turns map an empty visibility set to
`None` (all tools), preserving today's behavior on those paths.
Defense in depth: strip spawn/delegate tool names (is_subagent_spawn_tool +
`spawn_worker_thread`) at registration regardless of the allowlist, so the
"sub-agents must never spawn" invariant holds even under a widened/None
allowlist — not only via `allowed_indices`. Warn-log when the resolved
allowlist is empty so misconfigured agents are diagnosable.
The shadow tool-exposure middleware mirrors the same None/Some(set)/Some(empty)
semantics.
Tests: unit tests over the registration gate (None / Some(empty) /
Some({named}) / named-unresolvable / spawn-always-stripped) plus an e2e that a
tool-less (`Some(empty)`) sub-agent over a shell-bearing surface registers no
tools yet completes a text-only turn.
Claude-Session: https://claude.ai/code/session_019j5TLsRLHsM3kqAYFyH4hR
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
senamakel
pushed a commit
that referenced
this pull request
Oct 16, 2026
- #6: the service trusts every files folder (`tiny_store::file_roots`), so an artifact made in an earlier one can still be revised. - #8: a failed revision keeps the artifact: the record keeps its versions and the card shows the newest of them again. - #9: a failure the observer could not show fails the card directly, and a completion that records nothing fails the record too; no card stays "generating". - #10: listing a thread's artifacts (the row backfill, the UI) queries the `by_thread` index instead of reading the whole workspace. - #4: the open preview re-reads the artifact when a new version is saved (an agent revision) unless an edit is pending; the panel is keyed per artifact. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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
Fixes a fail-OPEN sub-agent tool allowlist (prompt-injection privilege escalation). Source issue: tinyhumansai#4452.
The registration predicate in
src/openhuman/tinyagents/mod.rswasallowed.is_empty() || allowed.contains(name)— an empty allowlist registered every tool acrosstool_sets(which includes the parent'sshell, file-write, and spawn/delegate tools). A deliberately tool-less sub-agent (tools = []archetype, a zero-matchskill_filter, or anamedlist of unresolvable names) therefore silently inherited the parent's full surface.Fix
Option<HashSet<String>>end-to-end (subagent_runner/ops/runner.rs→run_subagent_via_graph/AgentTurnRequest→assemble_turn_harness→run_turn_via_tinyagents_shared), so registration distinguishes:None→ no filter supplied, register every candidate (parent/channel/chat turns);Some(set)→ register only names inset;Some({})→ register nothing (deny-all).Some(set).ToolScope::Named([])and zero-matchskill_filterproduceSome(empty)= deny-all (v0.58.7 parity). Parent channel/chat turns map an empty visibility set toNone(all tools), preserving current behavior there.is_subagent_spawn_tool+spawn_worker_thread) at registration regardless of the allowlist, so the "sub-agents must never spawn" invariant holds even under a widened/Noneallowlist.[subagent] tool allowlist resolved empty …) when the resolved allowlist is empty.None/Some(set)/Some(empty)semantics.Acceptance criteria
tools = []/ zero-match filters register zero tools; the sub-agent still completes text-only turns.None,Some(empty),Some({named}), named-but-unresolvable, plus spawn-always-stripped; e2e that aSome(empty)sub-agent over a shell-bearing surface has noshell.Verified
cargo check --manifest-path Cargo.toml --lib→ clean finish (EXIT=0).cargo check --manifest-path Cargo.toml --lib --tests→ clean finish (EXIT=0), so the new tests compile.cargo fmtapplied.Not verified
cargo testlink step is too heavy for this memory-constrained box); relying on PR CI to run them. They are written and compile.vendor/tinyagents(submodule pointer unchanged); the fix lives entirely in the openhuman seam.https://claude.ai/code/session_019j5TLsRLHsM3kqAYFyH4hR