Skip to content

fix(security): use ASGI path for dashboard auth gates - #35547

Open
zapabob wants to merge 1 commit into
NousResearch:mainfrom
zapabob:codex/badhost-scope-path-hardening
Open

zapabob wants to merge 1 commit into
NousResearch:mainfrom
zapabob:codex/badhost-scope-path-hardening

Conversation

@zapabob

@zapabob zapabob commented May 30, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Use the ASGI-dispatched request scope path for dashboard OAuth and legacy API-token path gates.
  • Keep redirect next= generation aligned with the dispatched path instead of URL reconstruction.
  • Add regression coverage for both gated dashboard auth and loopback token middleware.

Validation

  • uv run --extra dev ruff check hermes_cli\dashboard_auth\middleware.py hermes_cli\web_server.py tests\hermes_cli\test_dashboard_auth_middleware.py
  • uv run --extra dev --extra web python -X utf8 scripts\run_tests_parallel.py tests\hermes_cli\test_dashboard_auth_middleware.py -- -q --timeout-method=thread

Notes

This is a defense-in-depth follow-up to the Starlette BadHost dependency floor: auth decisions should use the ASGI routing source of truth even when URL reconstruction behavior changes.

@alt-glitch alt-glitch added type/security Security vulnerability or hardening comp/cli CLI entry point, hermes_cli/, setup wizard area/auth Authentication, OAuth, credential pools P2 Medium — degraded but workaround exists labels May 30, 2026

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review Summary

Verdict: Approved ✅

Review Findings

This PR adds _request_path() to use the ASGI-dispatched scope["path"] instead of request.url.path for auth decisions in the dashboard OAuth gate and legacy API-token middleware. This is a defense-in-depth hardening following the Starlette BadHost fix.

✅ Looks Good

  • Security: Switching auth gates from URL reconstruction to ASGI scope path is the correct approach — scope["path"] is the routing source of truth and is not affected by host/header parsing quirks.
  • The _safe_next_target fix: Using _request_path() there also prevents protocol-relative open redirect attacks via the next= parameter. Well spotted.
  • Tests: Two new async tests (test_gate_uses_scope_path_for_auth_decision, test_legacy_token_gate_uses_scope_path_for_api_decision) use mock objects to simulate a request where request.url.path lies but scope["path"] reveals the real path — good regression coverage.
  • API design: _request_path() has a clean fallback to request.url.path if scope is absent, making it safe to introduce.
  • Minimal diff: 94 additions, only 4 deletions — mostly test code, very low risk.

No Issues Found


Reviewed by Hermes Agent

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review Summary

Verdict: Approved ✅

Changes

  • hermes_cli/dashboard_auth/middleware.py: Added _request_path() helper that uses request.scope["path"] (ASGI source of truth) instead of request.url.path (URL reconstruction) for all auth decisions
  • hermes_cli/web_server.py: Same fix in legacy token middleware
  • tests/hermes_cli/test_dashboard_auth_middleware.py: 2 regression tests + 1 new import

Review

🔒 Security ✅

  • Correctly uses the ASGI-dispatched scope path instead of URL-reconstructed path for all auth gates
  • Defense-in-depth against Starlette BadHost — host/header parsing quirks cannot desync auth checks from dispatch
  • _request_path() has a fallback to request.url.path if scope path is unavailable (defensive)
  • Both gated_auth_middleware and legacy auth_middleware are covered

✅ Correctness

  • _safe_next_target() also uses the scope path — important so redirect-after-login sends users to the right place
  • Public path checks (_path_is_public) use the same helper
  • _unauth_response() uses scope path for the next redirect target

✅ Testing

  • test_gate_uses_scope_path_for_auth_decision: verifies gated middleware rejects when scope path is /api/sessions but URL path says /login (would bypass without this fix)
  • test_legacy_token_gate_uses_scope_path_for_api_decision: same for legacy token middleware
  • Both tests are well-structured with clear fake classes

✅ Code Quality

  • Helper function with docstring explaining why (ASGI source of truth vs URL reconstruction)
  • Import is scoped in web_server.py (minimal coupling)
  • Small, focused diff — 3 files, clear responsibility per file

Summary

Clean security hardening fix. Replaces request.url.path with ASGI scope path for auth gate decisions — defense-in-depth against URL reconstruction desync. Good test coverage.


Reviewed by Hermes Agent (cron job)

@alt-glitch alt-glitch added comp/dashboard Web dashboard / control panel UI (dashboard/, landing) sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data labels Jun 26, 2026

@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 focused defense-in-depth hardening. The two original gates on current main still derive their authorization path from request.url.path (hermes_cli/dashboard_auth/middleware.py:274, hermes_cli/web_server.py:584), so the core approach remains relevant.

Problems

  • Current main added a non-interactive token-auth seam after this PR; it performs exact token-route authorization using request.url.path at hermes_cli/dashboard_auth/token_auth.py:162-164. A dispatched token route can therefore miss that seam under the same path-desynchronization class.
  • Current main also added _plugin_api_runtime_gate, whose disabled-plugin policy begins from request.url.path at hermes_cli/web_server.py:505-506. The PR does not cover this sibling authorization decision.

Suggested changes

  • Reuse the ASGI-path accessor for both current sibling gates and add mismatch tests for each, alongside the existing OAuth and legacy-token cases.

Automated hermes-sweeper review.

)


def _request_path(request: Request) -> str:

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.

Please make this the shared path source for the current sibling authorization checks as well: hermes_cli/dashboard_auth/token_auth.py:162 selects exact bearer-token routes from request.url.path, and hermes_cli/web_server.py:505 selects the runtime disabled-plugin gate from it. Both make authorization decisions from the same divergent value this helper is intended to avoid.

This branch has not been deployed

No deployments
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 comp/cli CLI entry point, hermes_cli/, setup wizard comp/dashboard Web dashboard / control panel UI (dashboard/, landing) P2 Medium — degraded but workaround exists sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/security Security vulnerability or hardening

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants