Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

fix(agent): browser tools leak owner state, and can key a permission on the wrong site

Open
#600 1 comment 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
48/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Active
Tech stack
python
Domain
devtools, security

Research direction

Start in raven/agent/browser.py at lines 179, 231, and 551, then read the related _mark, _touched, _reap_owners, Browser.release, url_for, and _page_for paths. Reproduce the owner-retention case and the concurrent-owner permission case at fe3b6278f before choosing between the two stated binding approaches. Done means owner state follows the binding lifetime, permission text identifies the page actually acted on, and browser_tabs explains the unowned-tab close rule.

Written by the indexing model from the issue text.

Description

Two defects in the browser tools that #578 shipped, found in a post-merge review of its raven/agent/** slice. Both are on main at 608cedf18, both are nonblocking, and neither was raised by the two rounds that ran while the PR was open. The full review, including three observations outside raven/agent/**, is the summary on #578.

1. _BrowserTool._acted is never pruned (browser.py:179)

_BrowserTool._acted is class-level state keyed by owner, written by _mark() and read by _touched(), with no path that ever removes an entry. Since the owner is now run:<uid>, every delegated run that acts on the browser adds a key that outlives it for the life of the process.

Reproduced at fe3b6278f: 50,000 activity.collecting() runs each calling _mark(current_owner()) leave 50,000 live entries and 6.6 MiB retained after gc.collect(). The same loop under the id(run) key this replaced produced 2 distinct keys, because addresses recycle - so the correct fix for the tab binding also turned a self-collapsing key space into one that never collapses.

The driver half of the same pairing does reap: _reap_owners drops a binding past OWNER_IDLE_S, and Browser.release(owner) exists for the explicit case (currently uncalled). The tool-side map that mirrors it has neither. Dropping _acted[owner] wherever the binding is released, or aging entries on the same clock, would keep the two halves on one lifetime.

2. A permission key can name a site the call will not act on (browser.py:231)

_ActingTool.cast_params resolves the site from url_for(owner), which falls back to the panel's active page when the owner has no live binding. If that active tab is held by another owner, _page_for then opens a fresh tab and the action lands there instead - so the site the gate is asked about is a page the call never touches.

Reproduced at fe3b6278f. A parent owner holds the active tab at bank.test; a delegated run whose first browser call is browser_press:

site written into the permission call : bank.test
page the keystroke actually landed on : about:blank
keystrokes delivered to bank.test     : []

And the grant that lands is reusable. session_keys returns one digest for all three acting verbs on a site, so the key banked by that call is identical to the key a later real click, type or press on bank.test would present:

mislabelled press : 235fc70fec980b49
later real press  : 235fc70fec980b49   same
later real type   : 235fc70fec980b49   same
later real click  : 235fc70fec980b49   same

Nonblocking because the person did see bank.test and approve it, so no grant appears for a site never shown, and the precondition is narrow: two concurrent owners, and the asking owner holding no live binding - its first browser call is an acting verb, since a snapshot would bind it, or its binding lapsed past OWNER_IDLE_S while a sibling holds the active tab. Worth fixing anyway, because the prompt is the one surface where the sentence shown to the person has to describe the call being approved, and the reachable case is the delegation case this feature exists for.

Two directions, and the choice between them is a design call rather than a patch: resolve the site after _page_for has bound the owner, or have url_for answer empty for an unbound owner instead of falling back to the panel, which would make the call key per-call until the owner has a page of its own.

3. Minor: browser_tabs does not say that close refuses an unheld tab (browser.py:551)

Its description names one thing a call cannot do, taking a tab "marked held". Since fe3b6278f that is no longer the whole rule - tab_close also refuses every tab with no owner, because an unowned tab is the reader's and may hold the login HANDOFF_NOTE just asked them to complete. The listing marks such a tab with neither yours nor held, so nothing the model reads tells it apart from one it may close. The recovery (activate, then close what you hold) is written only in tab_close's docstring, which the model never sees; the driver's refusal text does point at it, so the cost is one wasted call rather than a stuck agent.

Dominant language
Python
Stars
4.1k
Forks
94
Avg merge
10h 2m
Merged PRs (30d)
376

Getting set up

This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from EverMind-AI/Raven

All issues in EverMind-AI/Raven

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.