Skip to content

Commit 7cc4068

Browse files
authored
feat: only accept string payloads for webhooks.verify() and webhooks.verifyAndReceive() and the middleware (#793)
BREAKING CHANGE: Only accept string payloads for `webhooks.verify()` and `webhooks.verifyAndReceive()` BREAKING CHANGE: The middleware now expects only string payloads for `request.body`
1 parent d71b011 commit 7cc4068

10 files changed

+21
-125
lines changed

src/index.ts

+7-35
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { createLogger } from "./createLogger";
22
import { createEventHandler } from "./event-handler/index";
33
import { sign } from "./sign";
4-
import { verify } from "./verify";
4+
import { verify } from "@octokit/webhooks-methods";
55
import { verifyAndReceive } from "./verify-and-receive";
66
import {
77
EmitterWebhookEvent,
@@ -13,27 +13,15 @@ import {
1313
WebhookError,
1414
WebhookEventHandlerError,
1515
EmitterWebhookEventWithStringPayloadAndSignature,
16-
EmitterWebhookEventWithSignature,
1716
} from "./types";
1817

1918
export { createNodeMiddleware } from "./middleware/node/index";
2019
export { emitterEventNames } from "./generated/webhook-names";
2120

22-
export type Iverify = {
23-
/** @deprecated Passing a JSON payload object to `verify()` is deprecated and the functionality will be removed in a future release of `@octokit/webhooks`. */
24-
(eventPayload: object, signature: string): Promise<boolean>;
25-
(eventPayload: string, signature: string): Promise<boolean>;
26-
};
27-
export type IverifyAndReceive = {
28-
/** @deprecated Passing a JSON payload object to `verifyAndReceive()` is deprecated and the functionality will be removed in a future release of `@octokit/webhooks`. */
29-
(options: EmitterWebhookEventWithSignature): Promise<void>;
30-
(options: EmitterWebhookEventWithStringPayloadAndSignature): Promise<void>;
31-
};
32-
3321
// U holds the return value of `transform` function in Options
3422
class Webhooks<TTransformed = unknown> {
3523
public sign: (payload: string | object) => Promise<string>;
36-
public verify: Iverify;
24+
public verify: (eventPayload: string, signature: string) => Promise<boolean>;
3725
public on: <E extends EmitterWebhookEventName>(
3826
event: E | E[],
3927
callback: HandlerFunction<E, TTransformed>
@@ -45,7 +33,9 @@ class Webhooks<TTransformed = unknown> {
4533
callback: RemoveHandlerFunction<E, TTransformed>
4634
) => void;
4735
public receive: (event: EmitterWebhookEvent) => Promise<void>;
48-
public verifyAndReceive: IverifyAndReceive;
36+
public verifyAndReceive: (
37+
options: EmitterWebhookEventWithStringPayloadAndSignature
38+
) => Promise<void>;
4939

5040
constructor(options: Options<TTransformed> & { secret: string }) {
5141
if (!options || !options.secret) {
@@ -60,31 +50,13 @@ class Webhooks<TTransformed = unknown> {
6050
};
6151

6252
this.sign = sign.bind(null, options.secret);
63-
this.verify = (eventPayload: object | string, signature: string) => {
64-
if (typeof eventPayload === "object") {
65-
console.error(
66-
"[@octokit/webhooks] Passing a JSON payload object to `verify()` is deprecated and the functionality will be removed in a future release of `@octokit/webhooks`"
67-
);
68-
}
69-
return verify(options.secret, eventPayload, signature);
70-
};
53+
this.verify = verify.bind(null, options.secret);
7154
this.on = state.eventHandler.on;
7255
this.onAny = state.eventHandler.onAny;
7356
this.onError = state.eventHandler.onError;
7457
this.removeListener = state.eventHandler.removeListener;
7558
this.receive = state.eventHandler.receive;
76-
this.verifyAndReceive = (
77-
options:
78-
| EmitterWebhookEventWithStringPayloadAndSignature
79-
| EmitterWebhookEventWithSignature
80-
) => {
81-
if (typeof options.payload === "object") {
82-
console.error(
83-
"[@octokit/webhooks] Passing a JSON payload object to `verifyAndReceive()` is deprecated and the functionality will be removed in a future release of `@octokit/webhooks`"
84-
);
85-
}
86-
return verifyAndReceive(state, options);
87-
};
59+
this.verifyAndReceive = verifyAndReceive.bind(null, state);
8860
}
8961
}
9062

src/middleware/node/get-payload.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { WebhookEvent } from "@octokit/webhooks-types";
21
// @ts-ignore to address #245
32
import AggregateError from "aggregate-error";
43

@@ -12,11 +11,11 @@ import AggregateError from "aggregate-error";
1211
// }
1312
type IncomingMessage = any;
1413

15-
export function getPayload(request: IncomingMessage): Promise<WebhookEvent> {
14+
export function getPayload(request: IncomingMessage): Promise<string> {
1615
// If request.body already exists we can stop here
1716
// See https://github.com/octokit/webhooks.js/pull/23
1817

19-
if (request.body) return Promise.resolve(request.body as WebhookEvent);
18+
if (request.body) return Promise.resolve(request.body);
2019

2120
return new Promise((resolve, reject) => {
2221
let data = "";
@@ -28,7 +27,8 @@ export function getPayload(request: IncomingMessage): Promise<WebhookEvent> {
2827
request.on("data", (chunk: string) => (data += chunk));
2928
request.on("end", () => {
3029
try {
31-
resolve(JSON.parse(data));
30+
JSON.parse(data); // check if data is valid JSON
31+
resolve(data);
3232
} catch (error: any) {
3333
error.message = "Invalid JSON";
3434
error.status = 400;

src/middleware/node/middleware.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export async function middleware(
7979
await webhooks.verifyAndReceive({
8080
id: id,
8181
name: eventName as any,
82-
payload: payload as any,
82+
payload,
8383
signature: signatureSHA256,
8484
});
8585
clearTimeout(timeout);

src/types.ts

-4
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,6 @@ export type EmitterWebhookEventWithStringPayloadAndSignature = {
2121
payload: string;
2222
signature: string;
2323
};
24-
/** @deprecated */
25-
export type EmitterWebhookEventWithSignature = EmitterWebhookEvent & {
26-
signature: string;
27-
};
2824

2925
interface BaseWebhookEvent<TName extends WebhookEventName> {
3026
id: string;

src/verify-and-receive.ts

+3-12
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,18 @@
11
import { verify } from "@octokit/webhooks-methods";
22

3-
import { toNormalizedJsonString } from "./to-normalized-json-string";
43
import {
54
EmitterWebhookEventWithStringPayloadAndSignature,
6-
EmitterWebhookEventWithSignature,
75
State,
86
} from "./types";
97

108
export async function verifyAndReceive(
119
state: State & { secret: string },
12-
event:
13-
| EmitterWebhookEventWithStringPayloadAndSignature
14-
| EmitterWebhookEventWithSignature
10+
event: EmitterWebhookEventWithStringPayloadAndSignature
1511
): Promise<any> {
1612
// verify will validate that the secret is not undefined
1713
const matchesSignature = await verify(
1814
state.secret,
19-
typeof event.payload === "object"
20-
? toNormalizedJsonString(event.payload)
21-
: event.payload,
15+
event.payload,
2216
event.signature
2317
);
2418

@@ -35,9 +29,6 @@ export async function verifyAndReceive(
3529
return state.eventHandler.receive({
3630
id: event.id,
3731
name: event.name,
38-
payload:
39-
typeof event.payload === "string"
40-
? JSON.parse(event.payload)
41-
: event.payload,
32+
payload: JSON.parse(event.payload),
4233
});
4334
}

src/verify.ts

-15
This file was deleted.

test/integration/node-middleware.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ describe("createNodeMiddleware(webhooks)", () => {
7575
req.once("data", (chunk) => dataChunks.push(chunk));
7676
req.once("end", () => {
7777
// @ts-expect-error - TS2339: Property 'body' does not exist on type 'IncomingMessage'.
78-
req.body = JSON.parse(Buffer.concat(dataChunks).toString());
78+
req.body = Buffer.concat(dataChunks).toString();
7979
middleware(req, res);
8080
});
8181
}).listen();

test/integration/webhooks.test.ts

+4-7
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,15 @@ describe("Webhooks", () => {
3232
await webhooks.sign(pushEventPayloadString);
3333
});
3434

35-
test("webhooks.verify(payload, signature) with object payload containing special characters", async () => {
35+
test("webhooks.verify(payload, signature) with string payload containing special characters", async () => {
3636
const secret = "mysecret";
3737
const webhooks = new Webhooks({ secret });
3838

39-
const payload = {
39+
const payload = toNormalizedJsonString({
4040
foo: "Foo\n\u001b[34mbar: ♥♥♥♥♥♥♥♥\nthis-is-lost\u001b[0m\u001b[2K",
41-
};
41+
});
4242

43-
await webhooks.verify(
44-
payload,
45-
await sign(secret, toNormalizedJsonString(payload))
46-
);
43+
await webhooks.verify(payload, await sign(secret, payload));
4744
});
4845

4946
test("webhooks.verify(payload, signature) with string payload", async () => {

test/typescript-validate.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ export default async function () {
8383
webhooks.onAny(async ({ id, name, payload }) => {
8484
console.log(name, "event received", id);
8585
const sig = await webhooks.sign(payload);
86-
webhooks.verify(payload, sig);
86+
webhooks.verify(JSON.stringify(payload), sig);
8787
});
8888

8989
webhooks.on("check_run.completed", () => {});

test/unit/deprecation.test.ts

-45
This file was deleted.

0 commit comments

Comments
 (0)