fix(webhook): reject a non-ASCII signature header instead of crashing the endpoint - #65307
Closed
Drexuxux wants to merge 3 commits into
Closed
fix(webhook): reject a non-ASCII signature header instead of crashing the endpoint#65307Drexuxux wants to merge 3 commits into
Drexuxux wants to merge 3 commits into
Conversation
… the endpoint _validate_signature backs the public webhook receiver. It compared each attacker-supplied signature/token header (GitHub X-Hub-Signature-256, GitLab X-Gitlab-Token, generic X-Webhook-Signature / -V2, and the Svix v1 header) against a computed hex/base64 digest with hmac.compare_digest on two str values. compare_digest raises TypeError on a str containing non-ASCII characters, and the header is raw client input on an unauthenticated endpoint — so any internet client could POST a single non-ASCII byte in the signature header and raise out of the handler, returning a 500 instead of a clean 401. Fail-closed, but an on-demand crash of the request path. Route all five comparisons through a small _hmac_str_equal() helper that encodes both sides to UTF-8 bytes before the constant-time compare (compare_digest has no ASCII restriction on bytes). Semantics are unchanged for valid signatures; a hostile non-ASCII header now fails closed with a rejection instead of raising. Adds regression tests: non-ASCII GitHub/GitLab/generic/V2 signature headers return False (no raise), and a non-ASCII configured secret still matches its exact token value. Also maps drexux0@gmail.com in scripts/release.py AUTHOR_MAP.
teknium1
reviewed
Jul 16, 2026
teknium1
left a comment
Contributor
There was a problem hiding this comment.
Thanks for isolating the generic webhook fix and preserving constant-time comparison semantics. Current main still has the five direct str comparisons in gateway/platforms/webhook.py:930, :935, :978, :1002, and :1056; the PR replaces each one, and the handler's clean rejection path is at gateway/platforms/webhook.py:535-541.
Problems
- The same public-header pattern remains in
gateway/platforms/whatsapp_cloud.py: it readsX-Hub-Signature-256at:1464and directly compares the derived string at:1512. That leaves the reported failure class in a sibling webhook endpoint.
Suggested changes
- Harden the WhatsApp Cloud path and add its non-ASCII rejection regression; audit the analogous authenticated string comparisons in
gateway/platforms/msgraph_webhook.py:361andgateway/platforms/api_server.py:1219. - Add a Svix non-ASCII regression: this PR changes the Svix comparison, but the new tests do not enter that branch.
Automated hermes-sweeper review.
| req = _mock_request(headers={}) # no sig headers at all | ||
| assert adapter._validate_signature(req, b"{}", "my-secret") is False | ||
|
|
||
| def test_non_ascii_signature_headers_reject_without_raising(self): |
Contributor
There was a problem hiding this comment.
This covers GitHub, GitLab, generic V1, and generic V2, but the patch also changes the Svix v1 comparison. Please add a non-ASCII svix-signature case with valid svix-id and timestamp so that branch is regression-tested too.
…gression The fix routes the Svix v1 comparison through _hmac_str_equal too, but the existing non-ASCII tests only exercised the GitHub/GitLab/generic V1/V2 branches. Add a Svix case (valid svix-id + fresh svix-timestamp so it reaches the v1,<sig> compare) with a non-ASCII signature, which raised TypeError before the fix and now rejects cleanly.
…-nonascii-crash # Conflicts: # scripts/release.py
teknium1
added a commit
that referenced
this pull request
Jul 16, 2026
…g sites Same bug class as the salvaged #65305/#65307: hmac.compare_digest (and secrets.compare_digest) raise TypeError when given a str containing non-ASCII characters, and these call sites feed it raw request input. Compare as UTF-8 bytes everywhere: - gateway/platforms/msgraph_webhook.py: clientState from request body - gateway/platforms/whatsapp_cloud.py: hub.verify_token query param + X-Hub-Signature-256 header (comment claimed 'works on str' — it doesn't for non-ASCII) - plugins/platforms/feishu: verification token + x-lark-signature - plugins/platforms/raft: bridge token header - plugins/platforms/line: X-Line-Signature - plugins/platforms/sms: X-Twilio-Signature - tools/code_execution_tool.py: sandbox RPC token (both loops) Regression tests for the two gateway-core sites (msgraph, whatsapp).
Contributor
Gravezzz
pushed a commit
to Gravezzz/hermes-agent
that referenced
this pull request
Jul 21, 2026
…g sites Same bug class as the salvaged NousResearch#65305/NousResearch#65307: hmac.compare_digest (and secrets.compare_digest) raise TypeError when given a str containing non-ASCII characters, and these call sites feed it raw request input. Compare as UTF-8 bytes everywhere: - gateway/platforms/msgraph_webhook.py: clientState from request body - gateway/platforms/whatsapp_cloud.py: hub.verify_token query param + X-Hub-Signature-256 header (comment claimed 'works on str' — it doesn't for non-ASCII) - plugins/platforms/feishu: verification token + x-lark-signature - plugins/platforms/raft: bridge token header - plugins/platforms/line: X-Line-Signature - plugins/platforms/sms: X-Twilio-Signature - tools/code_execution_tool.py: sandbox RPC token (both loops) Regression tests for the two gateway-core sites (msgraph, whatsapp).
randlee
pushed a commit
to randlee/hermes-agent
that referenced
this pull request
Aug 11, 2026
…g sites Same bug class as the salvaged NousResearch#65305/NousResearch#65307: hmac.compare_digest (and secrets.compare_digest) raise TypeError when given a str containing non-ASCII characters, and these call sites feed it raw request input. Compare as UTF-8 bytes everywhere: - gateway/platforms/msgraph_webhook.py: clientState from request body - gateway/platforms/whatsapp_cloud.py: hub.verify_token query param + X-Hub-Signature-256 header (comment claimed 'works on str' — it doesn't for non-ASCII) - plugins/platforms/feishu: verification token + x-lark-signature - plugins/platforms/raft: bridge token header - plugins/platforms/line: X-Line-Signature - plugins/platforms/sms: X-Twilio-Signature - tools/code_execution_tool.py: sandbox RPC token (both loops) Regression tests for the two gateway-core sites (msgraph, whatsapp).
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.
What?
_validate_signaturebacks the public webhook receiver. It compared each attacker-supplied signature/token header (GitHubX-Hub-Signature-256, GitLabX-Gitlab-Token, genericX-Webhook-Signature/-V2, and the Svix v1 header) against a computed digest withhmac.compare_digeston twostrvalues.compare_digestraisesTypeErroron astrcontaining non-ASCII characters, and the header is raw client input on an unauthenticated endpoint — so any internet client can POST a single non-ASCII byte in the signature header and raise out of the handler, returning a 500 instead of a clean 401. Fail-closed, but an on-demand crash of the request path.Solution
Route all five comparisons through a small
_hmac_str_equal()helper that encodes both sides to UTF-8 bytes before the constant-time compare (compare_digesthas no ASCII restriction on bytes). Semantics are unchanged for valid signatures; a hostile non-ASCII header now fails closed with a rejection.How to test
pytest tests/gateway/test_webhook_adapter.py::TestValidateSignature -qFalse(no raise); a non-ASCII configured secret still matches its exact token.TypeErrorthere.Test results
TestValidateSignature— 26 passed (23 existing + 3 new).TypeError: comparing strings with non-ASCII characters is not supported; post-fix they reject / validate correctly.test_webhook_adapter.py+test_webhook_signature_rate_limit.py+test_msgraph_webhook.py— 120 passed, no regressions.