Skip to content

feat(dashboard): add size and rate limits with structured rejection logs to webhook endpoint - #274

Merged
Lakes41 merged 2 commits into
Adamantine-guild:mainfrom
vreabernardo:feat/webhook-abuse-guards
Jul 24, 2026
Merged

feat(dashboard): add size and rate limits with structured rejection logs to webhook endpoint#274
Lakes41 merged 2 commits into
Adamantine-guild:mainfrom
vreabernardo:feat/webhook-abuse-guards

Conversation

@vreabernardo

Copy link
Copy Markdown
Contributor

Closes #260

Stacked on #273 (main does not build at HEAD; this branches from the repaired tree). Happy to rebase once #273 merges.

The webhook route now rejects abuse before spending CPU on HMAC verification:

  • Bodies over WEBHOOK_MAX_BODY_BYTES (default 256 KB) get a 413, checked against both the declared content-length and the actual body, since the header can be absent or lying.
  • A source (first x-forwarded-for entry, else x-real-ip) that fails verification more than WEBHOOK_INVALID_ATTEMPT_LIMIT times (default 10) inside WEBHOOK_RATE_LIMIT_WINDOW_MS (default 60s) gets a 429 until the sliding window expires. A successful verification clears the source's window, so a legitimate sender with one bad clock sync is not punished. Limiter is in-process, noted in SECURITY.md that multi-instance deployments should back it with shared storage.
  • Every rejection emits one JSON line via console.warn with a reason enum (oversized_payload, invalid_signature, expired_timestamp, rate_limited, malformed_header), source, endpoint, status, and timestamp. The log never contains the secret, the signature header, or the body; there is a test asserting exactly that.

New module is apps/dashboard/lib/webhooks/abuse-guard.ts; the route diff is small. SECURITY.md documents the limits and log format, and the webhook-utils README best-practices section points at it, since the guards live in the route rather than the package.

Verification (watched locally):

  • npx tsx --test test/webhook-abuse.test.ts — 14/14 pass, covering 413 on declared and actual size, 429 after repeated invalid signatures, per-source isolation, reset-on-success, expired-timestamp classification, and log hygiene.
  • Full dashboard suite npx tsx --test 'test/**/*.test.ts' — 592/592 pass.
  • tsc --noEmit (5.6.3) — no new findings.
  • ESLint on the three touched TS files — clean.

@Lakes41
Lakes41 merged commit 4ed2db9 into Adamantine-guild:main Jul 24, 2026
2 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add rate limiting, payload size limits, and structured rejection logging to the webhook endpoint

2 participants