Repository navigation
fix: correct verification facts in 7 skills - #208
Merged
Merged
Conversation
Stripe's docs: "Currently, the only valid live signature scheme is v1. To aid with testing, Stripe sends an additional signature with a fake v0 scheme, for test events." The reference called v0 "legacy". Reworded, and added the same fact to the providers.yaml brief so regeneration keeps it. Docs: https://docs.stripe.com/webhooks Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SKILL.md and the verification reference said Linear has no first-party SDK helper. @linear/sdk 97.0.0 exports LinearWebhookClient from @linear/sdk/webhooks; verify(rawBody, signature) checks the hex HMAC-SHA256 of the raw body against Linear-Signature and enforces +/-60 s on the signed webhookTimestamp body field, throwing on failure. Documented the helper and kept the manual verification the examples use (it is equivalent). Brief updated so regeneration doesn't reintroduce the claim. Source: https://github.com/linear/linear/blob/master/packages/sdk/src/webhooks/client.ts Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SKILL.md and the verification reference said HubSpot has no SDK helper for webhook verification. All four official client libraries have one: Signature.isValid in Node (@hubspot/api-client 14.0.1) and PHP (14.1.0), Signature.is_valid in Python (12.0.0) and Ruby (20.0.0). They support v1, v2 and v3 and raise on a v3 timestamp older than 5 minutes. Documented them, with the caveats that they take the URL as given and the Node one compares with ===, and kept the manual examples. Brief updated so regeneration keeps the fact. Sources: - https://github.com/HubSpot/hubspot-api-nodejs/blob/14.0.1/src/utils/signature.ts - https://github.com/HubSpot/hubspot-api-python/blob/v12.0.0/hubspot/utils/signature.py Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The overview listed X-Body-Signature as a "(some accounts) alternate name" for the webhook signature. Intercom's webhook docs (all versions) name only X-Hub-Signature (sha1=<hex HMAC-SHA1>). X-Body-Signature appears only on the Canvas Kit page, where it signs Canvas Kit requests with HMAC-SHA256. Removed it from the webhook header table, added a note explaining the difference, and said the same in the providers.yaml brief. Sources: - https://developers.intercom.com/docs/references/webhooks/webhook-models - https://developers.intercom.com/docs/canvas-kit Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The skill called x-square-signature (HMAC-SHA1) a "deprecated"/"legacy" header that Square "still sends". Square's current docs (step3validate and the other webhook pages in llms.txt) document only x-square-hmacsha256-signature and say nothing about x-square-signature, so neither "deprecated" nor "legacy" has a Square source. The only evidence for it is our own 2026-08 sandbox capture. Relabelled it as undocumented and observed on a live delivery, without claiming Square has stopped sending it. Brief updated to match. Docs: https://developer.squareup.com/docs/webhooks/step3validate Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SKILL.md, references/verification.md and the FastAPI example escaped '\' and
':' in each value before joining. Adyen's verify-hmac-signatures docs describe
no escaping ("use a colon (":") to delimit the values"), and every official
library joins the raw values for a NotificationRequestItem: Node
(hmacValidator.getDataToSign, 32.2.0), Java (Util.implode), Python
(generate_notification_sig), PHP and Ruby. The Node SDK's escaping branch only
runs for objects whose values are all strings, never for a
NotificationRequestItem. The mismatch only shows when a signed field such as
merchantReference contains ':' or '\', where the escaped version would reject
genuine deliveries.
- Removed escaping from the prose, both Python snippets and examples/fastapi.
- Added a regression test to all three example suites: a merchantReference
containing ':' and '\' must be signed verbatim (Express/Next.js pin the SDK's
behaviour, FastAPI pins the manual implementation).
- Brief updated so regeneration doesn't reintroduce escaping.
Docs: https://docs.adyen.com/development-resources/webhooks/secure-webhooks/verify-hmac-signatures/
SDK: https://github.com/Adyen/adyen-node-api-library/blob/main/src/utils/hmacValidator.ts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The skill said GitLab "uses simple token comparison (not HMAC)" and only
covered X-Gitlab-Token. Since GitLab 19.0 (GA in 19.1) webhooks can have a
signing token, and GitLab's docs recommend it over the secret token, which
they call "not recommended for new webhooks". Delivery follows the Standard
Webhooks spec:
- signed content "{webhook-id}.{webhook-timestamp}.{raw body}"
- key: signing token with whsec_ stripped, base64-decoded
- webhook-signature: space-separated "v1,<base64 HMAC-SHA256>" entries
- constant-time compare; check webhook-timestamp is recent (GitLab gives no
number; 5 minutes is the Standard Webhooks reference library default)
Changes:
- SKILL.md, references (verification, setup, overview): signing token as the
recommended path, X-Gitlab-Token kept as the legacy option, GitLab's
migration advice, API signing_token format.
- Express, Next.js and FastAPI examples now verify webhook-signature over the
raw body when it is present (GITLAB_WEBHOOK_SIGNING_TOKEN) and fall back to
X-Gitlab-Token otherwise. A request carrying a signature never falls back.
- Eight new tests per suite, including the published Standard Webhooks test
vector (whsec_MfKQ9r8G..., msg_p5jXN8AQM9LWM0D4loKWxJek, 1614265330).
- providers.yaml brief, README row and marketplace description updated.
Docs: https://docs.gitlab.com/user/project/integrations/webhooks/#signing-tokens
Spec: https://www.standardwebhooks.com/
Co-Authored-By: Claude Opus 5.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.
Fixes verification facts in seven provider skills that disagreed with the vendor's own docs or official SDK source. Each fix also updates that provider's
notesinproviders.yaml, so regenerating the skill won't bring the error back. Example code changed only for GitLab and Adyen. The other five fixes are prose only.Twilio was also on the list, but the skill and its brief already say HMAC-SHA1 with
X-Twilio-Signature, so nothing changed there. The Twilio error (HMAC-SHA256 listed as an algorithm, andI-Twilio-Idempotency-Tokenlisted as a secret header) is in the webhook-registry row, which is a different repo.GitLab (
gitlab-webhooks)Was wrong: "GitLab uses simple token comparison (not HMAC)". The skill covered only
X-Gitlab-Token.Now: Since GitLab 19.0 (GA in 19.1) a webhook can have a signing token, and GitLab recommends it. Delivery follows the Standard Webhooks spec:
{webhook-id}.{webhook-timestamp}.{raw body}whsec_stripped, then base64-decodedwebhook-signatureholds space-separatedv1,<base64>HMAC-SHA256 valuesX-Gitlab-Tokenis kept as the legacy option. All three examples verify the signature over the raw body whenwebhook-signatureis present, and fall back toX-Gitlab-Tokenotherwise, which is GitLab's migration advice. A request that carries a signature never falls back to the token.GitLab says to check that
webhook-timestampis "recent" but gives no number, and the spec only asks for "some allowable tolerance". The examples use 5 minutes, which is the default in the Standard Webhooks reference libraries. The SKILL.md, references, example READMEs,.env.examplefiles, root README row and marketplace description are updated to match.Sources:
{message_id}.{timestamp}.{body}" and "The secret token is not recommended for new webhooks."signing_token"Must be inwhsec_<base64>format encoding a 32-byte key."WEBHOOK_TOLERANCE_IN_SECONDS = 5 * 60.Tests: 8 new tests per suite. They include the published Standard Webhooks test vector (
msg_p5jXN8AQM9LWM0D4loKWxJek,1614265330,v1,g0hM9SsE+OTPJTGt/tmIKtSyZlE3uFJELVlNIOLJ1OE=), a match on one of several signatures, a tampered body, the wrong key, a stale timestamp, no fallback on a bad signature, and a missingwebhook-id.validate-provider.sh gitlab-webhooks: PASSEDAdyen (
adyen-webhooks)Was wrong: "Each field value is escaped (
\→\\,:→\:)". The Python snippets andexamples/fastapiescaped each value before joining.Now: The eight values are joined with
:exactly as they are, with no escaping. The escaping is removed from SKILL.md, references/verification.md andexamples/fastapi/main.py. The difference only shows when a signed field such asmerchantReferencecontains:or\. In that case the old FastAPI code would reject a genuine delivery. The Express and Next.js examples use the SDK, which already did this correctly.Sources:
@adyen/api-library32.2.0,hmacValidator.getDataToSign: for a NotificationRequestItem it runssignedDataList.join(HmacValidator.DATA_SEPARATOR). The escaping branch only runs for objects whose values are all strings. The Java, Python, PHP and Ruby libraries also join without escaping.Tests: Added a regression test to each suite: a
merchantReferenceofOrder:42\Amust be signed verbatim.validate-provider.sh adyen-webhooks: PASSEDSquare (
square-webhooks)Was wrong: "Square also still sends a deprecated
x-square-signature(HMAC-SHA1) header". The overview table said "Legacy ... still sent, deprecated".Now: Square's docs document only
x-square-hmacsha256-signature.x-square-signatureis described as undocumented and seen on a live sandbox delivery (2026-08,square-version: 2026-07-15). The skill no longer calls it deprecated or legacy on Square's authority. It also doesn't claim Square has stopped sending it, because that can't be verified.Source: https://developer.squareup.com/docs/webhooks/step3validate: "All webhook notifications from Square include an x-square-hmacsha256-signature header." None of the webhook pages in Square's llms.txt mention
x-square-signature.Tests: prose only.
validate-provider.sh square-webhooks: PASSEDIntercom (
intercom-webhooks)Was wrong: "
X-Body-Signature| (Some accounts) alternate name for the same value"Now:
X-Hub-Signature(sha1=<hex HMAC-SHA1>) is the only webhook signature header. A note explains thatX-Body-Signaturesigns Canvas Kit requests with HMAC-SHA256, which is a different product.Sources:
X-Hub-Signatureheader."X-Body-Signatureheader."Tests: prose only.
validate-provider.sh intercom-webhooks: PASSEDLinear (
linear-webhooks)Was wrong: "Linear has no first-party Node SDK helper for verifying webhooks" and "
@linear/sdk... does not ship a webhook verification helper"Now: Documents
LinearWebhookClientfrom@linear/sdk/webhooks(v97.0.0). Itsverify(rawBody, signature)checks the hex HMAC-SHA256 againstLinear-Signature, enforces ±60 s on the signedwebhookTimestamp, and throws on failure. The manual examples are kept because they do the same checks.Source: https://github.com/linear/linear/blob/master/packages/sdk/src/webhooks/client.ts:
public verify(rawBody: Buffer, signature: string, timestamp?: number | string): boolean. I confirmed this in the published 97.0.0 tarball, whoseexportsinclude./webhooks.Tests: prose only.
validate-provider.sh linear-webhooks: PASSEDHubSpot (
hubspot-webhooks)Was wrong: "HubSpot does not provide an SDK helper for webhook signature verification"
Now: Documents the helper in each official SDK:
@hubspot/api-client14.0.1:Signature.isValid\HubSpot\Utils\Signature::isValidSignature.is_validHubspot::Helpers::Signature#is_validAll four support v1, v2 and v3, and raise on a v3 timestamp older than 5 minutes. Two caveats are noted: the helpers take the URL as given, and the Node helper compares with
===. The manual examples are kept.Sources:
public static isValid(...),MAX_ALLOWED_TIMESTAMP = 300000def is_valid(Tests: prose only.
validate-provider.sh hubspot-webhooks: PASSEDStripe (
stripe-webhooks)Was wrong: "The
v1signature is the current version. Ignorev0(legacy)."Now:
v1is the only valid live scheme.v0is a fake signature that Stripe adds to test events to aid testing. Ignore it, but it is not a legacy scheme.Source: https://docs.stripe.com/webhooks: "To aid with testing, Stripe sends an additional signature with a fake
v0scheme, for test events."Tests: prose only.
validate-provider.sh stripe-webhooks: PASSED🤖 Generated with Claude Code