fix: close SVG sanitization gap on upload routes and fix OCR/extension bugs

Security:
- Apply sanitizeSvg() to all file upload routes (files.ts, user-files.ts)
  preventing SSRF and script injection via SVG uploads to file library

Functional:
- Handle PaddleOCR-VL 1.5 markdown_texts output format in ocr.py
- Add empty-text fallback in OCR tier chain (ocr.ts) so higher tiers
  that return empty text fall back to the next tier automatically
- Fix SVG->PNG filename extension mismatch in tool-factory.ts so
  download endpoint serves correct Content-Type
- Report original upload size (not decoded size) in API response

Test infrastructure:
- Move Playwright auth state from test-results/ to .playwright/ to
  prevent mid-run cleanup deleting auth files
- Fix auth.setup.ts navigation race with waitForURL
- Fix gui-batch.spec.ts regex matching "Presets" instead of "reset"
- Fix pipeline-advanced.spec.ts crop bounds and resize assertions
- Broaden pipeline cleanup to include all E2E-prefixed pipelines
This commit is contained in:
SnapOtter
2026-04-30 16:11:33 +08:00
parent fc8b549d78
commit b00ef20667
13 changed files with 77 additions and 18 deletions
+11 -4
View File
@@ -30,6 +30,7 @@ import {
import { validateImageBuffer } from "../lib/file-validation.js";
import { sanitizeFilename } from "../lib/filename.js";
import { ensureSharpCompat } from "../lib/heic-converter.js";
import { isSvgBuffer, sanitizeSvg } from "../lib/svg-sanitize.js";
import { hasEffectivePermission } from "../permissions.js";
import { getAuthUser, requireAuth } from "../plugins/auth.js";
@@ -185,11 +186,14 @@ export async function userFileRoutes(app: FastifyInstance): Promise<void> {
});
}
// Sanitize SVG uploads to prevent XXE, SSRF, and script injection
const safeBuffer = isSvgBuffer(buffer) ? sanitizeSvg(buffer) : buffer;
const safeName = sanitizeFilename(part.filename ?? "upload");
const mimeType = formatToMime(validation.format);
// Persist to disk
const storedName = await saveFile(buffer, safeName);
const storedName = await saveFile(safeBuffer, safeName);
// Create DB record
const id = randomUUID();
@@ -200,7 +204,7 @@ export async function userFileRoutes(app: FastifyInstance): Promise<void> {
originalName: safeName,
storedName,
mimeType,
size: buffer.length,
size: safeBuffer.length,
width: validation.width,
height: validation.height,
version: 1,
@@ -537,8 +541,11 @@ export async function userFileRoutes(app: FastifyInstance): Promise<void> {
const mimeType = formatToMime(validation.format) || extToMime(ext);
// Sanitize SVG results to prevent XXE, SSRF, and script injection
const safeResultBuffer = isSvgBuffer(fileBuffer) ? sanitizeSvg(fileBuffer) : fileBuffer;
// Persist to disk
const storedName = await saveFile(fileBuffer, resultName);
const storedName = await saveFile(safeResultBuffer, resultName);
// Create DB record
const id = randomUUID();
@@ -549,7 +556,7 @@ export async function userFileRoutes(app: FastifyInstance): Promise<void> {
originalName: resultName,
storedName,
mimeType,
size: fileBuffer.length,
size: safeResultBuffer.length,
width: validation.width,
height: validation.height,
version: nextVersion,