NousResearch/hermes-agent

Gateway staging fix for local media files

Cluster 38106. Data as of

Nothing open on GitHub. Fix merged in #39063 on 4 Jun 2026. Check it covers your case.

The short answer

0open issues
0open PRs
3items grouped
Known?
Yes. 3 related items grouped here, 1 issue already closed.
Fix event
A fix was merged to main in PR #39063. Check it covers your case. No release data is tracked.
Tracker
The maintainers' tracker marks this resolved, deciding PR #39063. That is separate from the fix event.
Still open
Nothing open on GitHub.

These items address a bug where the gateway silently drops safe local media files from certain working directories (e.g. /root) instead of staging them, along with corresponding fixes to ensure proper staging and delivery.

Open issues0

No open issues.

Open PRs0

No open pull requests.

Fixes on record

Merged to main. A stamp counts the issues a fix is linked to.

  • #39063 fix(gateway): deliver $HOME deliverables on root-run gateways

    merged to main

    files: gateway/platforms/base.py, tests/gateway/test_platform_base.py

    Show the diff
    • gateway/platforms/base.py
      @@ -967,14 +967,35 @@ def _media_delivery_denied_paths() -> List[Path]:
       
       
       def _path_under_denied_prefix(resolved: Path) -> bool:
      -    """Return True if ``resolved`` lives under a deny-listed system path."""
      +    """Return True if ``resolved`` lives under a deny-listed system path.
      +
      +    One narrow exception: when a denied prefix IS the running user's own home,
      +    the home itself is not treated as denied. ``/root`` is on the system-path
      +    denylist so that a non-root gateway can't deliver another user's home, but
      +    on a root-run gateway ``$HOME=/root`` and the operator's own deliverables
      +    (``/root/work/proposal.docx``) live directly under it. The credential
      +    sub-directories inside home (``~/.ssh``, ``~/.aws``, ...) and Hermes
      +    secrets (``~/.hermes/.env``, ``auth.json``) are *separate, more-specific*
      +    denied paths, so they stay blocked regardless of this exception — it can
      +    only un-block a plain file sitting in the running user's home tree, never a
      +    credential location or another user's home.
      +    """
      +    try:
      +        home = Path(os.path.expanduser("~")).resolve(strict=False)
      +    except (OSError, RuntimeError, ValueError):
      +        home = None
           for denied in _media_delivery_denied_paths():
               try:
                   resolved_denied = denied.expanduser().resolve(strict=False)
               except (OSError, RuntimeError, ValueError):
                   continue
      -        if _path_is_within(resolved, resolved_denied) or resolved == resolved_denied:
      -            return True
      +        if not (_path_is_within(resolved, resolved_denied) or resolved == resolved_denied):
      +            continue
      +        # Allow the running user's own home tree; its credential sub-dirs are
      +        # caught by their own (more-specific) denylist entries above.
      +        if home is not None and resolved_denied == home:
      +            continue
      +        return True
           return False
       
       
    • tests/gateway/test_platform_base.py
      @@ -954,6 +954,120 @@ def test_filter_passes_default_files_through(self, tmp_path, monkeypatch):
               out = BasePlatformAdapter.filter_local_delivery_paths([str(notes)])
               assert out == [str(notes.resolve())]
       
      +    def test_root_home_deliverable_is_accepted(self, tmp_path, monkeypatch):
      +        """The motivating bug (#38106): a root-run gateway has ``$HOME=/root``,
      +        which is on the system-prefix denylist. A plain deliverable the agent
      +        produced in its working dir (``/root/work/proposal.docx``) must still
      +        deliver — the home itself is not a credential location.
      +        """
      +        self._patch_roots(monkeypatch)
      +
      +        fake_home = tmp_path / "root"
      +        workdir = fake_home / "work"
      +        workdir.mkdir(parents=True)
      +        doc = workdir / "proposal.docx"
      +        doc.write_bytes(b"PK\x03\x04")
      +        monkeypatch.setenv("HOME", str(fake_home))
      +        # $HOME is itself on the denied-prefix list, mirroring /root.
      +        monkeypatch.setattr(
      +            "gateway.platforms.base._MEDIA_DELIVERY_DENIED_PREFIXES",
      +            (str(fake_home),),
      +        )
      +
      +        assert (
      +            BasePlatformAdapter.validate_media_delivery_path(str(doc))
      +            == str(doc.resolve())
      +        )
      +
      +    def test_root_home_credential_subdir_still_blocked(self, tmp_path, monkeypatch):
      +        """The $HOME exception must NOT un-block credential sub-dirs inside
      +        home. ``/root/.ssh/id_rsa`` stays denied because ``~/.ssh`` is a
      +        separate, more-specific denylist entry — even when $HOME is itself a
      +        denied prefix.
      +        """
      +        self._patch_roots(monkeypatch)
      +
      +        fake_home = tmp_path / "root"
      +        ssh_dir = fake_home / ".ssh"
      +        ssh_dir.mkdir(parents=True)
      +        key = ssh_dir / "id_rsa"
      +        key.write_bytes(b"[redacted:private-key]

1 PR closed without merging

From the close comments: superseded by another PR 1

  • #38108 fix(gateway): stage safe local MEDIA files before delivery · superseded by another PR → #39063
Closed issues · 1
  • Closed#38106[Bug]: Gateway silently drops safe local MEDIA files from working dirs (e.g. /root) instead of staging them
How this cluster was grouped
  • Link evidence (structural links throughout): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
  • Pairwise link density 0.67: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. 1.00 means every pair is linked.

Pick up a Lab task

Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.

Open the Lab

At a glance

Merged PRs
#39063
Closed issues
1
Attempts
1 PR closed without merging
Fix link checked
4 Jun 2026

For your agent

The same facts, typed.