fix(agent): make _last_resolved_tool_names thread-local to prevent cross-session tool bleed - #70757
fix(agent): make _last_resolved_tool_names thread-local to prevent cross-session tool bleed#70757necoweb3 wants to merge 1 commit into
Conversation
…oss-session tool bleed
|
suggesting changes The implementation replaces the process-global fallback tool-name list with per-thread storage and converts every repository consumer, and focused probes show that it prevents a restricted gateway worker from observing a concurrent privileged worker's tool names. The PR patch also applies cleanly to the current-main tree. However, the committed tests only rename accesses in existing single-thread tests; none reproduces the cross-thread security failure or proves the new isolation property. Because this is a tool-capability boundary and a global-backed getter/setter would pass the current tests while retaining the vulnerability, a deterministic concurrent regression test is required.
Security evidence:
Uncertainty: The complete focused pytest suites could not run because the checkout has no pytest-capable environment; no committed concurrent test demonstrates the security invariant, so future refactors would not be protected from reintroducing process-global storage. Signed: GPT-5.6-sol-xhigh in Codex |
teknium1
left a comment
There was a problem hiding this comment.
Thanks for isolating the mutable fallback state. Current main still writes _last_resolved_tool_names globally in model_tools.py:341-342 and model_tools.py:536-537, and consumes it for an execute_code call without explicit enabled_tools at model_tools.py:1324-1333.
Problems
- The changed tests only rename same-thread accesses (
tests/tools/test_delegate.py:562in this PR). They would pass if the getter/setter still wrapped a process-global list. Add a deterministicthreading.Barrierregression with two disjoint worker values and an unset-thread[]assertion. - The claimed default-gateway route needs a more precise reproduction: normal agent execution passes
agent.valid_tool_namesexplicitly in both branches ofagent/tool_executor.py:1682-1686andagent/tool_executor.py:1752-1756. Test the realenabled_tools=Nonefallback path or narrow the claim to that compatibility fallback.
Suggested changes
- Reconcile the conflict against current main and retain the current delegate lifecycle locations in
tools/delegate_tool.py:1983-1985,:2566-2568, and:2621-2626.
Automated hermes-sweeper review.
| @@ -562,7 +562,7 @@ def test_global_tool_names_restored_after_delegation(self): | |||
|
|
|||
There was a problem hiding this comment.
This remains a same-thread restoration test, so a process-global getter/setter would still pass. Please add a barrier-controlled two-thread regression with disjoint values plus an unset-thread [] assertion to protect the isolation property.
What
_last_resolved_tool_namesinmodel_tools.pyis a process-globalList[str]that is overwritten on every call toget_tool_definitions()(line 341 on cache hit, line 536 on fresh compute). Whenhandle_function_callis called forexecute_codewithenabled_tools=None(line 1307), the sandbox falls back to this global.The gateway runs 10 concurrent agent sessions via
ThreadPoolExecutor. Session A callsget_tool_definitions()(overwriting the global with session A's toolset), and session B simultaneously callshandle_function_call("execute_code", ...)withenabled_tools=None. Session B's sandbox picks up session A's tool list -- granting a restricted session access to tools it should not have (e.g.terminal,web_search,delegate_task).Fix
Replaced the process-global
List[str]withthreading.local()storage. Each session's thread gets its own copy via_get_last_resolved_tool_names()/_set_last_resolved_tool_names()helpers.model_tools.py: Declaration changed tothreading.local(), getter/setter functions added. Cache-hit path (line 341) and fresh-compute path (line 536) now call_set_last_resolved_tool_names().execute_codefallback (line 1307) calls_get_last_resolved_tool_names().tools/delegate_tool.py: All 4 references updated to use getter/setter.tests/tools/test_delegate.py: Test assertions updated.tests/tools/test_tool_search.py: Test assertions updated.PR #34451 attempted this fix but was closed without merge.
Why
Cross-session privilege escalation: a restricted user session can inherit tools from another user's session context in the default gateway configuration.
How to Test