Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
ef6fa1d
Generated with Hive: Harden GitHub App webhook HMAC verification with…
fayekelmith Jul 17, 2026
a7b00f8
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 17, 2026
1cfc02c
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 17, 2026
f936b10
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 17, 2026
16602e1
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 17, 2026
fb1315e
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 18, 2026
77dddd2
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 18, 2026
2b30e81
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 18, 2026
8a9a2c7
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 18, 2026
8d1d31f
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 19, 2026
e5bdc1e
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 19, 2026
8d7fd61
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 20, 2026
9698e2a
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 20, 2026
79e7ff3
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 20, 2026
cdbd6f0
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 20, 2026
9fd6c88
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 20, 2026
25bcb22
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 20, 2026
ca1eb58
Generated with Hive: Fix missing DIRECT_URL env for unit tests in CI …
fayekelmith Jul 20, 2026
4e6a83a
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 20, 2026
d348f25
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 20, 2026
9dae637
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 21, 2026
ba36991
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 21, 2026
d00995e
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 21, 2026
f09edd9
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 21, 2026
b9964cf
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 21, 2026
9fb1e4e
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 21, 2026
4d77006
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 21, 2026
302af98
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 22, 2026
b18b225
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 22, 2026
5151214
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 22, 2026
3dceba2
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 22, 2026
47483e2
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 23, 2026
78df151
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 23, 2026
2c89ed4
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 23, 2026
f08f54f
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 23, 2026
b596a94
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 23, 2026
14033e8
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 23, 2026
c921546
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 23, 2026
bd6d8d8
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 23, 2026
5c8dacf
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 23, 2026
8d88b62
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 23, 2026
d8b2335
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 24, 2026
f54a8ba
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 24, 2026
a7d50d9
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 24, 2026
4615594
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 24, 2026
6c5071e
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 24, 2026
344b507
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 24, 2026
09f5c2a
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 24, 2026
4c4e2d5
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 24, 2026
e2d58bd
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 24, 2026
b819102
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 26, 2026
1696a6d
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 26, 2026
f0a8bae
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 27, 2026
f1fd745
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 27, 2026
4afc16d
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 28, 2026
eafa1e9
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 28, 2026
e55d5a7
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 29, 2026
d582314
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 29, 2026
84346af
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 29, 2026
c3f392b
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 29, 2026
2522f9b
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 29, 2026
d0c10c4
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 29, 2026
00aabf5
Merge branch 'master' into bugfix/CMROJEMQ8-harden-github-app-webhook…
tomsmith8 Jul 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ jobs:
NEXTAUTH_SECRET: test-secret
JWT_SECRET: test-jwt-secret
DATABASE_URL: postgresql://localhost:5432/hive_test_unit
DIRECT_URL: postgresql://localhost:5432/hive_test_unit
run: npm run test:unit

integration-tests:
Expand Down
14 changes: 12 additions & 2 deletions src/__tests__/integration/api/github-app-webhook.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { describe, test, expect, beforeEach, afterEach, vi } from "vitest";
import { POST } from "@/app/api/github/app/webhook/route";
import * as encryptionModule from "@/lib/encryption";
import { db } from "@/lib/db";
import { NextRequest } from "next/server";
import {
Expand Down Expand Up @@ -137,6 +138,8 @@ describe("GitHub App Webhook - POST /api/github/app/webhook", () => {
const body = JSON.stringify(payload);
const validSignature = computeValidWebhookSignature(mockWebhookSecret, body);

const timingSafeSpy = vi.spyOn(encryptionModule, "timingSafeEqual");

const request = createGitHubAppWebhookRequest(
webhookUrl,
payload,
Expand All @@ -146,6 +149,13 @@ describe("GitHub App Webhook - POST /api/github/app/webhook", () => {
const response = await POST(request as NextRequest);

expect(response.status).toBe(200);
expect(timingSafeSpy).toHaveBeenCalledOnce();
// First arg is computed digest, second is the incoming signature header
const [calledDigest, calledSignature] = timingSafeSpy.mock.calls[0];
expect(calledDigest).toMatch(/^sha256=[0-9a-f]{64}$/);
expect(calledSignature).toBe(validSignature);

timingSafeSpy.mockRestore();
});
});

Expand Down Expand Up @@ -520,7 +530,7 @@ describe("GitHub App Webhook - POST /api/github/app/webhook", () => {
db.user.findUnique = originalFindUnique;
});

test.skip("should handle missing GITHUB_WEBHOOK_SECRET environment variable", async () => {
test("should handle missing GITHUB_WEBHOOK_SECRET environment variable", async () => {
// Remove env var
delete process.env.GITHUB_WEBHOOK_SECRET;

Expand All @@ -536,7 +546,7 @@ describe("GitHub App Webhook - POST /api/github/app/webhook", () => {

const response = await POST(request as NextRequest);

// Should fail without secret
// Should return controlled 500, not throw an unhandled exception
expect(response.status).toBe(500);

// Restore env var
Expand Down
18 changes: 9 additions & 9 deletions src/app/api/github/app/webhook/route.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import crypto from "crypto";
import { NextRequest, NextResponse } from "next/server";
import { timingSafeEqual } from "@/lib/encryption";

export async function POST(req: NextRequest) {
const secret = process.env.GITHUB_WEBHOOK_SECRET;
if (!secret) {
return new Response("Webhook secret not configured", { status: 500 });
}

console.log("🔴 Github app webhook received");

console.log("🔴 Github app webhook headers", req.headers);

console.log("🔴 Github app webhook body", req);

const secret = process.env.GITHUB_WEBHOOK_SECRET!;
const signature = req.headers.get("x-hub-signature-256") || "";
const body = await req.text();

// Verify signature
const hmac = crypto.createHmac("sha256", secret);
const digest = `sha256=${hmac.update(body).digest("hex")}`;

if (signature !== digest) {
if (!timingSafeEqual(digest, signature)) {
return NextResponse.json({ message: "Invalid signature" }, { status: 401 });
}

console.log("🔴 Github app webhook received");

const event = req.headers.get("x-github-event");
const payload = JSON.parse(body);

Expand All @@ -30,4 +30,4 @@ export async function POST(req: NextRequest) {
}

return NextResponse.json({ success: true });
}
}
Loading