From 5507dae3d797eacb38cfa2429c46b233b212a785 Mon Sep 17 00:00:00 2001 From: Nathan Esquenazi Date: Tue, 14 Apr 2026 19:35:52 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20restrict=20/api/media=20allowed=20roots?= =?UTF-8?q?=20=E2=80=94=20remove=20~=20(home=20dir)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/routes.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/api/routes.py b/api/routes.py index 8eadcef..777c7c1 100644 --- a/api/routes.py +++ b/api/routes.py @@ -1521,13 +1521,22 @@ def _handle_media(handler, parsed): except Exception: return bad(handler, "Invalid path", 400) - # Allowed roots: hermes home, /tmp, common screenshot cache dirs + # Allowed roots: hermes home, /tmp, and active workspace. + # Intentionally NOT the entire home dir — that would expose ~/.ssh, + # ~/.aws, browser profiles, etc. to any authenticated user. allowed_roots = [ _HERMES_HOME.resolve(), Path("/tmp").resolve(), (_HOME / ".hermes").resolve(), - _HOME.resolve(), # allow any file under the user's home ] + # Also allow the active workspace directory (where screenshots land) + try: + from api.workspace import get_last_workspace + ws = Path(get_last_workspace()).resolve() + if ws.is_dir(): + allowed_roots.append(ws) + except Exception: + pass within_allowed = any( _os.path.commonpath([str(target), str(root)]) == str(root) for root in allowed_roots