Skip to content

feat(webhook): Add Gitea webhook signature validation support - #66895

Open
MISAKIGA wants to merge 3 commits into
NousResearch:mainfrom
MISAKIGA:fix/add-gitea-webhook-signature-support
Open

feat(webhook): Add Gitea webhook signature validation support#66895
MISAKIGA wants to merge 3 commits into
NousResearch:mainfrom
MISAKIGA:fix/add-gitea-webhook-signature-support

Conversation

@MISAKIGA

Copy link
Copy Markdown

Pull Request: Add Gitea Webhook Signature Validation Support

Summary

This PR adds support for Gitea webhook signature validation in Hermes Gateway.

Problem

Gitea webhooks fail with Invalid signature error because Hermes Gateway does not recognize the X-Gitea-Signature header format.

Solution

Add Gitea signature validation logic to _validate_signature() method in gateway/platforms/webhook.py.

Changes

+        # Gitea: X-Gitea-Signature = <hex HMAC-SHA256 of body>
+        # Gitea sends the raw hex digest (no sha256= prefix like GitHub)
+        gitea_sig = request.headers.get("X-Gitea-Signature", "")
+        if gitea_sig:
+            expected = hmac.new(
+                secret.encode(), body, hashlib.sha256
+            ).hexdigest()
+            return _hmac_str_equal(gitea_sig, expected)

Technical Details

Platform Header Format Signature Format
GitHub X-Hub-Signature-256 sha256=<hex>
GitLab X-Gitlab-Token <plain secret>
Gitea X-Gitea-Signature <hex> (raw)
Generic X-Webhook-Signature <hex>

Gitea sends the raw HMAC-SHA256 hex digest without the sha256= prefix that GitHub uses.

Testing

Test Environment

  • Gitea v1.27.0
  • Hermes Gateway (latest)
  • Webhook secret: configured

Test Steps

  1. Create Gitea webhook with secret
  2. Trigger webhook (create Issue/PR)
  3. Verify Hermes accepts webhook (no signature error)
  4. Verify agent processes webhook correctly

Expected Result

  • Webhook accepted: HTTP 202
  • No Invalid signature warning in logs
  • Agent receives webhook payload

Impact

  • ✅ Enables Gitea integration for Hermes users
  • ✅ No breaking changes
  • ✅ Minimal code change (9 lines)
  • ✅ Consistent with existing signature validation pattern

Security

  • Uses constant-time string comparison (_hmac_str_equal) to prevent timing attacks
  • Same security guarantees as GitHub/GitLab validation
  • No sensitive data exposed

Related


Checklist

  • Code follows existing patterns
  • No sensitive data in commit
  • Commit message follows conventional commits
  • Tested with Gitea v1.27.0
  • Unit tests added (optional)
  • Documentation updated (optional)

Gitea sends X-Gitea-Signature header with HMAC-SHA256 hex digest.
This patch adds support for validating Gitea webhook signatures.

Fixes: Gitea webhooks fail with 'Invalid signature' error

Related-to: https://github.com/NousResearch/hermes-agent
@PRATHAMESH75

Copy link
Copy Markdown
Contributor

This was generated by AI during triage.

Review — addresses #66893. Verdict: correct, minimal fix; the one gap is missing test coverage.

What's solid

  • Root cause is right: _validate_signature() in gateway/platforms/webhook.py had branches for GitHub / GitLab / Svix / generic but nothing for Gitea's X-Gitea-Signature (raw hex HMAC-SHA256 of the body, no sha256= prefix). The added branch computes exactly that and compares with _hmac_str_equal, so it's constant-time — consistent with the existing GitHub/GitLab branches.
  • request.headers is aiohttp's case-insensitive CIMultiDict, so reading request.headers.get("X-Gitea-Signature", "") directly (rather than the _header() helper) is fine and matches how the GitHub/GitLab branches already read their headers.
  • Placement (after GitLab, before generic V2) is safe — Gitea uses a distinct header, so ordering among the distinct-header branches doesn't change behavior.

Gaps / suggestions

  1. No test. tests/gateway/test_webhook_adapter.py::TestValidateSignature has valid+invalid pairs for GitHub (test_validate_github_signature_valid/invalid) and GitLab. A Gitea pair should parallel those, plus ideally a non-ASCII-header case (the existing test_non_ascii_signature_headers_reject_without_raising covers X-Hub-Signature-256/X-Gitlab-Token/X-Webhook-Signature but not X-Gitea-Signature). Without a test the new branch is unguarded against regressions.
  2. Informational (not a blocker): modern Gitea also emits X-Hub-Signature-256 (with the sha256= prefix), which the existing GitHub branch already validates and returns on first — so for default Gitea configs this branch is a fallback that's reached only when Gitea is set to send X-Gitea-Signature alone. Worth a one-line comment noting that, so a future reader doesn't think Gitea was previously wholly unsupported.

