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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255import crypto from "node:crypto";
import { expect } from "@std/expect";import { describe, test } from "@std/testing/bdd";import { Hono } from "hono";
import { TEST_SIGNING_SECRET as SIGNING_SECRET, withSlackConfig,} from "@/libs/test/slack-config";
import type { SlackEnv } from "./config";import { verifySlackSignature } from "./verify";
function signRequest(body: string, timestamp: number): string { const basestring = `v0:${timestamp}:${body}`; const hmac = crypto .createHmac("sha256", SIGNING_SECRET) .update(basestring) .digest("hex"); return `v0=${hmac}`;}
function createTestApp() { const app = withSlackConfig(new Hono<SlackEnv>()); app.post("/test", verifySlackSignature, (c) => { return c.json({ body: c.get("slackBody") }); }); return app;}
describe("verifySlackSignature", () => { const app = createTestApp();
test("accepts valid JSON signature", async () => { const body = JSON.stringify({ type: "event_callback", event: {} }); const timestamp = Math.floor(Date.now() / 1000); const signature = signRequest(body, timestamp);
const res = await app.request("/test", { method: "POST", headers: { "Content-Type": "application/json", "x-slack-request-timestamp": String(timestamp), "x-slack-signature": signature, }, body, });
expect(res.status).toBe(200); const json = (await res.json()) as { body: { type: string } }; expect(json.body.type).toBe("event_callback"); });
test("accepts valid form-urlencoded payload", async () => { const payload = JSON.stringify({ type: "block_actions", user: { id: "U1" }, }); const body = `payload=${encodeURIComponent(payload)}`; const timestamp = Math.floor(Date.now() / 1000); const signature = signRequest(body, timestamp);
const res = await app.request("/test", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", "x-slack-request-timestamp": String(timestamp), "x-slack-signature": signature, }, body, });
expect(res.status).toBe(200); const json = (await res.json()) as { body: { type: string } }; expect(json.body.type).toBe("block_actions"); });
test("rejects missing headers", async () => { const res = await app.request("/test", { method: "POST", headers: { "Content-Type": "application/json" }, body: "{}", });
expect(res.status).toBe(401); });
test("rejects old timestamp", async () => { const body = "{}"; const timestamp = Math.floor(Date.now() / 1000) - 600; const signature = signRequest(body, timestamp);
const res = await app.request("/test", { method: "POST", headers: { "Content-Type": "application/json", "x-slack-request-timestamp": String(timestamp), "x-slack-signature": signature, }, body, });
expect(res.status).toBe(401); });
test("rejects invalid signature", async () => { const body = "{}"; const timestamp = Math.floor(Date.now() / 1000);
const res = await app.request("/test", { method: "POST", headers: { "Content-Type": "application/json", "x-slack-request-timestamp": String(timestamp), "x-slack-signature": "v0=invalidsignature000000000000000000000000000000000000000000000000", }, body, });
expect(res.status).toBe(401); });
test("rejects signature with wrong length", async () => { const body = "{}"; const timestamp = Math.floor(Date.now() / 1000);
const res = await app.request("/test", { method: "POST", headers: { "Content-Type": "application/json", "x-slack-request-timestamp": String(timestamp), "x-slack-signature": "v0=short", }, body, });
expect(res.status).toBe(401); });
test("rejects missing timestamp header only", async () => { const body = "{}"; const res = await app.request("/test", { method: "POST", headers: { "Content-Type": "application/json", "x-slack-signature": "v0=abc", }, body, }); expect(res.status).toBe(401); });
test("rejects missing signature header only", async () => { const body = "{}"; const timestamp = Math.floor(Date.now() / 1000); const res = await app.request("/test", { method: "POST", headers: { "Content-Type": "application/json", "x-slack-request-timestamp": String(timestamp), }, body, }); expect(res.status).toBe(401); });
test("rejects future timestamp beyond 5 minutes", async () => { const body = "{}"; const timestamp = Math.floor(Date.now() / 1000) + 600; const signature = signRequest(body, timestamp);
const res = await app.request("/test", { method: "POST", headers: { "Content-Type": "application/json", "x-slack-request-timestamp": String(timestamp), "x-slack-signature": signature, }, body, }); expect(res.status).toBe(401); });
test("accepts timestamp within 5 minute window", async () => { const body = JSON.stringify({ type: "test" }); const timestamp = Math.floor(Date.now() / 1000) - 200; const signature = signRequest(body, timestamp);
const res = await app.request("/test", { method: "POST", headers: { "Content-Type": "application/json", "x-slack-request-timestamp": String(timestamp), "x-slack-signature": signature, }, body, }); expect(res.status).toBe(200); });
test("rejects signature computed with wrong body", async () => { const body = JSON.stringify({ type: "real_body" }); const timestamp = Math.floor(Date.now() / 1000); const wrongSignature = signRequest("different body", timestamp);
const res = await app.request("/test", { method: "POST", headers: { "Content-Type": "application/json", "x-slack-request-timestamp": String(timestamp), "x-slack-signature": wrongSignature, }, body, }); expect(res.status).toBe(401); });
test("handles form-urlencoded without payload param", async () => { const body = "key=value"; const timestamp = Math.floor(Date.now() / 1000); const signature = signRequest(body, timestamp);
const res = await app.request("/test", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", "x-slack-request-timestamp": String(timestamp), "x-slack-signature": signature, }, body, }); expect(res.status).toBe(200); });
test("handles large JSON payload", async () => { const largePayload = { type: "test", data: "x".repeat(10000) }; const body = JSON.stringify(largePayload); const timestamp = Math.floor(Date.now() / 1000); const signature = signRequest(body, timestamp);
const res = await app.request("/test", { method: "POST", headers: { "Content-Type": "application/json", "x-slack-request-timestamp": String(timestamp), "x-slack-signature": signature, }, body, }); expect(res.status).toBe(200); });});