Skip to content

Agent bypasses "web search off" via trigger_research — research tool is not treated as a web tool #5527

Description

@Ninthkitten

Summary

With the composer web toggle OFF (allow_web_search=false), asking the agent to
"search online for X" still results in full internet access: the agent calls
trigger_research ({"topic": "X"}), which launches a multi-round deep research
job — arguably far MORE web traffic than the single web_search call the user
explicitly disabled. Screenshot-verified: user sent "search online for beans"
with web tools toggled off; agent ran TRIGGER_RESEARCH and a research job started.

Root cause

src/tool_policy.py:19:

WEB_TOOL_NAMES = frozenset({"web_search", "web_fetch"})

When web search is denied for a turn, only these two tools are disabled
(routes/chat_routes.py ~883). trigger_research is not in the set, and its own
gating (tool_policy.blocks("trigger_research")) only fires via the per-user
can_use_research privilege or the global disabled-tools list — the per-turn
web toggle never affects it. The tool description even trains the model to
reach for it on search-like requests ("Use for 'research X', 'look into Y'"),
so when web_search is missing, trigger_research is the natural fallback.

Expected

"Web search off" should mean the agent cannot reach the internet that turn —
including research. A user who explicitly disabled web lookups does not expect
a bigger web pipeline to run instead.

Suggested fix

Include trigger_research (and arguably manage_research's start-adjacent
paths) in the per-turn web gating: when web_search_enabled_for_turn(...) is
false, add trigger_research to disabled_tools alongside WEB_TOOL_NAMES.
Users who want research-but-not-search can still enable it via the explicit
research toggle/slash command, which sets its own flag.

Workaround

Admin → user privileges → disable "Deep research" (can_use_research), or add
trigger_research to the global disabled-tools list. Both are blunt (all-or-
nothing) but effective.

Verified against current dev (2026-07-14, post-#5313)

PR #5313 correctly hard-gates web_search/web_fetch behind explicit per-turn settings — but the gate is built on WEB_TOOL_NAMES = frozenset({"web_search", "web_fetch"}) in src/tool_policy.py:19, and trigger_research is not in the set. The research pipeline still performs live web retrieval (via the bundled SearXNG) even when every web toggle is off, so the bypass this issue describes survives #5313.

Investigated and written by Claude (Fable 5), posted by the account owner.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ready for reviewDescription complete — ready for maintainer review

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions