Skip to content

fix: harden dashboard security and runtime status - #61305

Open
AndreasG78 wants to merge 4 commits into
NousResearch:mainfrom
AndreasG78:fix/dashboard-security-runtime-a11y
Open

AndreasG78 wants to merge 4 commits into
NousResearch:mainfrom
AndreasG78:fix/dashboard-security-runtime-a11y

Conversation

@AndreasG78

Copy link
Copy Markdown

Summary

  • harden dashboard cookie-auth mutating APIs with Fetch Metadata + Origin/Referer CSRF checks
  • block sensitive credential file/directory access through dashboard FS list/read/data-url/write APIs
  • fix stale dashboard process detection and profile-specific gateway liveness in /api/status?profile=...
  • improve dashboard modal focus/inertness behavior and secret input UX

Verification

  • .venv/bin/python -m py_compile hermes_cli/main.py hermes_cli/dashboard_auth/middleware.py hermes_cli/web_server.py
  • scripts/run_tests.sh tests/hermes_cli/test_update_stale_dashboard.py tests/hermes_cli/test_dashboard_auth_middleware.py tests/hermes_cli/test_web_server_fs.py tests/hermes_cli/test_web_server_gateway_topology.py -q → 103 passed
  • npm run --workspace web lint -- --max-warnings=0
  • npm run --workspace web typecheck
  • npm run --workspace web test -- --run → 6 files / 33 tests passed
  • npm run --workspace web build → passed; existing Vite chunk-size warning remains
  • git diff --check
  • Runtime smoke on 127.0.0.1:9119: /api/status?profile=assistant reports gateway_running=True, dashboard status reports exactly one server process, stale detector returns the dashboard child PID

Notes

  • Remaining performance follow-up: route-level code splitting for the large web bundle (~1.96 MB raw / ~560 kB gzip).
  • Unrelated pre-existing gateway noise-filter edits were stashed locally and intentionally excluded from this PR.

@alt-glitch alt-glitch added type/security Security vulnerability or hardening comp/dashboard Web dashboard / control panel UI (dashboard/, landing) comp/cli CLI entry point, hermes_cli/, setup wizard area/auth Authentication, OAuth, credential pools P3 Low — cosmetic, nice to have sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data labels Jul 9, 2026
@AndreasG78

Copy link
Copy Markdown
Author

Local verification completed after rebasing this PR onto current origin/main (88a58ff135).

Evidence:

  • scripts/run_tests.sh tests/hermes_cli/test_update_stale_dashboard.py tests/hermes_cli/test_dashboard_auth_middleware.py tests/hermes_cli/test_web_server_fs.py tests/hermes_cli/test_web_server_gateway_topology.py -q → 103 passed
  • npm run --workspace web lint -- --max-warnings=0 → passed
  • npm run --workspace web typecheck → passed
  • npm run --workspace web test -- --run → 6 files / 33 tests passed
  • npm run --workspace web build → passed; existing Vite chunk-size warning remains
  • runtime smoke on 127.0.0.1:9119: /api/status?profile=assistant reports gateway_running=True; dashboard status reports exactly one server process

The upstream Actions run for this fork PR is currently action_required, so CI appears to need maintainer approval before it can execute.

@AndreasG78

Copy link
Copy Markdown
Author

Fork-local CI is green for the current head f55a3f1a36d43423e47b9e6f50296bd3ece8301a. Evidence: https://github.com/AndreasG78/hermes-agent/actions/runs/28998070436

AndreasG78 and others added 4 commits July 10, 2026 05:27
- add CSRF/Origin checks for cookie-auth mutating dashboard requests
- block sensitive file API access for credential stores
- fix stale dashboard detection and profile-specific gateway liveness
- improve modal inertness/focus behavior and secret input UX

Verification: targeted Python tests, web lint/typecheck/tests/build, runtime smoke.
@AndreasG78
AndreasG78 force-pushed the fix/dashboard-security-runtime-a11y branch from 7c7bdf1 to 396c8aa Compare July 10, 2026 05:35

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the concrete dashboard hardening and profile-status coverage. The underlying main-branch gaps are real: hermes_cli/web_server.py:2017-2130 lacks raw-filesystem sensitive-path checks, and hermes_cli/web_server.py:2455-2507 uses process-scoped gateway identity defaults while serving a selected profile.

Problems

  • The new CSRF check at hermes_cli/dashboard_auth/middleware.py:481 is bypassed for POST /auth/logout, because _GATE_PUBLIC_PREFIXES includes that route at current-main middleware.py:47. The handler revokes its refresh token and clears session cookies (dashboard_auth/routes.py:556-583). Please protect this cookie-mutating route too and add same-origin/cross-origin logout coverage.
  • gateway/run.py:8993-9027 adds a pending_self_reset.json protocol that is outside this PR's stated scope. A current-main search found no producer for that state file, and the changed restart tests only assert prompt text. Please split it into a focused, end-to-end-tested gateway change.

Suggested changes

  • Preserve unauthenticated logout behavior if needed, but validate Origin/Referer when cookie-authenticated logout performs revocation.
  • Keep the dashboard security/status salvage focused on the tested dashboard changes.

Automated hermes-sweeper review.

