fix(tui,gateway): preserve explicit empty tool allowlists and fix failing check regressions#665
fix(tui,gateway): preserve explicit empty tool allowlists and fix failing check regressions#665badMade wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
Code Review
This pull request ensures that empty toolset allowlists (i.e., []) are preserved instead of being coerced to None or falling back to default toolsets, and adds corresponding unit tests. However, the implementation in _background_agent_kwargs uses getattr(agent, "enabled_toolsets", None) is not None to check for the attribute, which introduces a bug when enabled_toolsets is explicitly set to None (representing all tools enabled). In this case, it incorrectly falls back to _load_enabled_toolsets(). Using hasattr(agent, "enabled_toolsets") is recommended to properly handle this scenario.
| "enabled_toolsets": ( | ||
| agent.enabled_toolsets | ||
| if getattr(agent, "enabled_toolsets", None) is not None | ||
| else _load_enabled_toolsets() | ||
| ), |
There was a problem hiding this comment.
Using getattr(agent, "enabled_toolsets", None) is not None to check if the parent agent has an explicit toolset allowlist introduces a bug when the parent agent has enabled_toolsets = None (which represents "all tools enabled").
In this case, getattr(agent, "enabled_toolsets", None) returns None, causing the condition to evaluate to False and falling back to _load_enabled_toolsets(). If _load_enabled_toolsets() returns a restricted list (e.g., ["web"]), the background agent will be restricted to ["web"] even though the parent agent had all tools enabled (None).
Using hasattr(agent, "enabled_toolsets") instead correctly preserves None while still falling back to _load_enabled_toolsets() if the attribute is missing entirely.
| "enabled_toolsets": ( | |
| agent.enabled_toolsets | |
| if getattr(agent, "enabled_toolsets", None) is not None | |
| else _load_enabled_toolsets() | |
| ), | |
| "enabled_toolsets": ( | |
| agent.enabled_toolsets | |
| if hasattr(agent, "enabled_toolsets") | |
| else _load_enabled_toolsets() | |
| ), |
🔎 Lint report:
|
| Rule | Count |
|---|---|
invalid-argument-type |
3 |
unsupported-operator |
1 |
First entries
gateway/platforms/qqbot/adapter.py:1211: [invalid-argument-type] invalid-argument-type: Argument to function `QQAdapter._merge_quote_into` is incorrect: Expected `str`, found `str | Unknown | (list[Unknown] & ~AlwaysFalsy)`
gateway/platforms/qqbot/adapter.py:1197: [unsupported-operator] unsupported-operator: Operator `+` is not supported between objects of type `str | Unknown` and `(Any & ~AlwaysFalsy) | (list[Unknown] & ~AlwaysFalsy) | (str & ~AlwaysFalsy)`
gateway/platforms/qqbot/adapter.py:1223: [invalid-argument-type] invalid-argument-type: Argument to function `QQAdapter._detect_message_type` is incorrect: Expected `list[Unknown]`, found `Any | list[Unknown] | str`
gateway/platforms/qqbot/adapter.py:1227: [invalid-argument-type] invalid-argument-type: Argument is incorrect: Expected `list[str]`, found `Any | list[Unknown] | str`
✅ Fixed issues (1):
| Rule | Count |
|---|---|
unresolved-reference |
1 |
First entries
gateway/run.py:5544: [unresolved-reference] unresolved-reference: Name `team_id` used when not defined
Unchanged: 4357 pre-existing issues carried over.
Diagnostics are surfaced as warnings — this check never fails the build.
There was a problem hiding this comment.
Pull request overview
This PR fixes a security-relevant “fail-open” behavior in the TUI toolset allowlist handling by preserving an explicitly empty allowlist ([]) end-to-end, rather than collapsing it into None (which is treated as “all toolsets enabled”).
Changes:
- Preserve explicit empty toolset allowlists by returning the resolved
enabledlist directly from_load_enabled_toolsets()(so[]stays[], whileNoneremains the “all toolsets” sentinel). - Preserve an agent parent’s explicit empty
enabled_toolsetswhen constructing background agent kwargs (avoid truthiness-based fallback). - Update TUI listing endpoints to correctly distinguish
None(all enabled) vs[](none enabled), and add regression tests for the previously failing cases.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
tui_gateway/server.py |
Fixes allowlist propagation and endpoint display logic so [] does not become “all enabled”. |
tests/test_tui_gateway_server.py |
Adds regression tests ensuring empty allowlists are preserved through config load, agent creation, and background agent kwargs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Auto-merge: review received — action requiredReview activity detected (1 inline comment(s) by non-author(s); 2 comment(s)/review(s) from review bot(s): gemini-code-assist[bot], copilot-pull-request-reviewer[bot]). To confirm you have reviewed and accepted the feedback, please add the |
|
@claude code review |
|
@jules code review |
|
@copilot, fix all failing checks. |
Auto-merge: checks failingThe following checks did not pass:
Please fix the failing checks before this PR can be merged. |
Addressed the failing checks in commit 8bfa5c2. I fixed the gateway auth |
Motivation
[]) was converted toNoneand interpreted as “all tools enabled”, re-exposing sensitive capabilities when operators explicitly disabled all tools.Description
enabledlist directly from_load_enabled_toolsets()instead ofenabled or Noneso[]remains[]whileNonestill signals “no filtering / all tools”.enabled_toolsetsin_background_agent_kwargs()by using the parent'senabled_toolsetswhen it is notNoneinstead of falling back on truthiness.tools.list,toolsets.list,tools.show) to distinguishNone(all-tools sentinel) from[](explicit no-tools) when computing what to display/return, and to computeenabledasset(enabled_toolsets or [])while usingenabled_toolsets is Noneto mean “show everything enabled”.tests/test_tui_gateway_server.py.team_idinitialization inGatewayRunner._is_user_authorizedto avoidNameErrorand preserve workspace-scoped auth checks;${WEBHOOK_SECRET}) in webhook signature validation;Testing
python -m ruff check tui_gateway/server.py tests/test_tui_gateway_server.py gateway/run.py gateway/platforms/webhook.py gateway/platforms/qqbot/adapter.pypytest -q tests/test_tui_gateway_server.pytests/gateway/test_unauthorized_dm_behavior.py::test_unauthorized_dm_pairs_by_defaulttests/gateway/test_webhook_adapter.py::TestValidateSignature::test_validate_placeholder_secret_rejects_literal_hmactests/gateway/test_qqbot.py::TestVoiceAttachmentSSRFProtection::test_unauthorized_c2c_skips_attachment_processingCodex Task