Skip to content

Commit 8d7a936

Browse files
authored
Merge branch 'master' into use-full-relative-paths
2 parents 216efe4 + 7d6a81d commit 8d7a936

12 files changed

+1277
-1709
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ webhooks.verify(eventPayload, signature);
189189
eventPayload
190190
</code>
191191
<em>
192-
(Object)
192+
(Object or String)
193193
</em>
194194
</td>
195195
<td>
@@ -260,7 +260,7 @@ webhooks.verifyAndReceive({ id, name, payload, signature });
260260
payload
261261
</code>
262262
<em>
263-
Object
263+
Object or String
264264
</em>
265265
</td>
266266
<td>

package-lock.json

+1,103-1,680
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+6-6
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,23 @@
1010
"build": "pika build",
1111
"coverage": "jest --coverage && open coverage/lcov-report/index.html",
1212
"generate-types": "ts-node --transpile-only scripts/generate-types.ts",
13-
"lint": "prettier --check 'src/**/*.{ts,json}' 'scripts/**/*' 'test/**/*' README.md package.json",
14-
"lint:fix": "prettier --write 'src/**/*.{ts,json}' 'scripts/**/*' 'test/**/*' README.md package.json",
13+
"lint": "prettier --check 'src/**/*.{ts,json}' 'scripts/**/*' 'test/**/*.ts' README.md package.json",
14+
"lint:fix": "prettier --write 'src/**/*.{ts,json}' 'scripts/**/*' 'test/**/*.ts' README.md package.json",
1515
"pretest": "npm run -s lint",
1616
"test": "jest --coverage",
1717
"validate:ts": "tsc --noEmit --noImplicitAny --target es2020 --esModuleInterop --moduleResolution node test/typescript-validate.ts"
1818
},
1919
"prettier": {},
2020
"dependencies": {
2121
"@octokit/request-error": "^2.0.2",
22-
"@octokit/webhooks-methods": "^1.0.0",
23-
"@octokit/webhooks-types": "3.75.2",
22+
"@octokit/webhooks-methods": "^2.0.0",
23+
"@octokit/webhooks-types": "4.0.1",
2424
"aggregate-error": "^3.1.0"
2525
},
2626
"devDependencies": {
2727
"@jest/types": "^27.0.0",
2828
"@octokit/tsconfig": "^1.0.1",
29-
"@octokit/webhooks-schemas": "3.75.2",
29+
"@octokit/webhooks-schemas": "4.0.1",
3030
"@pika/pack": "^0.5.0",
3131
"@pika/plugin-build-node": "^0.9.2",
3232
"@pika/plugin-build-web": "^0.9.2",
@@ -41,7 +41,7 @@
4141
"get-port": "^5.0.0",
4242
"jest": "^27.0.0",
4343
"node-fetch": "^2.6.1",
44-
"prettier": "2.3.0",
44+
"prettier": "2.3.1",
4545
"prettier-plugin-packagejson": "^2.2.9",
4646
"semantic-release": "^17.0.0",
4747
"ts-jest": "^27.0.0",

src/index.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { sign, verify } from "@octokit/webhooks-methods";
2-
31
import { createLogger } from "./createLogger.js";
42
import { createEventHandler } from "./event-handler/index.js";
3+
import { sign } from "./sign.js";
4+
import { verify } from "./verify.js";
55
import { verifyAndReceive } from "./verify-and-receive.js";
66
import {
77
EmitterWebhookEvent,
@@ -11,6 +11,8 @@ import {
1111
State,
1212
WebhookError,
1313
WebhookEventHandlerError,
14+
EmitterWebhookEventWithStringPayloadAndSignature,
15+
EmitterWebhookEventWithSignature,
1416
} from "./types";
1517

1618
export { createNodeMiddleware } from "./middleware/node/index.js";
@@ -34,7 +36,9 @@ class Webhooks<TTransformed = unknown> {
3436
) => void;
3537
public receive: (event: EmitterWebhookEvent) => Promise<void>;
3638
public verifyAndReceive: (
37-
options: EmitterWebhookEvent & { signature: string }
39+
options:
40+
| EmitterWebhookEventWithStringPayloadAndSignature
41+
| EmitterWebhookEventWithSignature
3842
) => Promise<void>;
3943

4044
constructor(options: Options<TTransformed> & { secret: string }) {

src/sign.ts

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { sign as signMethod } from "@octokit/webhooks-methods";
2+
3+
import { toNormalizedJsonString } from "./to-normalized-json-string";
4+
5+
export async function sign(
6+
secret: string,
7+
payload: string | object
8+
): Promise<any> {
9+
return signMethod(
10+
secret,
11+
typeof payload === "string" ? payload : toNormalizedJsonString(payload)
12+
);
13+
}

src/to-normalized-json-string.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* GitHub sends its JSON with an indentation of 2 spaces and a line break at the end
3+
*/
4+
export function toNormalizedJsonString(payload: object) {
5+
const payloadString = JSON.stringify(payload, null, 2) + "\n";
6+
return payloadString.replace(/[^\\]\\u[\da-f]{4}/g, (s) => {
7+
return s.substr(0, 3) + s.substr(3).toUpperCase();
8+
});
9+
}

src/types.ts

+11
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,17 @@ export type EmitterWebhookEvent<
1515
}
1616
: BaseWebhookEvent<Extract<TEmitterEvent, WebhookEventName>>;
1717

18+
export type EmitterWebhookEventWithStringPayloadAndSignature = {
19+
id: string;
20+
name: EmitterWebhookEventName;
21+
payload: string;
22+
signature: string;
23+
};
24+
25+
export type EmitterWebhookEventWithSignature = EmitterWebhookEvent & {
26+
signature: string;
27+
};
28+
1829
interface BaseWebhookEvent<TName extends WebhookEventName> {
1930
id: string;
2031
name: TName;

src/verify-and-receive.ts

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

3-
import { EmitterWebhookEvent, State } from "./types";
3+
import { toNormalizedJsonString } from "./to-normalized-json-string";
4+
import {
5+
EmitterWebhookEventWithStringPayloadAndSignature,
6+
EmitterWebhookEventWithSignature,
7+
State,
8+
} from "./types";
49

510
export async function verifyAndReceive(
611
state: State & { secret: string },
7-
event: EmitterWebhookEvent & { signature: string }
12+
event:
13+
| EmitterWebhookEventWithStringPayloadAndSignature
14+
| EmitterWebhookEventWithSignature
815
): Promise<any> {
916
// verify will validate that the secret is not undefined
1017
const matchesSignature = await verify(
1118
state.secret,
12-
event.payload,
19+
typeof event.payload === "object"
20+
? toNormalizedJsonString(event.payload)
21+
: event.payload,
1322
event.signature
1423
);
1524

@@ -26,6 +35,9 @@ export async function verifyAndReceive(
2635
return state.eventHandler.receive({
2736
id: event.id,
2837
name: event.name,
29-
payload: event.payload,
38+
payload:
39+
typeof event.payload === "string"
40+
? JSON.parse(event.payload)
41+
: event.payload,
3042
});
3143
}

src/verify.ts

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { verify as verifyMethod } from "@octokit/webhooks-methods";
2+
3+
import { toNormalizedJsonString } from "./to-normalized-json-string";
4+
5+
export async function verify(
6+
secret: string,
7+
payload: string | object,
8+
signature: string
9+
): Promise<any> {
10+
return verifyMethod(
11+
secret,
12+
typeof payload === "string" ? payload : toNormalizedJsonString(payload),
13+
signature
14+
);
15+
}

test/fixtures/push-payload.json

+6-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@
2727
},
2828
"added": [],
2929
"removed": [],
30-
"modified": ["README.md"]
30+
"modified": [
31+
"README.md"
32+
]
3133
}
3234
],
3335
"head_commit": {
@@ -49,7 +51,9 @@
4951
},
5052
"added": [],
5153
"removed": [],
52-
"modified": ["README.md"]
54+
"modified": [
55+
"README.md"
56+
]
5357
},
5458
"repository": {
5559
"id": 35129377,

test/integration/node-middleware.test.ts

+16-12
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { createServer } from "http";
2+
import { readFileSync } from "fs";
23

34
import fetch from "node-fetch";
45
import { sign } from "@octokit/webhooks-methods";
@@ -7,15 +8,18 @@ import { sign } from "@octokit/webhooks-methods";
78
const express = require("express");
89

910
import { Webhooks, createNodeMiddleware } from "../../src/index.js";
10-
import { pushEventPayload } from "../fixtures/index.js";
1111

12+
const pushEventPayload = readFileSync(
13+
"test/fixtures/push-payload.json",
14+
"utf-8"
15+
);
1216
let signatureSha256: string;
1317

1418
describe("createNodeMiddleware(webhooks)", () => {
1519
beforeAll(async () => {
1620
signatureSha256 = await sign(
1721
{ secret: "mySecret", algorithm: "sha256" },
18-
JSON.stringify(pushEventPayload)
22+
pushEventPayload
1923
);
2024
});
2125

@@ -48,7 +52,7 @@ describe("createNodeMiddleware(webhooks)", () => {
4852
"X-GitHub-Event": "push",
4953
"X-Hub-Signature-256": signatureSha256,
5054
},
51-
body: JSON.stringify(pushEventPayload),
55+
body: pushEventPayload,
5256
}
5357
);
5458

@@ -92,7 +96,7 @@ describe("createNodeMiddleware(webhooks)", () => {
9296
"X-GitHub-Event": "push",
9397
"X-Hub-Signature-256": signatureSha256,
9498
},
95-
body: JSON.stringify(pushEventPayload),
99+
body: pushEventPayload,
96100
}
97101
);
98102

@@ -256,7 +260,7 @@ describe("createNodeMiddleware(webhooks)", () => {
256260
"X-GitHub-Event": "push",
257261
"X-Hub-Signature-256": signatureSha256,
258262
},
259-
body: JSON.stringify(pushEventPayload),
263+
body: pushEventPayload,
260264
}
261265
);
262266

