chore(deps): upgrade file-type 16.5.4 -> 22.0.1 (clears the ASF-parser DoS)

file-type 16 -> 22 fixes GHSA-5v7r-6r5c-r473 (infinite loop in the ASF parser on
malformed input). v22 is ESM-only with a strict `exports` map, so the CommonJS
server can no longer `require()` it (and tsc would down-level a normal dynamic
import() back to require()). Converted the single call site in
nas-file-manager.ts to a cached runtime dynamic ESM import via the
Function('return import("file-type")') indirection (the same pattern server.ts
already uses for Vite), and updated the API to the v22 named export
`fileTypeFromBuffer` (was the v16 default `FileType.fromBuffer`).

Verified at runtime that the dynamic import loads v22 from CJS and detects
PNG/JPEG/PDF correctly. `npm audit` no longer lists file-type (6 -> 5 vulns; the
rest are quill [Phase 4] and Prisma 7's dev-tooling @hono/node-server).

Gates: tsc 0 | build 0 | vitest 247/247 | eslint 0 errors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
BOHA
2026-06-09 07:12:23 +02:00
parent 63192dc781
commit 1914fddb4d
3 changed files with 81 additions and 159 deletions

View File

@@ -3,7 +3,25 @@ import path from "path";
import { config } from "../config/env";
import { localDateStr, localTimeStr } from "../utils/date";
const FileType = require("file-type") as typeof import("file-type");
// file-type v22 is ESM-only. The server compiles to CommonJS, where a plain
// `require("file-type")` fails (you can't require an ESM-only package) and tsc
// would down-level a normal dynamic import() back to require(). The Function(...)
// indirection keeps it a genuine runtime ESM import — the same trick server.ts
// uses to load Vite from the CJS bundle. Cached so we import the module once.
type FileTypeModule = {
fileTypeFromBuffer: (
buffer: Uint8Array,
) => Promise<{ ext: string; mime: string } | undefined>;
};
let fileTypeModulePromise: Promise<FileTypeModule> | undefined;
function loadFileType(): Promise<FileTypeModule> {
if (!fileTypeModulePromise) {
fileTypeModulePromise = Function(
'return import("file-type")',
)() as Promise<FileTypeModule>;
}
return fileTypeModulePromise;
}
const BLOCKED_EXTENSIONS = new Set([
"exe",
@@ -378,8 +396,9 @@ export class NasFileManager {
return "Tento typ souboru není povolen";
}
// MIME validation
const typeResult = await FileType.fromBuffer(fileBuffer);
// MIME validation (file-type v22: named `fileTypeFromBuffer` export)
const { fileTypeFromBuffer } = await loadFileType();
const typeResult = await fileTypeFromBuffer(fileBuffer);
if (typeResult) {
if (this.isSuspiciousMime(typeResult.mime)) {
return "Obsah souboru neodpovídá jeho příponě";