diff --git a/src/__tests__/api.test.ts b/src/__tests__/api.test.ts index d173297..0645f00 100644 --- a/src/__tests__/api.test.ts +++ b/src/__tests__/api.test.ts @@ -197,6 +197,28 @@ describe("URL to PDF", () => { expect(data.error).toContain("private"); }); + it("blocks 0.0.0.0 (SSRF protection)", async () => { + const res = await fetch(`${BASE}/v1/convert/url`, { + method: "POST", + headers: { Authorization: "Bearer test-key", "Content-Type": "application/json" }, + body: JSON.stringify({ url: "http://0.0.0.0" }), + }); + expect(res.status).toBe(400); + const data = await res.json(); + expect(data.error).toContain("private"); + }); + + it("returns default filename in Content-Disposition for /convert/html", async () => { + const res = await fetch(`${BASE}/v1/convert/html`, { + method: "POST", + headers: { Authorization: "Bearer test-key", "Content-Type": "application/json" }, + body: JSON.stringify({ html: "
hello
" }), + }); + expect(res.status).toBe(200); + const disposition = res.headers.get("content-disposition"); + expect(disposition).toContain('filename="document.pdf"'); + }); + it("rejects invalid protocol (ftp)", async () => { const res = await fetch(`${BASE}/v1/convert/url`, { method: "POST", diff --git a/src/index.ts b/src/index.ts index 2d8cd94..a0499b3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -422,6 +422,16 @@ async function start() { }; process.on("SIGTERM", () => shutdown("SIGTERM")); process.on("SIGINT", () => shutdown("SIGINT")); + + process.on("uncaughtException", (err) => { + logger.fatal({ err }, "Uncaught exception — shutting down"); + process.exit(1); + }); + + process.on("unhandledRejection", (reason) => { + logger.fatal({ err: reason }, "Unhandled rejection — shutting down"); + process.exit(1); + }); } if (process.env.NODE_ENV !== "test") {