Skip to content

feat(webhook): accept Standard Webhooks signatures - #47849

Open
HwangJohn wants to merge 1 commit into
NousResearch:mainfrom
HwangJohn:fix/47451-standard-webhook-signature
Open

feat(webhook): accept Standard Webhooks signatures#47849
HwangJohn wants to merge 1 commit into
NousResearch:mainfrom
HwangJohn:fix/47451-standard-webhook-signature

Conversation

@HwangJohn

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds Standard Webhooks header support to the generic webhook adapter so providers such as GitLab can authenticate with webhook-id, webhook-timestamp, and webhook-signature.

This reuses the existing Svix-compatible validator because the signed content and v1,<base64-hmac-sha256> format are the same: {id}.{timestamp}.{raw_body}. It also uses webhook-id as the delivery ID fallback so retries dedupe correctly.

Existing GitHub X-Hub-Signature-256, GitLab X-Gitlab-Token, Svix, and generic X-Webhook-Signature paths are preserved for requests that do not send Standard Webhooks headers. Requests that do send webhook-id are treated as Standard Webhooks requests and fail closed unless the required Standard Webhooks fields validate.

Fixes #47451

Related Issue

Fixes #47451

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • Accepted webhook-id as an alias for svix-id.
  • Accepted webhook-timestamp as an alias for svix-timestamp.
  • Accepted webhook-signature as an alias for svix-signature.
  • Used webhook-id as the webhook delivery ID fallback for idempotency.
  • Added tests for valid Standard Webhooks signatures, raw-body mismatch rejection, and webhook-id deduplication.
  • Updated webhook security documentation.

How to Test

  1. Configure a webhook route with a shared secret.
  2. Send a POST with webhook-id, webhook-timestamp, and webhook-signature.
  3. The signature should validate when it signs {id}.{timestamp}.{raw_body} with HMAC-SHA256 and the route secret.
  4. Reusing the same webhook-id should return the duplicate response on retry.

Validation run:

  • DGX Spark Linux: scripts/run_tests.sh tests/gateway/test_webhook_adapter.py tests/gateway/test_webhook_signature_rate_limit.py tests/gateway/test_webhook_integration.py -> 78 passed.
  • DGX Spark Linux: ~/.hermes/hermes-agent/venv/bin/python -m ruff check gateway/platforms/webhook.py tests/gateway/test_webhook_adapter.py -> passed.
  • DGX Spark Linux: git diff --check origin/main...fix/47451-standard-webhook-signature -> passed after rebase.
  • Windows: .\\.venv\\Scripts\\python -m pytest tests\\gateway\\test_webhook_adapter.py tests\\gateway\\test_webhook_signature_rate_limit.py tests\\gateway\\test_webhook_integration.py -q -> 78 passed.
  • Windows: .\\.venv\\Scripts\\python -m ruff check gateway\\platforms\\webhook.py tests\\gateway\\test_webhook_adapter.py -> passed.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this feature
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes
  • I've tested on my platform: Windows local and DGX Spark Linux

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings)
  • I've updated cli-config.yaml.example if I added/changed config keys — N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide
  • I've updated tool descriptions/schemas if I changed tool behavior — N/A

Screenshots / Logs

Not applicable. This is a webhook signature validation change covered by tests.

@alt-glitch alt-glitch added type/feature New feature or request comp/gateway Gateway runner, session dispatch, delivery platform/webhook Webhook / API server P3 Low — cosmetic, nice to have labels Jun 17, 2026
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the focused Standard Webhooks implementation. Current main still only extracts svix-id, svix-timestamp, and svix-signature in gateway/platforms/webhook.py:912-921, while delivery deduplication only considers svix-id at gateway/platforms/webhook.py:660-667; the reported protocol therefore remains unsupported.

The proposed aliases correctly reuse _validate_svix_signature() (gateway/platforms/webhook.py:1010-1058), which already verifies the same signed-content layout, timestamp window, and v1,<base64> signature format. The existing partial-Svix test at tests/gateway/test_webhook_adapter.py:396-400 also confirms the shared validator fails closed when required fields are absent.

GitHub marks this PR dirty after merged #58508 changed nearby webhook tests and documentation, but #58508 implements generic V2 replay hardening rather than these Standard Webhooks aliases. This should be a mechanical salvage with conflict resolution against the current documentation/test context.

Automated hermes-sweeper review.

@teknium1 teknium1 added sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 14, 2026
@HwangJohn
HwangJohn force-pushed the fix/47451-standard-webhook-signature branch from cdba4e6 to 734d76f Compare July 14, 2026 22:59
andrexibiza added a commit to andrexibiza/hermes-agent that referenced this pull request Aug 16, 2026
…odes, signature_mode across CLI/REST/desktop, mode-bound test signing

Adapts HwangJohn's NousResearch#47849 (GitLab Standard Webhooks wire format: webhook-id/
webhook-timestamp/webhook-signature, signed {id}.{timestamp}.{raw_body}) and
sg-shag's NousResearch#80329 (X-Hindsight-Signature sha256=<hex> raw-body contract) into
the explicit-mode architecture as dedicated signature modes, preserving
attribution. Legacy gitlab mode remains the plaintext X-Gitlab-Token path.

- webhook_auth: gitlab_standard + hindsight modes; SIGNATURE_MODES extended
- CLI: subscribe --signature-mode persists on the route; test signs per the
  route's configured mode (all seven wire formats); list shows the mode
- REST: WebhookCreate.signature_mode; router persists and summarizes it
- Desktop: signature-mode select in the create dialog, detail display,
  i18n (en/zh), API types
- Tests: real wire-format acceptance + cross-mode rejection for both modes
@teknium1

Copy link
Copy Markdown
Contributor

Thanks @HwangJohn — this was the right fix. Your commit has been salvaged (authorship preserved) onto current main in #92024, with additional hardening tests (expired-timestamp reject, incomplete-headers fail-closed) added on top; the branch here had gone stale against the reworked test file. Leaving this open until #92024 lands, then it will be closed with credit.

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 P3 Low — cosmetic, nice to have platform/webhook Webhook / API server sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Support GitLab Standard Webhooks signing token (webhook-signature)

3 participants