fix(security): harden dashboard API against unauthenticated access#9800
Merged
Conversation
Addresses responsible disclosure from FuzzMind Security Lab (CVE pending). The web dashboard API server had 36 endpoints, of which only 5 checked the session token. The token itself was served from an unauthenticated GET /api/auth/session-token endpoint, rendering the protection circular. When bound to 0.0.0.0 (--host flag), all API keys, config, and cron management were accessible to any machine on the network. Changes: - Add auth middleware requiring session token on ALL /api/ routes except a small public whitelist (status, config/defaults, config/schema, model/info) - Remove GET /api/auth/session-token endpoint entirely; inject the token into index.html via a <script> tag at serve time instead - Replace all inline token comparisons (!=) with hmac.compare_digest() to prevent timing side-channel attacks - Block non-localhost binding by default; require --insecure flag to override (with warning log) - Update frontend fetchJSON() to send Authorization header on all requests using the injected window.__HERMES_SESSION_TOKEN__ Credit: Callum (@0xca1x) and @migraine-sudo at FuzzMind Security Lab
Contributor
|
This was referenced Apr 14, 2026
kingsleydon
added a commit
to Clawdi-AI/hermes-agent
that referenced
this pull request
Apr 16, 2026
Four minimal patches for Clawdi's multi-tenant dashboard integration: 1. web_server.py: HERMES_SESSION_TOKEN env override — reverse-proxied deployments set a stable token so the SPA can authenticate across restarts (mirrors upstream PR NousResearch#9800 pattern). 2. web_server.py: load_hermes_dotenv() at module init — uvicorn launches web_server.py directly, bypassing hermes_cli/main.py where the dotenv loader normally runs. Without this, the HERMES_SESSION_TOKEN written to ~/.hermes/.env is never loaded into os.environ. 3. web_server.py: expose plain `value` field for non-password env vars in GET /api/env — lets the dashboard round-trip comma-separated allow-lists (TELEGRAM_ALLOWED_USERS, FEISHU_ALLOWED_USERS, etc.) without forcing users to retype them. 4. config.py: register FEISHU_ALLOWED_USERS in OPTIONAL_ENV_VARS — the Feishu gateway adapter reads this from os.getenv() but it was missing from the registration list, so GET /api/env never surfaced it.
LucaDeLeo
added a commit
to LucaDeLeo/hermes-agent
that referenced
this pull request
Apr 16, 2026
Upstream's dashboard auth middleware (NousResearch#9800) gates all /api/* routes except a public allowlist. The chat fetch was a raw fetch() that didn't include the bearer token, so every dashboard chat request returned 401 Unauthorized. Other dashboard pages go through fetchJSON() which reads window.__HERMES_SESSION_TOKEN__ and auto-injects the Authorization header; the chat fetch needs SSE (res.body streaming) so can't reuse fetchJSON — instead it reads the token directly and sets the header. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ulasbilgen
pushed a commit
to ulasbilgen/hermes-adhd-agent
that referenced
this pull request
May 1, 2026
…ousResearch#9800) Addresses responsible disclosure from FuzzMind Security Lab (CVE pending). The web dashboard API server had 36 endpoints, of which only 5 checked the session token. The token itself was served from an unauthenticated GET /api/auth/session-token endpoint, rendering the protection circular. When bound to 0.0.0.0 (--host flag), all API keys, config, and cron management were accessible to any machine on the network. Changes: - Add auth middleware requiring session token on ALL /api/ routes except a small public whitelist (status, config/defaults, config/schema, model/info) - Remove GET /api/auth/session-token endpoint entirely; inject the token into index.html via a <script> tag at serve time instead - Replace all inline token comparisons (!=) with hmac.compare_digest() to prevent timing side-channel attacks - Block non-localhost binding by default; require --insecure flag to override (with warning log) - Update frontend fetchJSON() to send Authorization header on all requests using the injected window.__HERMES_SESSION_TOKEN__ Credit: Callum (@0xca1x) and @migraine-sudo at FuzzMind Security Lab
aj-nt
pushed a commit
to aj-nt/hermes-agent
that referenced
this pull request
May 1, 2026
…ousResearch#9800) Addresses responsible disclosure from FuzzMind Security Lab (CVE pending). The web dashboard API server had 36 endpoints, of which only 5 checked the session token. The token itself was served from an unauthenticated GET /api/auth/session-token endpoint, rendering the protection circular. When bound to 0.0.0.0 (--host flag), all API keys, config, and cron management were accessible to any machine on the network. Changes: - Add auth middleware requiring session token on ALL /api/ routes except a small public whitelist (status, config/defaults, config/schema, model/info) - Remove GET /api/auth/session-token endpoint entirely; inject the token into index.html via a <script> tag at serve time instead - Replace all inline token comparisons (!=) with hmac.compare_digest() to prevent timing side-channel attacks - Block non-localhost binding by default; require --insecure flag to override (with warning log) - Update frontend fetchJSON() to send Authorization header on all requests using the injected window.__HERMES_SESSION_TOKEN__ Credit: Callum (@0xca1x) and @migraine-sudo at FuzzMind Security Lab
kingsleydon
added a commit
to Clawdi-AI/hermes-agent
that referenced
this pull request
May 9, 2026
Four minimal patches for Clawdi's multi-tenant dashboard integration: 1. web_server.py: HERMES_SESSION_TOKEN env override — reverse-proxied deployments set a stable token so the SPA can authenticate across restarts (mirrors upstream PR NousResearch#9800 pattern). 2. web_server.py: load_hermes_dotenv() at module init — uvicorn launches web_server.py directly, bypassing hermes_cli/main.py where the dotenv loader normally runs. Without this, the HERMES_SESSION_TOKEN written to ~/.hermes/.env is never loaded into os.environ. 3. web_server.py: expose plain `value` field for non-password env vars in GET /api/env — lets the dashboard round-trip comma-separated allow-lists (TELEGRAM_ALLOWED_USERS, FEISHU_ALLOWED_USERS, etc.) without forcing users to retype them. 4. config.py: register FEISHU_ALLOWED_USERS in OPTIONAL_ENV_VARS — the Feishu gateway adapter reads this from os.getenv() but it was missing from the registration list, so GET /api/env never surfaced it.
02356abc
pushed a commit
to 02356abc/hermes-agent
that referenced
this pull request
May 14, 2026
…ousResearch#9800) Addresses responsible disclosure from FuzzMind Security Lab (CVE pending). The web dashboard API server had 36 endpoints, of which only 5 checked the session token. The token itself was served from an unauthenticated GET /api/auth/session-token endpoint, rendering the protection circular. When bound to 0.0.0.0 (--host flag), all API keys, config, and cron management were accessible to any machine on the network. Changes: - Add auth middleware requiring session token on ALL /api/ routes except a small public whitelist (status, config/defaults, config/schema, model/info) - Remove GET /api/auth/session-token endpoint entirely; inject the token into index.html via a <script> tag at serve time instead - Replace all inline token comparisons (!=) with hmac.compare_digest() to prevent timing side-channel attacks - Block non-localhost binding by default; require --insecure flag to override (with warning log) - Update frontend fetchJSON() to send Authorization header on all requests using the injected window.__HERMES_SESSION_TOKEN__ Credit: Callum (@0xca1x) and @migraine-sudo at FuzzMind Security Lab
gweeteve
pushed a commit
to gweeteve/hermes-agent
that referenced
this pull request
Jun 2, 2026
…ousResearch#9800) Addresses responsible disclosure from FuzzMind Security Lab (CVE pending). The web dashboard API server had 36 endpoints, of which only 5 checked the session token. The token itself was served from an unauthenticated GET /api/auth/session-token endpoint, rendering the protection circular. When bound to 0.0.0.0 (--host flag), all API keys, config, and cron management were accessible to any machine on the network. Changes: - Add auth middleware requiring session token on ALL /api/ routes except a small public whitelist (status, config/defaults, config/schema, model/info) - Remove GET /api/auth/session-token endpoint entirely; inject the token into index.html via a <script> tag at serve time instead - Replace all inline token comparisons (!=) with hmac.compare_digest() to prevent timing side-channel attacks - Block non-localhost binding by default; require --insecure flag to override (with warning log) - Update frontend fetchJSON() to send Authorization header on all requests using the injected window.__HERMES_SESSION_TOKEN__ Credit: Callum (@0xca1x) and @migraine-sudo at FuzzMind Security Lab
Egavasyug
pushed a commit
to Egavasyug/hermes-agent
that referenced
this pull request
Jun 10, 2026
…ousResearch#9800) Addresses responsible disclosure from FuzzMind Security Lab (CVE pending). The web dashboard API server had 36 endpoints, of which only 5 checked the session token. The token itself was served from an unauthenticated GET /api/auth/session-token endpoint, rendering the protection circular. When bound to 0.0.0.0 (--host flag), all API keys, config, and cron management were accessible to any machine on the network. Changes: - Add auth middleware requiring session token on ALL /api/ routes except a small public whitelist (status, config/defaults, config/schema, model/info) - Remove GET /api/auth/session-token endpoint entirely; inject the token into index.html via a <script> tag at serve time instead - Replace all inline token comparisons (!=) with hmac.compare_digest() to prevent timing side-channel attacks - Block non-localhost binding by default; require --insecure flag to override (with warning log) - Update frontend fetchJSON() to send Authorization header on all requests using the injected window.__HERMES_SESSION_TOKEN__ Credit: Callum (@0xca1x) and @migraine-sudo at FuzzMind Security Lab
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Addresses responsible disclosure from FuzzMind Security Lab (Callum @0xca1x, @migraine-sudo).
The web dashboard API server had 36 endpoints, of which only 5 checked the session token. The token itself was served from an unauthenticated
GET /api/auth/session-tokenendpoint, rendering the protection circular. When bound to0.0.0.0(--hostflag), all API keys, config, and cron management were accessible to any machine on the network.Changes
/api/routes except a small public whitelist (/api/status,/api/config/defaults,/api/config/schema,/api/model/info)GET /api/auth/session-tokendeleted; token is now injected intoindex.htmlvia a<script>tag at serve time so only the actual SPA has ithmac.compare_digest()instead of!=--hostnow requires--insecureflag (hard error without it)fetchJSON()sends Authorization header on all requests usingwindow.__HERMES_SESSION_TOKEN__Test plan
test_web_server.py: 67 tests pass (includes newtest_unauthenticated_api_blocked+test_session_token_endpoint_removed)test_api_server*.py: 188 tests passNote
This is a stopgap hardening. Robust authentication (user accounts, API keys) is planned as a separate feature. The primary defense here is preventing public serving without
--insecure.