@@ -292,7 +296,7 @@ describe("createNodeMiddleware(webhooks)", () => {
292296
"X-GitHub-Event": "push",
293297
"X-Hub-Signature-256": signatureSha256,
294298
},
295-
body: JSON.stringify(pushEventPayload),
299+
body: pushEventPayload,
296300
}
297301
);
298302

@@ -327,7 +331,7 @@ describe("createNodeMiddleware(webhooks)", () => {
327331
"X-GitHub-Event": "push",
328332
"X-Hub-Signature-256": signatureSha256,
329333
},
330-
body: JSON.stringify(pushEventPayload),
334+
body: pushEventPayload,
331335
}
332336
);
333337

@@ -352,7 +356,7 @@ describe("createNodeMiddleware(webhooks)", () => {
352356

353357
const response = await fetch(`http://localhost:${port}/test`, {
354358
method: "POST",
355-
body: JSON.stringify(pushEventPayload),
359+
body: pushEventPayload,
356360
});
357361

358362
await expect(response.text()).resolves.toBe("Dafuq");
@@ -376,15 +380,15 @@ describe("createNodeMiddleware(webhooks)", () => {
376380

377381
const response = await fetch(`http://localhost:${port}/test`, {
378382
method: "POST",
379-
body: JSON.stringify(pushEventPayload),
383+
body: pushEventPayload,
380384
});
381385

382386
await expect(response.text()).resolves.toContain("Cannot POST /test");
383387
expect(response.status).toEqual(404);
384388

385389
const responseForFoo = await fetch(`http://localhost:${port}/foo`, {
386390
method: "POST",
387-
body: JSON.stringify(pushEventPayload),
391+
body: pushEventPayload,
388392
});
389393

390394
await expect(responseForFoo.text()).resolves.toContain("ok\n");
@@ -415,7 +419,7 @@ describe("createNodeMiddleware(webhooks)", () => {
415419
"X-GitHub-Event": "push",
416420
"X-Hub-Signature-256": signatureSha256,
417421
},
418-
body: JSON.stringify(pushEventPayload),
422+
body: pushEventPayload,
419423
});
420424

421425
await expect(response.text()).resolves.toBe("ok\n");
@@ -446,7 +450,7 @@ describe("createNodeMiddleware(webhooks)", () => {
446450
"X-GitHub-Event": "push",
447451
"X-Hub-Signature-256": signatureSha256,
448452
},
449-
body: JSON.stringify(pushEventPayload),
453+
body: pushEventPayload,
450454
});
451455

452456
await expect(response.text()).resolves.toBe("ok\n");

0 commit comments

Comments
 (0)