Skip to content

fix(security): validate secret in _reload_dynamic_routes to prevent HMAC bypass (#8306)#30370

Merged
teknium1 merged 3 commits into
mainfrom
hermes/hermes-37ccebe4
May 22, 2026
Merged

fix(security): validate secret in _reload_dynamic_routes to prevent HMAC bypass (#8306)#30370
teknium1 merged 3 commits into
mainfrom
hermes/hermes-37ccebe4

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Summary

Salvages @memosr's PR #8306. Closes an HMAC bypass that let an agent-induced webhook subscription with an empty secret reach the request handler — empty string is falsy at line 356 of gateway/platforms/webhook.py, so the signature check was skipped and unauthenticated POSTs executed the webhook prompt with full tool access.

Root cause

_reload_dynamic_routes() (called on every webhook POST via mtime gate) merged any dynamic route into self._routes without secret validation. Startup-time validation in connect() only fires once; the hot-reload path bypassed it entirely.

Changes

  • gateway/platforms/webhook.py (+20 -5): per-route secret validation inside _reload_dynamic_routes(). Routes with an effectively empty secret (no per-route value and no global fallback) are skipped with a WARNING log. INSECURE_NO_AUTH opt-in is preserved.
  • tests/gateway/test_webhook_dynamic_routes.py (+80 -1): 6 new regression cases — empty rejected, missing-no-global rejected, missing-with-global inherits, INSECURE_NO_AUTH preserved, WARN logged, partial-skip.

Validation

Scenario Before After
Dynamic route with secret="" merged → HMAC skipped → 200 OK on unsigned POST skipped at load, WARN logged
Dynamic route with missing secret, global set inherits global secret inherits global secret (preserved)
Dynamic route with missing secret, no global merged → HMAC skipped skipped at load
Route with secret=INSECURE_NO_AUTH loads (testing opt-in) loads (preserved)

Targeted tests: tests/gateway/test_webhook_dynamic_routes.py — 12/12 pass.
Full webhook suite (6 files): 105/105 pass.

Attribution

Salvaged via plain cherry-pick of @memosr's commit so original authorship survives. Closes #8306 once merged; follow-up close-with-credit will be posted on the original PR thread.

Infographic

PR 8306 webhook HMAC bypass

memosr and others added 3 commits May 22, 2026 03:39
Covers _reload_dynamic_routes() rejecting empty or missing per-route
secrets when no global fallback exists, preserving the INSECURE_NO_AUTH
opt-in, inheriting a global secret when only the per-route value is
missing, and partial-skip when only one of multiple routes is bad.
@teknium1 teknium1 merged commit 8b49012 into main May 22, 2026
16 of 17 checks passed
@teknium1 teknium1 deleted the hermes/hermes-37ccebe4 branch May 22, 2026 10:45
@github-actions

Copy link
Copy Markdown
Contributor

🔎 Lint report: hermes/hermes-37ccebe4 vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 9012 on HEAD, 9012 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 4763 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@alt-glitch alt-glitch added type/security Security vulnerability or hardening P0 Critical — data loss, security, crash loop comp/gateway Gateway runner, session dispatch, delivery platform/webhook Webhook / API server labels May 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P0 Critical — data loss, security, crash loop platform/webhook Webhook / API server type/security Security vulnerability or hardening

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants