Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/openstatusHQ/openstatus. ๐ซ Status page with uptime monitoring & API monitoring as code ๐ซ openstatus.dev
bun drizzle-orm monitoring monitoring-as-code nextjs observability on-call open-source shadcn-ui status-page statuspage synthetic-monitoring tinybird turso uptime uptime-checker uptime-monitor
Something went wrong. Try again.
TypeScript
at incident-api
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697import { getLogger } from "@logtape/logtape";import { createMiddleware } from "hono/factory";
import type { SlackEnv } from "./config";
const logger = getLogger("api-server");
// Slack retries on a 401 and then disables the subscription, so a secret that// doesn't match the app looks like "the bot never answers" โ make it loud.function logInvalidSignature(path: string) { logger.warn( "slack request rejected: invalid signature โ does SLACK_SIGNING_SECRET belong to this Slack app?", { path }, );}
export const verifySlackSignature = createMiddleware<SlackEnv>( async (c, next) => { const signingSecret = c.get("slackConfig")?.signingSecret;
if (!signingSecret) { logger.error("slack request rejected: signing secret not configured", { path: c.req.path, }); return c.json({ error: "Slack not configured" }, 503); }
const timestamp = c.req.header("x-slack-request-timestamp"); const signature = c.req.header("x-slack-signature");
if (!timestamp || !signature) { return c.json({ error: "Missing Slack headers" }, 401); }
const now = Math.floor(Date.now() / 1000); if (Math.abs(now - Number(timestamp)) > 300) { logger.warn("slack request rejected: stale timestamp", { path: c.req.path, skewSeconds: now - Number(timestamp), }); return c.json({ error: "Request too old" }, 401); }
const rawBody = await c.req.text();
const encoder = new TextEncoder(); const basestring = `v0:${timestamp}:${rawBody}`; const key = await crypto.subtle.importKey( "raw", encoder.encode(signingSecret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"], ); const sig = await crypto.subtle.sign( "HMAC", key, encoder.encode(basestring), ); const computed = `v0=${Array.from(new Uint8Array(sig)) .map((b) => b.toString(16).padStart(2, "0")) .join("")}`;
if (computed.length !== signature.length) { logInvalidSignature(c.req.path); return c.json({ error: "Invalid signature" }, 401); }
const a = encoder.encode(computed); const b = encoder.encode(signature); let mismatch = 0; for (let i = 0; i < a.length; i++) { mismatch |= a[i] ^ b[i]; } if (mismatch !== 0) { logInvalidSignature(c.req.path); return c.json({ error: "Invalid signature" }, 401); }
const contentType = c.req.header("content-type") ?? ""; if (contentType.includes("application/json")) { c.set("slackBody", JSON.parse(rawBody)); } else if (contentType.includes("application/x-www-form-urlencoded")) { const params = new URLSearchParams(rawBody); const payload = params.get("payload"); // Interactions arrive as a `payload` field; slash commands arrive as the // flat form fields themselves. c.set( "slackBody", payload ? JSON.parse(payload) : Object.fromEntries(params), ); }
await next(); },);