[Security] Verify GitHub webhook signatures and rate-limit /github-webhook - #178
Merged
N-thnI merged 2 commits intoJul 28, 2026
Merged
Conversation
The relayer accepted any JSON body shaped like a merged-PR event with no auth and no throttling, so a spoofed request could trigger on-chain registration once real signing is wired up. Add HMAC-SHA256 verification of X-Hub-Signature-256 (401 on missing/invalid, checked before any body-derived logic) and express-rate-limit on the route (429 over quota). Document GITHUB_WEBHOOK_SECRET and check off both README deployment checklist items. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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
/github-webhookaccepted any JSON body shaped like a merged-PR event with no authentication and no rate limiting — confirmed via the repro in the issue (unsigned request returned200 { registered: true }).X-Hub-Signature-256(raw body captured viaexpress.json({ verify }), compared withcrypto.timingSafeEqual) — returns401on missing/invalid signature before any body-derived logic orregisterTaskOnChaincall runs.express-rate-limitscoped to/github-webhook— returns429once the configured threshold is exceeded.GITHUB_WEBHOOK_SECRETin.env.exampleand the README's Environment Variables table; update the Webhook Relayer docs, diagrams, and API reference; check off both README Deployment Checklist items.scripts/mock-webhook.jsnow signs its payload withGITHUB_WEBHOOK_SECRETso local simulation exercises the real verification path.Acceptance criteria
X-Hub-Signature-256→401,registerTaskOnChainnever invoked ✅ (tested)429✅ (tested)GITHUB_WEBHOOK_SECRETdocumented in.env.exampleand README env table ✅Test plan
npx jest __tests__/webhook.test.js— 5 new tests (missing signature, invalid signature, wrong-secret signature, valid signature succeeds, rate-limit 429)npx eslint index.js __tests__/webhook.test.js scripts/mock-webhook.js— cleancurlrepro against the running relayer — now returns401instead of200 { registered: true }; a correctly signed request still returns200Closes #155