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
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:
parent
6b1b3d584e
commit
424a16ed8a
5 changed files with 293 additions and 13 deletions
|
|
@ -125,10 +125,14 @@ convertRouter.post("/html", async (req: Request & { acquirePdfSlot?: () => Promi
|
|||
} catch (err: any) {
|
||||
logger.error({ err }, "Convert HTML error");
|
||||
if (err.message === "QUEUE_FULL") {
|
||||
res.status(429).json({ error: "Server busy - too many concurrent PDF generations. Please try again in a few seconds." });
|
||||
res.status(503).json({ error: "Server busy — too many concurrent PDF generations. Please try again in a few seconds." });
|
||||
return;
|
||||
}
|
||||
res.status(500).json({ error: `PDF generation failed: ${err.message}` });
|
||||
if (err.message === "PDF_TIMEOUT") {
|
||||
res.status(504).json({ error: "PDF generation timed out." });
|
||||
return;
|
||||
}
|
||||
res.status(500).json({ error: "PDF generation failed." });
|
||||
} finally {
|
||||
if (slotAcquired && req.releasePdfSlot) {
|
||||
req.releasePdfSlot();
|
||||
|
|
@ -227,10 +231,14 @@ convertRouter.post("/markdown", async (req: Request & { acquirePdfSlot?: () => P
|
|||
} catch (err: any) {
|
||||
logger.error({ err }, "Convert MD error");
|
||||
if (err.message === "QUEUE_FULL") {
|
||||
res.status(429).json({ error: "Server busy - too many concurrent PDF generations. Please try again in a few seconds." });
|
||||
res.status(503).json({ error: "Server busy — too many concurrent PDF generations. Please try again in a few seconds." });
|
||||
return;
|
||||
}
|
||||
res.status(500).json({ error: `PDF generation failed: ${err.message}` });
|
||||
if (err.message === "PDF_TIMEOUT") {
|
||||
res.status(504).json({ error: "PDF generation timed out." });
|
||||
return;
|
||||
}
|
||||
res.status(500).json({ error: "PDF generation failed." });
|
||||
} finally {
|
||||
if (slotAcquired && req.releasePdfSlot) {
|
||||
req.releasePdfSlot();
|
||||
|
|
@ -360,10 +368,14 @@ convertRouter.post("/url", async (req: Request & { acquirePdfSlot?: () => Promis
|
|||
} catch (err: any) {
|
||||
logger.error({ err }, "Convert URL error");
|
||||
if (err.message === "QUEUE_FULL") {
|
||||
res.status(429).json({ error: "Server busy - too many concurrent PDF generations. Please try again in a few seconds." });
|
||||
res.status(503).json({ error: "Server busy — too many concurrent PDF generations. Please try again in a few seconds." });
|
||||
return;
|
||||
}
|
||||
res.status(500).json({ error: `PDF generation failed: ${err.message}` });
|
||||
if (err.message === "PDF_TIMEOUT") {
|
||||
res.status(504).json({ error: "PDF generation timed out." });
|
||||
return;
|
||||
}
|
||||
res.status(500).json({ error: "PDF generation failed." });
|
||||
} finally {
|
||||
if (slotAcquired && req.releasePdfSlot) {
|
||||
req.releasePdfSlot();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue