{"repo":"NousResearch/hermes-agent","public_id":"33208","display_label":"Security and reliability fixes for gateway and subprocess","summary":"This group covers security-focused patches including restricting gateway quick commands to authorized users, reducing shell=True subprocess usage, and a reliability fix replacing blocking sleep with async sleep in MCP auth polling.","subsystem":"agent","counts":{"members":5,"open_issues":0,"open_issues_distinct":0,"open_prs":0,"closed_issues":2,"merged_prs":1,"attempt_prs":2},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":54128},"resolution":{"state":"partially_resolved","by":"pr-54128"},"freshness":{"state":"verified","last_verified_at":"2026-06-28T09:43:24+00:00","is_stale":false},"grouping":{"backbone":"STRUCTURAL_PARTIAL","link_density":0.33599999999999997},"fix_edges":[{"issue_number":44727,"pr_number":54128,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true}],"members":[{"kind":"issue","number":33208,"title":"Audit Fixes: 15 targeted bug fixes (security, perf, reliability)","url":null,"state":"closed"},{"kind":"issue","number":44727,"title":"[Security] Hermes gateway quick commands bypass admin-only slash command restrictions","url":null,"state":"closed"},{"kind":"pr","number":44791,"title":"fix(gateway): gate quick commands with slash access policy","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"9844243b1","number":0},{"kind":"pr","value":"#54128","number":54128},{"kind":"pr","value":"#44727","number":44727}],"category":"merged_via","implication":"superseded"},"merged":false},{"kind":"pr","number":45536,"title":"fix(security): gate gateway quick commands","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"9844243b1","number":0},{"kind":"pr","value":"#54128","number":54128},{"kind":"pr","value":"#44791","number":44791}],"category":"merged_via","implication":"superseded"},"merged":false},{"kind":"pr","number":54128,"title":"fix(gateway): gate quick_commands through slash access policy (#44727)","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["gateway/run.py","tests/gateway/test_slash_access_dispatch.py"],"fix_hunks":[{"file":"gateway/run.py","hunks":["@@ -8874,6 +8874,16 @@ async def _do_undo():\n             if not isinstance(quick_commands, dict):\n                 quick_commands = {}\n             if command in quick_commands:\n+                # Quick commands are slash capabilities too — and type:exec\n+                # ones run a shell command in the gateway process. The early\n+                # gate above only fires for registry-known commands, so quick\n+                # commands (never in the registry) would otherwise reach this\n+                # dispatch sink unchecked. Apply the same admin/user policy to\n+                # the raw typed name here so non-admins can't invoke admin-only\n+                # quick commands. (#44727)\n+                _denied = self._check_slash_access(source, command)\n+                if _denied is not None:\n+                    return _denied\n                 qcmd = quick_commands[command]\n                 if qcmd.get(\"type\") == \"exec\":\n                     exec_cmd = qcmd.get(\"command\", \"\")"]},{"file":"tests/gateway/test_slash_access_dispatch.py","hunks":["@@ -315,6 +315,74 @@ def fake_is_known(name):\n     assert \"/myplugin is admin-only here\" in result\n \n \n[redacted:email]\n+async def test_non_admin_denied_for_unlisted_quick_command_exec():\n+    \"\"\"A non-admin must not reach the quick_commands exec sink for a command\n+    that isn't in user_allowed_commands. Regression for #44727 — quick\n+    commands are never in the gateway registry, so the early gate skips them;\n+    the sink gate must catch them.\"\"\"\n+    runner = _make_runner(\n+        platform_extra={\n+            \"allow_admin_from\": [\"111\"],\n+            \"user_allowed_commands\": [],\n+        }\n+    )\n+    runner.config.quick_commands = {\n+        \"limits\": {\"type\": \"exec\", \"command\": \"printf quick-command-bypass-confirmed\"}\n+    }\n+\n+    result = await runner._handle_message(\n+        _make_event(\"/limits\", _make_source(user_id=\"999\"))\n+    )\n+\n+    assert result is not None\n+    assert \"⛔\" in result\n+    assert \"/limits is admin-only here\" in result\n+    assert \"quick-command-bypass-confirmed\" not in result\n+\n+\n[redacted:email]\n+async def test_listed_quick_command_runs_for_non_admin():\n+    \"\"\"When the operator lists the quick command in user_allowed_commands, a\n+    non-admin can run it — the gate must allow, not blanket-deny.\"\"\"\n+    runner = _make_runner(\n+        platform_extra={\n+            \"allow_admin_from\": [\"111\"],\n+            \"user_allowed_commands\": [\"limits\"],\n+        }\n+    )\n+    runner.config.quick_commands = {\n+        \"limits\": {\"type\": \"exec\", \"command\": \"printf quick-command-allowed\"}\n+    }\n+\n+    result = await runner._handle_message(\n+        _make_event(\"/limits\", _make_source(user_id=\"999\"))\n+    )\n+\n+    assert result == \"quick-command-allowed\"\n+\n+\n[redacted:email]\n+async def test_admin_runs_quick_command_when_gating_enabled():\n+    \"\"\"An admin runs the quick command even under an enabled gate with an\n+    empty user_allowed_commands list.\"\"\"\n+    runner = _make_runner(\n+        platform_extra={\n+            \"allow_admin_from\": [\"111\"],\n+            \"user_allowed_commands\": [],\n+        }\n+    )\n+    runner.config.quick_commands = {\n+        \"limits\": {\"type\": \"exec\", \"command\": \"printf quick-command-admin\"}\n+    }\n+\n+    result = await runner._handle_message(\n+        _make_event(\"/limits\", _make_source(user_id=\"111\"))\n+    )\n+\n+    assert result == \"quick-command-admin\"\n+\n+\n # ---------------------------------------------------------------------------\n # Running-agent fast-path gating — admin/user split must hold even when an\n # agent is already running. The fast-path block in _handle_message dispatches\n"]}]}],"trust":{"default_tier":"verified","includes_suggested":false}}