return response

request.state.session = session
if request.method.upper() in {"POST", "PUT", "PATCH", "DELETE"} and not _csrf_origin_ok(request):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check does not cover POST /auth/logout: _path_is_public() returns earlier for that route, while its handler revokes the refresh token and clears session cookies. Please apply the same origin validation to cookie-bearing logout and add same-origin/cross-origin coverage.

Comment thread gateway/run.py
# consumed as update answers instead of being dispatched normally.
_quick_key = self._session_key_for_source(source)

# Profile-local context-hygiene watchdogs may request a silent session

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This introduces a new pending_self_reset.json state-file protocol outside the PR summary. Current main has no producer for this file, and the changed tests exercise only restart-resume prompt wording. Please split this into a focused gateway change with an end-to-end producer/consumer test.

@teknium1 teknium1 added sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows sweeper:blast-broad Sweeper blast radius: broad — a core path most sessions hit labels Jul 11, 2026
@egilewski

Copy link
Copy Markdown
Contributor

too large to review safely

This PR changes 553 production lines before tests and docs. Please split it or add a focused justification if it should stay together.

Signed: GPT-5.6-luna-high in Codex

@Enough1122

Copy link
Copy Markdown
Contributor

AI code review — automated review for reference, author can ignore or act on any point.

fix: harden dashboard security and runtime status

  1. hermes_cli/dashboard_auth/middleware.py (_csrf_origin_ok): the gate is fail-closed when both Origin and Referer are missing, so any non-browser cookie-authenticated client (curl/scripts with a cookie jar, integrations) doing POST/PUT/PATCH/DELETE now gets 403. The diff itself had to add Origin: https://fly-app.fly.dev to many existing tests, which hints real flows may be affected — worth documenting that cookie-auth unsafe requests now require a browser-like client, or providing an explicit escape hatch.

  2. hermes_cli/web_server.py (get_status): _gateway_liveness_for_home is documented as making /api/status?profile reflect the selected profile, but the call site passes selected_home = get_hermes_home(), which resolves to the dashboard process's own profile unless a context-local HERMES_HOME override is active earlier in the function. Please verify the profile query param actually flows into the home resolution — as written, the liveness probe reports the process profile's gateway, not the requested one.

  3. hermes_cli/main.py (_cmdline_runs_dashboard_server): the scanner bails at the first non-option token it doesn't recognize, and options_with_values is a hardcoded set. An option-with-value flag NOT in that set placed before the subcommand (e.g. hermes --port 9119 dashboard, or any future --flag value) is treated as a boolean and its value as a positional, yielding a false negative (stale backend not reaped). Consider only treating a recognized subcommand name as the trigger and skipping unknown --opt value pairs generically.

  4. Minor: gateway/run.py's pending_self_reset.json pop+rewrite is a read-modify-write race against concurrent inbound messages (two messages could both read the pending map and drop one reset), and every non-internal message now pays a filesystem stat/read; a short-lived in-memory cache keyed on file mtime would reduce the cost.

@alt-glitch alt-glitch added comp/gateway Gateway runner, session dispatch, delivery sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state needs-decision Awaiting maintainer decision before any implementation sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages and removed sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows labels Aug 15, 2026
@deathlord911

Copy link
Copy Markdown

+1 for the Origin/Referer check on cookie-authenticated mutating routes — we have been running an equivalent local patch on a self-hosted 0.21.3 dashboard since September 2026 without side effects.

One argument that may help prioritise this: the current protection against CSRF is effectively SameSite=Lax on hermes_session_at alone. That only stops cross-site requests. A page on a sibling subdomain (other.example.com next to hermes.example.com, or any other service on a shared internal domain like *.corp.intranet) is same-site, so the browser does attach the Lax cookie to its POST/PATCH/DELETE. Self-hosted setups very often put many services under one internal domain, which is exactly where an Origin check is the only real line of defence. Still unchanged on main @ ea0c2b8 as far as we can see.

Two details from our version, in case useful:

  • non-http(s) origins (null, file:, app:) are let through so the desktop shell / sandboxed iframes keep working; requests with neither Origin nor Referer (CLI, API clients) pass as well;
  • the trusted set is the same one already used for Host / WS-Origin validation (incl. dashboard.public_url), so there is a single source of truth.

Minor, related: host_header_middleware is registered before the auth middlewares and therefore (Starlette runs the last-registered first) executes after them — an unauthenticated request with a foreign Host gets the auth 302 rather than the intended 400. Not exploitable, but if this PR touches middleware order anyway it might be worth making the Host guard the outermost layer.

@alt-glitch alt-glitch added the area/sessions Session lifecycle, resume, persistence, history label Sep 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/auth Authentication, OAuth, credential pools area/sessions Session lifecycle, resume, persistence, history comp/cli CLI entry point, hermes_cli/, setup wizard comp/dashboard Web dashboard / control panel UI (dashboard/, landing) comp/gateway Gateway runner, session dispatch, delivery needs-decision Awaiting maintainer decision before any implementation P3 Low — cosmetic, nice to have sweeper:blast-broad Sweeper blast radius: broad — a core path most sessions hit sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/security Security vulnerability or hardening

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants