@isnap/sdk 1.3.0 → 1.4.0-next.204
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +1 -1
- package/dist/client.d.ts +2 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +3 -0
- package/dist/client.js.map +1 -1
- package/dist/generated/openapi.d.ts +1784 -346
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/resources/apps.d.ts +11 -4
- package/dist/resources/apps.d.ts.map +1 -1
- package/dist/resources/apps.js +11 -4
- package/dist/resources/apps.js.map +1 -1
- package/dist/resources/lines.d.ts +207 -6
- package/dist/resources/lines.d.ts.map +1 -1
- package/dist/resources/lines.js +216 -6
- package/dist/resources/lines.js.map +1 -1
- package/dist/resources/macos-sessions.d.ts +48 -0
- package/dist/resources/macos-sessions.d.ts.map +1 -0
- package/dist/resources/macos-sessions.js +51 -0
- package/dist/resources/macos-sessions.js.map +1 -0
- package/dist/resources/preorders.d.ts +19 -7
- package/dist/resources/preorders.d.ts.map +1 -1
- package/dist/resources/preorders.js +19 -7
- package/dist/resources/preorders.js.map +1 -1
- package/dist/resources/webhooks.d.ts +1 -1
- package/dist/resources/webhooks.js +1 -1
- package/dist/version.d.ts +2 -2
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +13 -5
- package/dist/version.js.map +1 -1
- package/dist/webhooks/types.d.ts +46 -3
- package/dist/webhooks/types.d.ts.map +1 -1
- package/dist/webhooks/types.js +3 -0
- package/dist/webhooks/types.js.map +1 -1
- package/dist/webhooks/verify.d.ts +22 -9
- package/dist/webhooks/verify.d.ts.map +1 -1
- package/dist/webhooks/verify.js +101 -29
- package/dist/webhooks/verify.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/webhooks/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAErE;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC9B;AA0BD,MAAM,MAAM,gBAAgB,GACxB,gBAAgB,GAChB,mBAAmB,GACnB,cAAc,GACd,mBAAmB,GACnB,gBAAgB,GAChB,kBAAkB,GAClB,cAAc,GACd,mBAAmB,GAMnB,4BAA4B,CAAA;AAEhC,MAAM,WAAW,YAAa,SAAQ,gBAAgB;IACpD,UAAU,EAAE,WAAW,MAAM,EAAE,CAAA;IAC/B,IAAI,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAA;CAC3B;AAID,MAAM,MAAM,iBAAiB,GACzB,gBAAgB,GAChB,mBAAmB,GACnB,kBAAkB,CAAA;AAEtB,wEAAwE;AACxE,KAAK,qBAAqB,GAAG;IAC3B,iBAAiB,EAAE,MAAM,CAAA;IACzB,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,YAAY,CAAA;IAC3B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,MAAM,iBAAiB,GACzB,CAAC,qBAAqB,GAAG;IACvB,SAAS,EAAE,SAAS,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAC,GACF,CAAC,qBAAqB,GAAG;IACvB,SAAS,EAAE,UAAU,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,CAAC,CAAA;AAEN,MAAM,WAAW,aAAc,SAAQ,gBAAgB;IACrD,UAAU,EAAE,YAAY,MAAM,EAAE,CAAA;IAChC,IAAI,EAAE,iBAAiB,CAAA;CACxB;AAID,MAAM,MAAM,aAAa,GACrB,gBAAgB,GAChB,mBAAmB,GACnB,cAAc,GACd,eAAe,GACf,uBAAuB,GACvB,oBAAoB,GACpB,qBAAqB,GAMrB,yBAAyB,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/webhooks/types.ts"],"names":[],"mappings":"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"}
|
package/dist/webhooks/types.js
CHANGED
|
@@ -20,6 +20,9 @@ export function isPreOrderEvent(event) {
|
|
|
20
20
|
export function isBindingEvent(event) {
|
|
21
21
|
return event.event_type.startsWith('binding.');
|
|
22
22
|
}
|
|
23
|
+
export function isMacosSessionEvent(event) {
|
|
24
|
+
return event.event_type.startsWith('macos_session.');
|
|
25
|
+
}
|
|
23
26
|
export function isAdminEvent(event) {
|
|
24
27
|
return event.event_type.startsWith('webhook.');
|
|
25
28
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/webhooks/types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/webhooks/types.ts"],"names":[],"mappings":"AA8WA,6EAA6E;AAE7E,MAAM,UAAU,cAAc,CAAC,KAAuB;IACpD,OAAO,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC,UAAU,CAAC,CAAA;AAChD,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAuB;IACrD,OAAO,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC,WAAW,CAAC,CAAA;AACjD,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAuB;IACjD,OAAO,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC,OAAO,CAAC,CAAA;AAC7C,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,KAAuB;IAEvB,OAAO,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC,mBAAmB,CAAC,CAAA;AACzD,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAuB;IAClD,OAAO,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAA;AAC9C,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAuB;IACrD,OAAO,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC,YAAY,CAAC,CAAA;AAClD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAuB;IACpD,OAAO,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC,UAAU,CAAC,CAAA;AAChD,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,KAAuB;IACzD,OAAO,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAA;AACtD,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAuB;IAClD,OAAO,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC,UAAU,CAAC,CAAA;AAChD,CAAC"}
|
|
@@ -13,12 +13,23 @@ export interface VerifyWebhookOptions {
|
|
|
13
13
|
*/
|
|
14
14
|
now?: () => number;
|
|
15
15
|
}
|
|
16
|
+
type WebhookHeaders = Record<string, string | string[] | undefined> | Headers;
|
|
16
17
|
/**
|
|
17
|
-
* Verify an incoming webhook delivery
|
|
18
|
+
* Verify an incoming webhook delivery. Every iSnap delivery carries two
|
|
19
|
+
* signatures over the same body, and this checks one of them:
|
|
18
20
|
*
|
|
19
|
-
* -
|
|
20
|
-
* `
|
|
21
|
-
*
|
|
21
|
+
* - **Standard Webhooks** (https://www.standardwebhooks.com), checked
|
|
22
|
+
* whenever a `webhook-signature` header is present: `webhook-id`,
|
|
23
|
+
* `webhook-timestamp`, and `webhook-signature` carrying `v1,` + base64
|
|
24
|
+
* `HMAC-SHA256(key, "<webhook-id>.<webhook-timestamp>.<rawBody>")`,
|
|
25
|
+
* where the key is the base64 decoding of the secret without its
|
|
26
|
+
* `whsec_` (or legacy `whs_`) prefix.
|
|
27
|
+
* - **The iSnap scheme**, the fallback when there is no
|
|
28
|
+
* `webhook-signature`: `X-iSnap-Signature` carries the hex-encoded
|
|
29
|
+
* `HMAC-SHA256(secret, "<timestamp>.<rawBody>")` and `X-iSnap-Timestamp`
|
|
30
|
+
* the unix-second issuance time.
|
|
31
|
+
*
|
|
32
|
+
* Both paths take the secret string exactly as the dashboard shows it.
|
|
22
33
|
*
|
|
23
34
|
* Throws `WebhookSignatureError` on missing headers, malformed
|
|
24
35
|
* timestamp, expired timestamp (outside `tolerance`), or signature
|
|
@@ -32,10 +43,12 @@ export interface VerifyWebhookOptions {
|
|
|
32
43
|
* Headers may be a `Record<string, string>` (e.g. Express's `req.headers`)
|
|
33
44
|
* or a `Headers` instance — both are read case-insensitively.
|
|
34
45
|
*
|
|
35
|
-
* **Consumers MUST dedupe by `event.event_id`**
|
|
36
|
-
* envelope (same `event_id`, same body,
|
|
37
|
-
* timestamp). Without consumer-side dedup, an
|
|
38
|
-
* model produces duplicate side-effects on the
|
|
46
|
+
* **Consumers MUST dedupe by `event.event_id`** (also sent as `webhook-id`)
|
|
47
|
+
* — retries replay the same envelope (same `event_id`, same body,
|
|
48
|
+
* refreshed timestamp and signatures). Without consumer-side dedup, an
|
|
49
|
+
* at-least-once delivery model produces duplicate side-effects on the
|
|
50
|
+
* partner system.
|
|
39
51
|
*/
|
|
40
|
-
export declare function verifyWebhook(rawBody: string, headers:
|
|
52
|
+
export declare function verifyWebhook(rawBody: string, headers: WebhookHeaders, secret: string | readonly string[], options?: VerifyWebhookOptions): WebhookEventBase;
|
|
53
|
+
export {};
|
|
41
54
|
//# sourceMappingURL=verify.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/webhooks/verify.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAIlD,MAAM,WAAW,oBAAoB;IACnC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAED
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/webhooks/verify.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAIlD,MAAM,WAAW,oBAAoB;IACnC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAED,KAAK,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GAAG,OAAO,CAAA;AAE7E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,EAClC,OAAO,GAAE,oBAAyB,GACjC,gBAAgB,CAKlB"}
|
package/dist/webhooks/verify.js
CHANGED
|
@@ -3,11 +3,21 @@ import { WebhookSignatureError } from '../errors.js';
|
|
|
3
3
|
import { parseWebhookEvent } from './parse.js';
|
|
4
4
|
const DEFAULT_TOLERANCE_SECONDS = 300;
|
|
5
5
|
/**
|
|
6
|
-
* Verify an incoming webhook delivery
|
|
6
|
+
* Verify an incoming webhook delivery. Every iSnap delivery carries two
|
|
7
|
+
* signatures over the same body, and this checks one of them:
|
|
7
8
|
*
|
|
8
|
-
* -
|
|
9
|
-
* `
|
|
10
|
-
*
|
|
9
|
+
* - **Standard Webhooks** (https://www.standardwebhooks.com), checked
|
|
10
|
+
* whenever a `webhook-signature` header is present: `webhook-id`,
|
|
11
|
+
* `webhook-timestamp`, and `webhook-signature` carrying `v1,` + base64
|
|
12
|
+
* `HMAC-SHA256(key, "<webhook-id>.<webhook-timestamp>.<rawBody>")`,
|
|
13
|
+
* where the key is the base64 decoding of the secret without its
|
|
14
|
+
* `whsec_` (or legacy `whs_`) prefix.
|
|
15
|
+
* - **The iSnap scheme**, the fallback when there is no
|
|
16
|
+
* `webhook-signature`: `X-iSnap-Signature` carries the hex-encoded
|
|
17
|
+
* `HMAC-SHA256(secret, "<timestamp>.<rawBody>")` and `X-iSnap-Timestamp`
|
|
18
|
+
* the unix-second issuance time.
|
|
19
|
+
*
|
|
20
|
+
* Both paths take the secret string exactly as the dashboard shows it.
|
|
11
21
|
*
|
|
12
22
|
* Throws `WebhookSignatureError` on missing headers, malformed
|
|
13
23
|
* timestamp, expired timestamp (outside `tolerance`), or signature
|
|
@@ -21,12 +31,19 @@ const DEFAULT_TOLERANCE_SECONDS = 300;
|
|
|
21
31
|
* Headers may be a `Record<string, string>` (e.g. Express's `req.headers`)
|
|
22
32
|
* or a `Headers` instance — both are read case-insensitively.
|
|
23
33
|
*
|
|
24
|
-
* **Consumers MUST dedupe by `event.event_id`**
|
|
25
|
-
* envelope (same `event_id`, same body,
|
|
26
|
-
* timestamp). Without consumer-side dedup, an
|
|
27
|
-
* model produces duplicate side-effects on the
|
|
34
|
+
* **Consumers MUST dedupe by `event.event_id`** (also sent as `webhook-id`)
|
|
35
|
+
* — retries replay the same envelope (same `event_id`, same body,
|
|
36
|
+
* refreshed timestamp and signatures). Without consumer-side dedup, an
|
|
37
|
+
* at-least-once delivery model produces duplicate side-effects on the
|
|
38
|
+
* partner system.
|
|
28
39
|
*/
|
|
29
40
|
export function verifyWebhook(rawBody, headers, secret, options = {}) {
|
|
41
|
+
if (getHeader(headers, 'webhook-signature') !== null) {
|
|
42
|
+
return verifyStandardWebhook(rawBody, headers, secret, options);
|
|
43
|
+
}
|
|
44
|
+
return verifyISnapWebhook(rawBody, headers, secret, options);
|
|
45
|
+
}
|
|
46
|
+
function verifyISnapWebhook(rawBody, headers, secret, options) {
|
|
30
47
|
const sigHeader = getHeader(headers, 'x-isnap-signature');
|
|
31
48
|
if (!sigHeader) {
|
|
32
49
|
throw new WebhookSignatureError('Missing X-iSnap-Signature header');
|
|
@@ -35,16 +52,86 @@ export function verifyWebhook(rawBody, headers, secret, options = {}) {
|
|
|
35
52
|
if (!tsHeader) {
|
|
36
53
|
throw new WebhookSignatureError('Missing X-iSnap-Timestamp header');
|
|
37
54
|
}
|
|
55
|
+
const timestamp = checkTimestamp(tsHeader, 'X-iSnap-Timestamp', options);
|
|
56
|
+
const candidates = secretCandidates(secret);
|
|
57
|
+
const provided = decodeHex(sigHeader);
|
|
58
|
+
if (!provided) {
|
|
59
|
+
throw new WebhookSignatureError('X-iSnap-Signature is not valid hex');
|
|
60
|
+
}
|
|
61
|
+
const signedPayload = `${timestamp}.${rawBody}`;
|
|
62
|
+
// Multi-secret loop returns early on first match — this is a minor
|
|
63
|
+
// timing side-channel during the rotate-secret grace window (an
|
|
64
|
+
// attacker who controls request timing could distinguish "matched first
|
|
65
|
+
// candidate" from "matched second"). Practical exploit value is near
|
|
66
|
+
// zero: knowing which secret matched does not help forge signatures
|
|
67
|
+
// for either secret. Worth revisiting if real partners run rotation
|
|
68
|
+
// through hostile networks.
|
|
69
|
+
for (const candidate of candidates) {
|
|
70
|
+
const expected = createHmac('sha256', candidate).update(signedPayload).digest();
|
|
71
|
+
if (provided.length === expected.length && timingSafeEqual(provided, expected)) {
|
|
72
|
+
return parseWebhookEvent(rawBody);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
throw new WebhookSignatureError('Webhook signature did not match any provided secret');
|
|
76
|
+
}
|
|
77
|
+
function verifyStandardWebhook(rawBody, headers, secret, options) {
|
|
78
|
+
const msgId = getHeader(headers, 'webhook-id');
|
|
79
|
+
if (!msgId) {
|
|
80
|
+
throw new WebhookSignatureError('Missing webhook-id header');
|
|
81
|
+
}
|
|
82
|
+
const tsHeader = getHeader(headers, 'webhook-timestamp');
|
|
83
|
+
if (!tsHeader) {
|
|
84
|
+
throw new WebhookSignatureError('Missing webhook-timestamp header');
|
|
85
|
+
}
|
|
86
|
+
const timestamp = checkTimestamp(tsHeader, 'webhook-timestamp', options);
|
|
87
|
+
const candidates = secretCandidates(secret);
|
|
88
|
+
// Space-delimited `<version>,<base64>` pairs; only `v1` (HMAC-SHA256)
|
|
89
|
+
// exists today, and a version this SDK does not know is skipped rather
|
|
90
|
+
// than rejected, so a sender adding a scheme does not break verification.
|
|
91
|
+
const provided = (getHeader(headers, 'webhook-signature') ?? '')
|
|
92
|
+
.split(' ')
|
|
93
|
+
.filter((entry) => entry.startsWith('v1,'))
|
|
94
|
+
.map((entry) => Buffer.from(entry.slice(3), 'base64'));
|
|
95
|
+
if (provided.length === 0) {
|
|
96
|
+
throw new WebhookSignatureError('webhook-signature carries no v1 signature');
|
|
97
|
+
}
|
|
98
|
+
const signedPayload = `${msgId}.${timestamp}.${rawBody}`;
|
|
99
|
+
// Same early-return trade-off as the iSnap scheme above.
|
|
100
|
+
for (const candidate of candidates) {
|
|
101
|
+
const expected = createHmac('sha256', standardWebhookKey(candidate))
|
|
102
|
+
.update(signedPayload)
|
|
103
|
+
.digest();
|
|
104
|
+
for (const signature of provided) {
|
|
105
|
+
if (signature.length === expected.length && timingSafeEqual(signature, expected)) {
|
|
106
|
+
return parseWebhookEvent(rawBody);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
throw new WebhookSignatureError('Webhook signature did not match any provided secret');
|
|
111
|
+
}
|
|
112
|
+
// The Standard Webhooks key: the base64 decoding of the secret without its
|
|
113
|
+
// prefix. iSnap secrets are `whsec_` (or, minted by an older rotation,
|
|
114
|
+
// `whs_`) followed by hex, which is valid base64, so the dashboard string
|
|
115
|
+
// works as-is here and in any official Standard Webhooks library (pass a
|
|
116
|
+
// `whs_` secret to those as `whsec_` + the same hex).
|
|
117
|
+
function standardWebhookKey(secret) {
|
|
118
|
+
for (const prefix of ['whsec_', 'whs_']) {
|
|
119
|
+
if (secret.startsWith(prefix))
|
|
120
|
+
return Buffer.from(secret.slice(prefix.length), 'base64');
|
|
121
|
+
}
|
|
122
|
+
return Buffer.from(secret, 'base64');
|
|
123
|
+
}
|
|
124
|
+
function checkTimestamp(tsHeader, headerName, options) {
|
|
38
125
|
// Strict integer-only check — `Number.parseInt` would happily accept
|
|
39
126
|
// `"1714478400foo"` and silently truncate. The HMAC compare downstream
|
|
40
127
|
// catches the resulting payload mismatch, but rejecting earlier here
|
|
41
128
|
// surfaces a clearer error and avoids one unnecessary HMAC computation.
|
|
42
129
|
if (!/^\d+$/.test(tsHeader)) {
|
|
43
|
-
throw new WebhookSignatureError(`Malformed
|
|
130
|
+
throw new WebhookSignatureError(`Malformed ${headerName}: ${tsHeader}`);
|
|
44
131
|
}
|
|
45
132
|
const timestamp = Number(tsHeader);
|
|
46
133
|
if (!Number.isFinite(timestamp) || timestamp <= 0) {
|
|
47
|
-
throw new WebhookSignatureError(`Malformed
|
|
134
|
+
throw new WebhookSignatureError(`Malformed ${headerName}: ${tsHeader}`);
|
|
48
135
|
}
|
|
49
136
|
const tolerance = options.tolerance ?? DEFAULT_TOLERANCE_SECONDS;
|
|
50
137
|
// Validate before use — `Math.abs(...) > NaN` is always false (so the
|
|
@@ -59,29 +146,14 @@ export function verifyWebhook(rawBody, headers, secret, options = {}) {
|
|
|
59
146
|
if (Math.abs(nowSec - timestamp) > tolerance) {
|
|
60
147
|
throw new WebhookSignatureError(`Webhook timestamp is outside the ${tolerance}s tolerance window (drift: ${nowSec - timestamp}s)`);
|
|
61
148
|
}
|
|
149
|
+
return timestamp;
|
|
150
|
+
}
|
|
151
|
+
function secretCandidates(secret) {
|
|
62
152
|
const candidates = typeof secret === 'string' ? [secret] : secret;
|
|
63
153
|
if (candidates.length === 0) {
|
|
64
154
|
throw new WebhookSignatureError('No webhook secret(s) provided');
|
|
65
155
|
}
|
|
66
|
-
|
|
67
|
-
if (!provided) {
|
|
68
|
-
throw new WebhookSignatureError('X-iSnap-Signature is not valid hex');
|
|
69
|
-
}
|
|
70
|
-
const signedPayload = `${timestamp}.${rawBody}`;
|
|
71
|
-
// Multi-secret loop returns early on first match — this is a minor
|
|
72
|
-
// timing side-channel during the rotate-secret grace window (an
|
|
73
|
-
// attacker who controls request timing could distinguish "matched first
|
|
74
|
-
// candidate" from "matched second"). Practical exploit value is near
|
|
75
|
-
// zero: knowing which secret matched does not help forge signatures
|
|
76
|
-
// for either secret. Worth revisiting if real partners run rotation
|
|
77
|
-
// through hostile networks.
|
|
78
|
-
for (const candidate of candidates) {
|
|
79
|
-
const expected = createHmac('sha256', candidate).update(signedPayload).digest();
|
|
80
|
-
if (provided.length === expected.length && timingSafeEqual(provided, expected)) {
|
|
81
|
-
return parseWebhookEvent(rawBody);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
throw new WebhookSignatureError('Webhook signature did not match any provided secret');
|
|
156
|
+
return candidates;
|
|
85
157
|
}
|
|
86
158
|
function getHeader(headers, name) {
|
|
87
159
|
if (typeof Headers !== 'undefined' && headers instanceof Headers) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/webhooks/verify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAG9C,MAAM,yBAAyB,GAAG,GAAG,CAAA;
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/webhooks/verify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAG9C,MAAM,yBAAyB,GAAG,GAAG,CAAA;AAmBrC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,UAAU,aAAa,CAC3B,OAAe,EACf,OAAuB,EACvB,MAAkC,EAClC,UAAgC,EAAE;IAElC,IAAI,SAAS,CAAC,OAAO,EAAE,mBAAmB,CAAC,KAAK,IAAI,EAAE,CAAC;QACrD,OAAO,qBAAqB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;IACjE,CAAC;IACD,OAAO,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;AAC9D,CAAC;AAED,SAAS,kBAAkB,CACzB,OAAe,EACf,OAAuB,EACvB,MAAkC,EAClC,OAA6B;IAE7B,MAAM,SAAS,GAAG,SAAS,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAA;IACzD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,qBAAqB,CAAC,kCAAkC,CAAC,CAAA;IACrE,CAAC;IACD,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAA;IACxD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,qBAAqB,CAAC,kCAAkC,CAAC,CAAA;IACrE,CAAC;IACD,MAAM,SAAS,GAAG,cAAc,CAAC,QAAQ,EAAE,mBAAmB,EAAE,OAAO,CAAC,CAAA;IACxE,MAAM,UAAU,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAA;IAE3C,MAAM,QAAQ,GAAG,SAAS,CAAC,SAAS,CAAC,CAAA;IACrC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,qBAAqB,CAAC,oCAAoC,CAAC,CAAA;IACvE,CAAC;IACD,MAAM,aAAa,GAAG,GAAG,SAAS,IAAI,OAAO,EAAE,CAAA;IAC/C,mEAAmE;IACnE,gEAAgE;IAChE,wEAAwE;IACxE,qEAAqE;IACrE,oEAAoE;IACpE,oEAAoE;IACpE,4BAA4B;IAC5B,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,MAAM,EAAE,CAAA;QAC/E,IAAI,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,IAAI,eAAe,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC;YAC/E,OAAO,iBAAiB,CAAC,OAAO,CAAC,CAAA;QACnC,CAAC;IACH,CAAC;IACD,MAAM,IAAI,qBAAqB,CAAC,qDAAqD,CAAC,CAAA;AACxF,CAAC;AAED,SAAS,qBAAqB,CAC5B,OAAe,EACf,OAAuB,EACvB,MAAkC,EAClC,OAA6B;IAE7B,MAAM,KAAK,GAAG,SAAS,CAAC,OAAO,EAAE,YAAY,CAAC,CAAA;IAC9C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,qBAAqB,CAAC,2BAA2B,CAAC,CAAA;IAC9D,CAAC;IACD,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAA;IACxD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,qBAAqB,CAAC,kCAAkC,CAAC,CAAA;IACrE,CAAC;IACD,MAAM,SAAS,GAAG,cAAc,CAAC,QAAQ,EAAE,mBAAmB,EAAE,OAAO,CAAC,CAAA;IACxE,MAAM,UAAU,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAA;IAE3C,sEAAsE;IACtE,uEAAuE;IACvE,0EAA0E;IAC1E,MAAM,QAAQ,GAAG,CAAC,SAAS,CAAC,OAAO,EAAE,mBAAmB,CAAC,IAAI,EAAE,CAAC;SAC7D,KAAK,CAAC,GAAG,CAAC;SACV,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;SAC1C,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAA;IACxD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,qBAAqB,CAAC,2CAA2C,CAAC,CAAA;IAC9E,CAAC;IACD,MAAM,aAAa,GAAG,GAAG,KAAK,IAAI,SAAS,IAAI,OAAO,EAAE,CAAA;IACxD,yDAAyD;IACzD,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,EAAE,kBAAkB,CAAC,SAAS,CAAC,CAAC;aACjE,MAAM,CAAC,aAAa,CAAC;aACrB,MAAM,EAAE,CAAA;QACX,KAAK,MAAM,SAAS,IAAI,QAAQ,EAAE,CAAC;YACjC,IAAI,SAAS,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,IAAI,eAAe,CAAC,SAAS,EAAE,QAAQ,CAAC,EAAE,CAAC;gBACjF,OAAO,iBAAiB,CAAC,OAAO,CAAC,CAAA;YACnC,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,IAAI,qBAAqB,CAAC,qDAAqD,CAAC,CAAA;AACxF,CAAC;AAED,2EAA2E;AAC3E,uEAAuE;AACvE,0EAA0E;AAC1E,yEAAyE;AACzE,sDAAsD;AACtD,SAAS,kBAAkB,CAAC,MAAc;IACxC,KAAK,MAAM,MAAM,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC;QACxC,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,CAAA;IAC1F,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;AACtC,CAAC;AAED,SAAS,cAAc,CACrB,QAAgB,EAChB,UAAkB,EAClB,OAA6B;IAE7B,qEAAqE;IACrE,uEAAuE;IACvE,qEAAqE;IACrE,wEAAwE;IACxE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,qBAAqB,CAAC,aAAa,UAAU,KAAK,QAAQ,EAAE,CAAC,CAAA;IACzE,CAAC;IACD,MAAM,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAA;IAClC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,qBAAqB,CAAC,aAAa,UAAU,KAAK,QAAQ,EAAE,CAAC,CAAA;IACzE,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,yBAAyB,CAAA;IAChE,sEAAsE;IACtE,oEAAoE;IACpE,qEAAqE;IACrE,mEAAmE;IACnE,mEAAmE;IACnE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;QACjD,MAAM,IAAI,qBAAqB,CAC7B,uDAAuD,MAAM,CAAC,SAAS,CAAC,GAAG,CAC5E,CAAA;IACH,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,GAAG,IAAI,UAAU,CAAC,EAAE,CAAA;IAC5C,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,SAAS,CAAC,GAAG,SAAS,EAAE,CAAC;QAC7C,MAAM,IAAI,qBAAqB,CAC7B,oCAAoC,SAAS,8BAA8B,MAAM,GAAG,SAAS,IAAI,CAClG,CAAA;IACH,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,SAAS,gBAAgB,CAAC,MAAkC;IAC1D,MAAM,UAAU,GAAG,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAA;IACjE,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,qBAAqB,CAAC,+BAA+B,CAAC,CAAA;IAClE,CAAC;IACD,OAAO,UAAU,CAAA;AACnB,CAAC;AAED,SAAS,SAAS,CAAC,OAAuB,EAAE,IAAY;IACtD,IAAI,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,YAAY,OAAO,EAAE,CAAC;QACjE,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAC1B,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAA;IAChC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACnD,IAAI,GAAG,CAAC,WAAW,EAAE,KAAK,KAAK;YAAE,SAAQ;QACzC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAA;QACjD,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAA;QAC3C,OAAO,IAAI,CAAA;IACb,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,SAAS,SAAS,CAAC,GAAW;IAC5B,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IACpE,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;AAChC,CAAC;AAED,SAAS,UAAU;IACjB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;AACtC,CAAC"}
|