diff --git a/server/src/services/routines.ts b/server/src/services/routines.ts index 720b064d..86cc69cb 100644 --- a/server/src/services/routines.ts +++ b/server/src/services/routines.ts @@ -1272,6 +1272,9 @@ export function routineService(db: Db, deps: { heartbeat?: IssueAssignmentWakeup } else if (trigger.signingMode === "github_hmac") { const secretValue = await resolveTriggerSecret(trigger, routine.companyId); const rawBody = input.rawBody ?? Buffer.from(JSON.stringify(input.payload ?? {})); + // Accept X-Hub-Signature-256 (GitHub/Sentry) or fall back to the + // generic X-Paperclip-Signature header so operators can use github_hmac + // mode with either header convention. const providedSignature = (input.hubSignatureHeader ?? input.signatureHeader)?.trim() ?? ""; if (!providedSignature) throw unauthorized(); const expectedHmac = crypto @@ -1279,9 +1282,11 @@ export function routineService(db: Db, deps: { heartbeat?: IssueAssignmentWakeup .update(rawBody) .digest("hex"); const normalizedSignature = providedSignature.replace(/^sha256=/, ""); + const normalizedBuf = Buffer.from(normalizedSignature); + const expectedBuf = Buffer.from(expectedHmac); const valid = - normalizedSignature.length === expectedHmac.length && - crypto.timingSafeEqual(Buffer.from(normalizedSignature), Buffer.from(expectedHmac)); + normalizedBuf.length === expectedBuf.length && + crypto.timingSafeEqual(normalizedBuf, expectedBuf); if (!valid) throw unauthorized(); } else if (trigger.signingMode === "bearer") { const secretValue = await resolveTriggerSecret(trigger, routine.companyId);