fix(agent): stop network tools being granted a standing approval - #6
Merged
voidstackloop merged 1 commit intoJul 25, 2026
Conversation
READ_ONLY_TOOLS gated the "always allow this session" button. It included capture_page_screenshot, which fetches a model-chosen URL and writes a PNG into the workspace — not read-only in either sense — along with web_search, fetch_url and the three github_* tools. Those five are read-only with respect to the workspace, but each sends a request to a destination the model chooses. Agent mode reads file contents and web pages, and instructions embedded in that content can steer later tool calls, so a standing grant on fetch_url becomes an outbound channel for whatever the model has already read — firing every turn with no prompt shown. The classification moves to lib/tool-approval.ts so it can be unit-tested (Chat.tsx has no component tests) and lists only what qualifies, keeping it default-deny for tools added later. Network tools still work exactly as before; they just keep their per-call prompt.
voidstackloop
approved these changes
Jul 25, 2026
voidstackloop
added a commit
that referenced
this pull request
Jul 25, 2026
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.
Problem
READ_ONLY_TOOLSinChat.tsxgates the "Always allow this session" button. Once a tool is in that set and the user grants it, every later call resolves itself with no confirmation card for the rest of the session.The set is named for the wrong predicate. It contains six tools that don't modify the workspace but are not side-effect free:
capture_page_screenshot— loads a model-chosen URL in a real BrowserWindow and writes a PNG into the workspace. Not read-only in either sense.fetch_url,web_search,github_read_file,github_list_repositories,github_repository_tree— each sends a request to a destination the model chooses. The GitHub ones authenticate with the user's linked token.Agent mode's inputs are not trusted: it reads file contents and web pages, and instructions embedded in that content can steer subsequent tool calls. A standing grant on
fetch_url— the most natural click in the product during any research task — is therefore an unattended outbound channel for whatever the model has already read, firing on every turn with nothing shown to the user.networkToolsEnableddefaults to true, so nothing else stands in the way.This needs no shell injection or other bug to reach; the classification alone is enough.
Fix
The classification moves to
lib/tool-approval.tsand is drawn on "the user has nothing to lose by never being asked again" rather than "doesn't write to the workspace". Workspace-local reads keep the standing grant; the six above keep their per-call prompt.It lists what qualifies rather than what doesn't, so it stays default-deny — a tool added to
AGENT_TOOLSlater needs per-call approval until someone classifies it deliberately. That property already held via the old set and is worth keeping.It is a separate module mainly so it can be unit-tested;
Chat.tsxhas no component tests, and this is a rule worth pinning down. It follows the existingfrontend/src/lib/*.ts+ co-located*.test.tsconvention.Nothing is disabled. All six tools work exactly as before — they just don't get a standing approval.
What this doesn't fix
I'd rather flag this than let the PR imply more than it delivers: approval is enforced in the renderer, not behind the IPC boundary.
ipcMain.handle("tools:execute")has no notion of approval, andrunVerificationinChat.tsxalready callsrun_commandoutside the approval flow. So this narrows one specific unattended channel; it does not make the approval boundary sound. Happy to open a separate issue for that if it's useful.Trade-off
This is the one change in the batch that touches product behaviour rather than fixing a clear defect, so it's your call. The cost is real:
web_searchandfetch_urlare the most repeatedly-called tools in a research loop, and this makes each one a click. If you'd rather keep the convenience, a reasonable alternative is a separate opt-in setting for network-tool auto-approval, so the default is safe but the workflow is recoverable. I'm happy to rework it that way, or to drop it tocapture_page_screenshotonly — that one writes to the workspace from a set namedREAD_ONLY_TOOLS, which looks like a plain misclassification regardless of where you land on the network question.Tests
lib/tool-approval.test.ts— workspace-local reads qualify; each network tool does not;capture_page_screenshotdoes not; effectful tools do not; an unknown tool name defaults to denied.npm run lint,npx tsc -b,npm test(73 passed) andnpm run buildall clean.Ordering
Worth landing #2 first.
git_diffstays auto-approvable here, and that's only defensible once its path argument can't reach the shell.