hermes - ✅(Solved) Fix Security: browser orphan reaper trusts /tmp PID files and can SIGTERM arbitrary same-user processes [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#14073Fetched 2026-04-23 07:46:54
View on GitHub
Comments
0
Participants
1
Timeline
5
Reactions
0
Author
Participants
Timeline (top)
labeled ×4cross-referenced ×1

The browser orphan reaper trusts PID files found in predictable temp directories under /tmp and sends SIGTERM to any existing same-user process referenced there, without verifying that the PID actually belongs to an Hermes browser daemon.

Root Cause

The browser orphan reaper trusts PID files found in predictable temp directories under /tmp and sends SIGTERM to any existing same-user process referenced there, without verifying that the PID actually belongs to an Hermes browser daemon.

PR fix notes

PR #14394: fix(browser): verify daemon pid before orphan reap

Description (problem / solution / changelog)

Summary\n- Fixes #14073.\n- Adds a best-effort command-line identity check before the browser orphan reaper sends SIGTERM to a live PID from a legacy socket-dir pid file.\n- Keeps normal orphan cleanup for verified agent-browser daemons, but refuses to kill planted/non-agent-browser PIDs from predictable temp paths.\n- Adds regression coverage for a fake socket dir pointing at a non-browser process.\n\n## Root cause\nThe orphan reaper already uses owner_pid files for newer sessions, but its legacy fallback still trusted bare pid files in predictable temp directories. A same-user local actor could create an agent-browser-like directory and point the pid file at an unrelated same-user process.\n\n## Tests\n- source /Users/stephenyu/Documents/hermes-agent/.venv/bin/activate && python -m pytest tests/tools/test_browser_orphan_reaper.py\n- source /Users/stephenyu/Documents/hermes-agent/.venv/bin/activate && python -m pytest tests/tools/test_browser_hardening.py tests/tools/test_browser_ssrf_local.py tests/tools/test_browser_homebrew_paths.py\n- git diff --check

Changed files

  • tests/tools/test_browser_orphan_reaper.py (modified, +31/-2)
  • tools/browser_tool.py (modified, +58/-0)
RAW_BUFFERClick to expand / collapse

Summary

The browser orphan reaper trusts PID files found in predictable temp directories under /tmp and sends SIGTERM to any existing same-user process referenced there, without verifying that the PID actually belongs to an Hermes browser daemon.

Affected code

  • tools/browser_tool.py:492-547
  • tools/browser_tool.py:521-554

Relevant flow:

  1. _reap_orphaned_browser_sessions() scans /tmp/agent-browser-h_* and /tmp/agent-browser-cdp_*
  2. Reads <session_name>.pid
  3. Calls os.kill(pid, 0) to check existence
  4. If the process exists and the session is not tracked, it calls os.kill(pid, signal.SIGTERM)
  5. It does not verify the process command line/executable/ownership of the temp dir beyond same-user signal permissions

Impact

Any local actor able to create files in the shared temp directory can plant a fake browser session directory and PID file pointing at an arbitrary victim process owned by the same user.

On Hermes startup / cleanup thread initialization, Hermes will treat that PID as an orphaned browser daemon and terminate it.

That creates a local arbitrary-process DoS primitive against same-user processes.

Reproduction sketch

  1. Create /tmp/agent-browser-h_fake/
  2. Write a PID file h_fake.pid containing the PID of any long-running process owned by the same user
  3. Start Hermes (or trigger the browser cleanup thread startup path)
  4. Hermes reads the fake PID file and sends SIGTERM to the target PID

Suggested fix

Before killing, verify the target process really is an expected Hermes browser daemon, e.g. by checking:

  • process command line / executable path
  • session metadata bound to a private runtime directory
  • restrictive tempdir permissions / per-profile private state directory instead of shared /tmp globbing

A stronger fix is to avoid trusting bare PID files from shared temp storage at all and use validated runtime state or process handles.

Severity

Medium — local same-user denial of service via attacker-controlled PID file in shared temp storage.

extent analysis

TL;DR

Verify the target process is an expected Hermes browser daemon before sending SIGTERM to prevent local arbitrary-process DoS.

Guidance

  • Check the process command line/executable path to ensure it matches the expected Hermes browser daemon.
  • Validate session metadata bound to a private runtime directory to confirm the process is a legitimate browser session.
  • Consider using restrictive tempdir permissions or per-profile private state directories instead of shared /tmp globbing to prevent unauthorized access.
  • Before implementing a fix, reproduce the issue using the provided reproduction sketch to understand the attack vector.

Example

import psutil

def verify_process(pid):
    try:
        process = psutil.Process(pid)
        # Check if the process command line matches the expected Hermes browser daemon
        if process.cmdline() == ['hermes-browser-daemon']:
            return True
    except psutil.NoSuchProcess:
        pass
    return False

Notes

The suggested fix requires modifications to the tools/browser_tool.py file, specifically the _reap_orphaned_browser_sessions() function. The changes should focus on verifying the target process before sending SIGTERM.

Recommendation

Apply a workaround by modifying the _reap_orphaned_browser_sessions() function to verify the target process is an expected Hermes browser daemon before sending SIGTERM, as this will prevent local arbitrary-process DoS attacks.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING