@@ -73,6 +73,14 @@ type IssueCommentPayload = {
7373 }
7474}
7575
76+ type PingPayload = {
77+ zen ?: string
78+ hook_id ?: number
79+ hook ?: {
80+ id ?: number
81+ }
82+ }
83+
7684export async function POST ( request : NextRequest ) {
7785 const event = request . headers . get ( "x-github-event" )
7886 if ( ! event ) {
@@ -105,6 +113,15 @@ export async function POST(request: NextRequest) {
105113 return NextResponse . json ( { error : "Invalid JSON payload" } , { status : 400 } )
106114 }
107115
116+ if ( event === "ping" ) {
117+ const pingPayload = payload as PingPayload
118+ return NextResponse . json ( {
119+ message : "Webhook ping received" ,
120+ zen : pingPayload . zen ?? null ,
121+ hookId : pingPayload . hook_id ?? pingPayload . hook ?. id ?? null ,
122+ } )
123+ }
124+
108125 const baseUrl = getBaseUrl ( request )
109126
110127 if ( event === "installation" ) {
@@ -567,8 +584,8 @@ export async function GET(request: NextRequest) {
567584}
568585
569586function verifyWebhookRequest ( rawPayload : string , signatureHeader : string | null ) {
570- const secret = process . env . GITHUB_WEBHOOK_SECRET
571- if ( ! secret ) {
587+ const configuredSecret = process . env . GITHUB_WEBHOOK_SECRET
588+ if ( ! configuredSecret ) {
572589 if ( process . env . NODE_ENV === "production" ) {
573590 return NextResponse . json (
574591 { error : "GITHUB_WEBHOOK_SECRET is not configured" } ,
@@ -578,6 +595,7 @@ function verifyWebhookRequest(rawPayload: string, signatureHeader: string | null
578595 return null
579596 }
580597
598+ const secret = normalizeWebhookSecret ( configuredSecret )
581599 if ( ! signatureHeader ) {
582600 if ( process . env . NODE_ENV === "production" ) {
583601 return NextResponse . json ( { error : "Missing x-hub-signature-256 header" } , { status : 401 } )
@@ -588,15 +606,32 @@ function verifyWebhookRequest(rawPayload: string, signatureHeader: string | null
588606 const valid = verifyGitHubWebhookSignature ( {
589607 secret,
590608 payload : rawPayload ,
591- signatureHeader,
609+ signatureHeader : signatureHeader . trim ( ) ,
592610 } )
593611 if ( ! valid ) {
594- return NextResponse . json ( { error : "Invalid webhook signature" } , { status : 401 } )
612+ return NextResponse . json (
613+ {
614+ error :
615+ "Invalid webhook signature. Ensure GITHUB_WEBHOOK_SECRET exactly matches the GitHub App webhook secret." ,
616+ } ,
617+ { status : 401 }
618+ )
595619 }
596620
597621 return null
598622}
599623
624+ function normalizeWebhookSecret ( secret : string ) : string {
625+ const trimmed = secret . trim ( )
626+ if (
627+ ( trimmed . startsWith ( '"' ) && trimmed . endsWith ( '"' ) ) ||
628+ ( trimmed . startsWith ( "'" ) && trimmed . endsWith ( "'" ) )
629+ ) {
630+ return trimmed . slice ( 1 , - 1 )
631+ }
632+ return trimmed
633+ }
634+
600635function getBaseUrl ( request : NextRequest ) : string {
601636 const url = new URL ( request . url )
602637 return `${ url . protocol } //${ url . host } `
0 commit comments