Run #115 on main timed out after 30s on the Gitea runner under
concurrent-job load (writing 10001 rows via ExcelJS addRow + writeFile
is CPU-bound and CI contention pushed it past the previous threshold).
Locally these tests complete in ~1s, so doubling the budget removes
the flake without masking real regressions.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- dispo workbook imports are pinned to DISPO_IMPORT_DIR (default ./imports):
tRPC input rejects absolute paths and .. segments, runtime reader
re-validates containment via path.relative. Closes a path-traversal
class that reached ExcelJS CVEs through admin/compromised tokens.
- image validator now checks the full 8-byte PNG magic, enforces PNG IEND
and JPEG EOI trailers, scans the decoded buffer for markup polyglot
markers (<script, <svg, <iframe, javascript:, onerror=, ...), and
explicitly rejects SVG. Provider-generated covers (DALL-E, Gemini) run
through the same validator before persistence — an untrusted upstream
cannot smuggle a stored-XSS payload past us.
- added image-validation.test.ts and tightened documentation.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The 'rejects worksheets that exceed the row limit' test took 6599ms on
the QNAP act_runner, overflowing the default 5000ms vitest timeout.
Writing and parsing MAX_DISPO_WORKBOOK_ROWS+1 rows via ExcelJS is slow
on constrained hardware. Extend timeout for all three writeWorkbook-
dependent tests (row limit, column limit) to 30s, matching the fix
already applied to excel.test.ts and workbook-export.test.ts.