Net: the change is safe to merge on correctness; adding the parallel test in TestValidateSignature would bring it in line with the rest of the signature suite.

@alt-glitch alt-glitch added type/feature New feature or request comp/gateway Gateway runner, session dispatch, delivery platform/webhook Webhook / API server P2 Medium — degraded but workaround exists needs-decision Awaiting maintainer decision before any implementation sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages labels Jul 18, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Related: #63108 is the earlier open Gitea/Forgejo superset (event-header recognition, signature validation, and tests). This PR is signature-only and its live diff contains no regression tests, so it is related competing work rather than a duplicate.

Hermes Agent added 2 commits July 18, 2026 19:51
- Add _gitea_signature() helper function
- Add test_validate_gitea_signature_valid
- Add test_validate_gitea_signature_invalid
- Add test_validate_gitea_signature_wrong_body_rejected
- Add X-Gitea-Signature to non-ASCII header rejection test
- Add comment explaining X-Hub-Signature-256 fallback for modern Gitea

All 30 signature validation tests pass.

Addresses PR review feedback on NousResearch#66895
This PR now provides full Gitea and Forgejo webhook support:

**Signature Validation:**
- X-Gitea-Signature (HMAC-SHA256 hex, no prefix)
- X-Forgejo-Signature (same format as Gitea)

**Event Type Extraction:**
- X-Gitea-Event header recognition
- X-Forgejo-Event header recognition

**Tests Added:**
- test_validate_gitea_signature_valid/invalid/wrong_body
- test_validate_forgejo_signature_valid/invalid/wrong_body
- test_event_filter_accepts_gitea_event/rejects_non_matching
- test_event_filter_accepts_forgejo_event/rejects_non_matching
- X-Gitea-Signature and X-Forgejo-Signature in non-ASCII test

**Test Results:**
- 33 signature validation tests pass
- 8 event filter tests pass

This supersedes NousResearch#63108 by providing complete implementation
with matching tests (no test/code gaps).

Closes NousResearch#66893
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the complete follow-up. Current main at 7fd419e5 selects events from only X-GitHub-Event / X-GitLab-Event in gateway/platforms/webhook.py:629-634, and its signature validator moves from GitHub and GitLab directly to generic validation at gateway/platforms/webhook.py:986-1015. The PR adds the missing Gitea/Forgejo paths and covers valid, invalid, wrong-body, non-ASCII, and event-filter behavior in tests/gateway/test_webhook_adapter.py.

Automated hermes-sweeper review.

@teknium1 teknium1 added sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 19, 2026
@alt-glitch alt-glitch removed the sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data label Jul 19, 2026
@Leowr997

Copy link
Copy Markdown

Heads-up: this PR currently shows as dirty (conflicts with main). I just tested the rebase locally against current main (c0106e50e7) and it goes through cleanly:

  • Conflict: 1, trivial, in tests/gateway/test_webhook_adapter.py only (upstream moved test_event_filter_rejects_non_matching; keep the PR's version of that block). gateway/platforms/webhook.py merges with zero conflicts.
  • Result: rebased branch = 3 commits on top of today's main, and the full suite passes: 105 passed (main baseline is 34 — this PR adds 71 tests).
  • webhook.py diff after rebase: exactly +24 lines (the 2 event headers + Gitea/Forgejo signature validation), no stale code.

So a plain git rebase origin/main + force-push should flip this to mergeable. Happy to share the rebased branch if useful.

Also worth noting for the maintainer choosing scope: our focused header-recognition PR (#83723) is being closed in favor of this one, since it is a strict superset (headers + signature validation).

Leowr997 pushed a commit to Leowr997/hermes-agent that referenced this pull request Aug 18, 2026
- Add _gitea_signature() helper function
- Add test_validate_gitea_signature_valid
- Add test_validate_gitea_signature_invalid
- Add test_validate_gitea_signature_wrong_body_rejected
- Add X-Gitea-Signature to non-ASCII header rejection test
- Add comment explaining X-Hub-Signature-256 fallback for modern Gitea

All 30 signature validation tests pass.

Addresses PR review feedback on NousResearch#66895
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 needs-decision Awaiting maintainer decision before any implementation P2 Medium — degraded but workaround exists platform/webhook Webhook / API server sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants