@@ -4,10 +4,15 @@ import { and, eq } from "drizzle-orm"
44import { verifyWebhookSignature } from "@reprojs/integrations-github"
55import { db } from "../../../db"
66import { githubIntegrations , reportEvents , reports } from "../../../db/schema"
7- import { env } from "../../../lib/env"
87import { getWebhookSecret } from "../../../lib/github"
98import { invalidateInstallationRepos } from "../../../lib/github-repo-cache"
109import { parseGithubLabels } from "../../../lib/github-helpers"
10+ import {
11+ checkBodySize ,
12+ MAX_WEBHOOK_BODY_BYTES ,
13+ recordDelivery ,
14+ isKnownInstallation ,
15+ } from "../../../lib/github-webhook-auth"
1116
1217interface IssuesPayload {
1318 action : "opened" | "closed" | "reopened" | "edited" | "labeled" | "unlabeled" | "deleted" | string
@@ -32,17 +37,17 @@ interface InstallationReposPayload {
3237}
3338
3439export default defineEventHandler ( async ( event ) => {
35- const contentLength = Number ( getHeader ( event , "content-length" ) ?? 0 )
36- if ( contentLength > env . GITHUB_WEBHOOK_MAX_BYTES ) {
37- throw createError ( { statusCode : 413 , statusMessage : "Payload too large " } )
40+ const contentLength = Number ( getHeader ( event , "content-length" ) ?? NaN )
41+ if ( ! checkBodySize ( Number . isNaN ( contentLength ) ? undefined : contentLength ) ) {
42+ throw createError ( { statusCode : 413 , statusMessage : "Payload Too Large " } )
3843 }
3944 const raw = await readRawBody ( event )
4045 if ( ! raw || typeof raw !== "string" ) {
4146 throw createError ( { statusCode : 400 , statusMessage : "invalid body" } )
4247 }
4348 // Fallback guard when Content-Length is missing or understated (chunked).
44- if ( Buffer . byteLength ( raw , "utf8" ) > env . GITHUB_WEBHOOK_MAX_BYTES ) {
45- throw createError ( { statusCode : 413 , statusMessage : "Payload too large " } )
49+ if ( Buffer . byteLength ( raw , "utf8" ) > MAX_WEBHOOK_BODY_BYTES ) {
50+ throw createError ( { statusCode : 413 , statusMessage : "Payload Too Large " } )
4651 }
4752 const sig = getHeader ( event , "x-hub-signature-256" )
4853 if (
@@ -56,9 +61,27 @@ export default defineEventHandler(async (event) => {
5661 throw createError ( { statusCode : 401 , statusMessage : "invalid signature" } )
5762 }
5863
64+ const deliveryId = getHeader ( event , "x-github-delivery" )
65+ if ( ! deliveryId ) {
66+ throw createError ( { statusCode : 400 , statusMessage : "Missing X-GitHub-Delivery" } )
67+ }
68+ if ( ( await recordDelivery ( deliveryId ) ) === "replay" ) {
69+ setResponseStatus ( event , 202 )
70+ return { status : "replay" }
71+ }
72+
5973 const kind = getHeader ( event , "x-github-event" )
6074 const payload : unknown = JSON . parse ( raw )
6175
76+ const installationId = ( payload as { installation ?: { id ?: unknown } } ) ?. installation ?. id
77+ if ( typeof installationId === "number" && ! ( await isKnownInstallation ( installationId ) ) ) {
78+ console . warn (
79+ `[github-webhook] unknown installation id: ${ installationId } , delivery=${ deliveryId } ` ,
80+ )
81+ setResponseStatus ( event , 202 )
82+ return { status : "unknown-installation" }
83+ }
84+
6285 if ( kind === "installation" ) {
6386 const p = payload as InstallationPayload
6487 if ( p . action === "deleted" || p . action === "suspend" ) {
0 commit comments