feat(event-process): SignatureValidator registry + 7 provider validators (EVO-1210)#38
Conversation
…tors (EVO-1210)
Adds webhook signature validation to the event-process pipeline (story 3.4).
EventProcessService.handle() now resolves a per-provider validator via the
registry and runs it right after envelope validation: an invalid or
unverifiable signature is dropped (ack, never nack — no redelivery) with a
structured warning and the evo_webhook_signature_invalid_total{platform,reason}
counter; an unregistered platform (e.g. `unknown`) is dropped the same way.
Validators (plain, unit-testable classes resolved by the registry with secrets
from env via ConfigService):
- evolution-api: static token (apikey header / Bearer)
- sparkpost: HTTP Basic Auth
- sendgrid: ECDSA over timestamp+body, opt-in (passes through with no key)
- mailersend: HMAC-SHA256 (hex)
- resend: Svix
- ses: AWS SNS (sns-validator) + .amazonaws.com cert allowlist
- mandrill: HMAC-SHA1 (base64) over webhook URL + sorted params
Fail-closed when a secret is missing (except opt-in sendgrid); the registry
logs an "unconfigured-secrets" warning at boot so misconfiguration does not
masquerade as an attack in the metric. Constant-time comparisons throughout.
Adds deps svix + sns-validator (+ type shim) and documents the new
*_WEBHOOK_* env vars in .env.example. Covered by per-validator specs (valid +
invalid fixtures, AC4), a registry spec and EventProcessService drop/pass specs
— 45 tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Sorry @nickoliveira23, you have reached your weekly rate limit of 500000 diff characters.
Please try again later or upgrade to continue using Sourcery
🔴 Reprovado — EVO-1210 [3.4] SignatureValidatorOs 4 critérios de aceite passam no texto literal, mas a revisão adversarial achou 2 quebras de segurança blocker-grade (ambas verificadas no código real). Card volta para Todo; PR fica aberto para o ajuste. B1 — SparkPost descarta 100% do tráfego legítimoO B2 — SES fail-OPEN (forja de assinatura)
Branch íntegro (base |
…idator (EVO-1210) The custom `/(^|\.)amazonaws\.com$/` host pattern weakened sns-validator's secure default: an attacker could host a forged signing cert on a public S3 bucket (*.s3.amazonaws.com) and pass verification. Drop the custom override (use the library's default `^sns\.…\.amazonaws\.com` pattern) and tighten the local cert-URL guard to the same strict SNS host shape. Adds a fixture proving an S3-hosted cert is rejected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…t (EVO-1210) The normalizer redacted the Authorization header before publishing the envelope, so the SparkPost validator (HTTP Basic Auth) always saw [REDACTED] and dropped 100% of legitimate SparkPost traffic. Preserve Authorization for the `sparkpost` platform only; every other credential header, and Authorization on any other platform, stays redacted. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
dpaes
left a comment
There was a problem hiding this comment.
✅ Aprovado — EVO-1210 [3.4] SignatureValidator
Re-review dos fixes (head 85b191f): os 2 blockers fecharam e foram verificados no código final.
- B1 (SparkPost):
Authorizationpreservado exclusivamente quandoplatform === 'sparkpost'(payload-normalizer.shouldRedact); todas as outras plataformas e demais headers de credencial seguem redigidos — sem novo vazamento. O validator passa a verificar oBasicreal (constant-time). - B2 (SES): removido o override frouxo; usa o default seguro do
sns-validator+ guard^sns\.[a-z0-9-]{3,}\.amazonaws\.com(\.cn)?$. Cert forjado em*.s3.amazonaws.comé rejeitado. - tsc / jest (82) / eslint limpos (auto-reportados — CI do evo-flow não roda).
Follow-up registrado (não-bloqueante): o B1 faz o secret Basic-Auth do SparkPost ser persistido no event-store (escopado aos eventos sparkpost). Para não persistir a credencial, o end-state mais limpo é validar o Basic no receiver (antes da redação) — fica como melhoria futura, fora desta entrega.
Aprovado e mergeado para develop.
What
Story 3.4 of the Webhook Event Pipeline: signature validation for inbound provider webhooks.
EventProcessService.handle()resolves a per-provider validator from a registry and runs it right after envelope validation — an invalid/unverifiable signature, or an unregistered platform, is dropped (ack, never nack) with a structured warning and theevo_webhook_signature_invalid_total{platform,reason}counter.Validators
Plain, unit-testable classes resolved by
SignatureValidatorRegistrywith secrets from env viaConfigService:apikeyheader /Bearer)timestamp+body, opt-in (passes through when no key)sns-validator) +.amazonaws.comcert allowlistConstant-time comparisons throughout; fail-closed on a missing secret (except opt-in sendgrid), with a boot-time "unconfigured-secrets" warning so misconfiguration doesn't masquerade as an attack in the metric.
Acceptance Criteria
Decisions / notes
validateis async (boolean | Promise<boolean>) — SES/SNS fetches the signing cert over HTTPS;handle()awaits.svix,sns-validator(+ type shim). New env vars documented in.env.example.Tests
45 unit tests: per-validator (valid + invalid), registry resolution, and
EventProcessServicedrop/pass/no-validator paths.tsc+ eslint clean.Story 3.4 — blocked-by EVO-1208 (Done). Unblocks EVO-1211 [3.5] (idempotency wires into the same
handle()).