fix: prevent error message information disclosure + standardize error handling (TDD)
All checks were successful
Build & Deploy to Staging / Build & Deploy to Staging (push) Successful in 13m10s

Security & Consistency Fixes:
- Convert routes no longer leak internal error messages (err.message)
- Templates route no longer exposes error details via 'detail' field
- Admin cleanup endpoint no longer exposes error message
- Standardized QUEUE_FULL response: 429 → 503 (Service Unavailable)
- Added missing PDF_TIMEOUT handling: returns 504 Gateway Timeout
- Generic 500 errors now return 'PDF generation failed.' without internals

TDD Approach:
1. RED: Created error-responses.test.ts with 11 failing tests
2. GREEN: Fixed src/routes/convert.ts, templates.ts, and index.ts
3. Updated convert.test.ts to expect new correct status codes
4. All 541 tests pass

Before: 'PDF generation failed: Puppeteer crashed: SIGSEGV in Chrome'
After:  'PDF generation failed.' (internals logged, not exposed)

Closes security audit findings re: information disclosure
This commit is contained in:
OpenClaw Agent 2026-03-07 17:05:54 +01:00
parent 6b1b3d584e
commit 424a16ed8a
5 changed files with 293 additions and 13 deletions

View file

@ -53,25 +53,25 @@ describe("POST /v1/convert/html", () => {
expect(res.headers["content-type"]).toMatch(/application\/pdf/);
});
it("returns 429 on QUEUE_FULL", async () => {
it("returns 503 on QUEUE_FULL", async () => {
const { renderPdf } = await import("../services/browser.js");
vi.mocked(renderPdf).mockRejectedValue(new Error("QUEUE_FULL"));
const res = await request(app)
.post("/v1/convert/html")
.set("content-type", "application/json")
.send({ html: "<h1>Hello</h1>" });
expect(res.status).toBe(429);
expect(res.status).toBe(503);
});
it("returns 500 on PDF_TIMEOUT", async () => {
it("returns 504 on PDF_TIMEOUT", async () => {
const { renderPdf } = await import("../services/browser.js");
vi.mocked(renderPdf).mockRejectedValue(new Error("PDF_TIMEOUT"));
const res = await request(app)
.post("/v1/convert/html")
.set("content-type", "application/json")
.send({ html: "<h1>Hello</h1>" });
expect(res.status).toBe(500);
expect(res.body.error).toMatch(/PDF_TIMEOUT/);
expect(res.status).toBe(504);
expect(res.body.error).toBe("PDF generation timed out.");
});
it("wraps fragments (no <html tag) with wrapHtml", async () => {