refactor(security): harden temp-path handling for inbound media
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import crypto from "node:crypto";
|
||||
import { mkdtemp, rm } from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
@@ -20,6 +21,12 @@ function sanitizeExtension(extension?: string): string {
|
||||
return `.${token}`;
|
||||
}
|
||||
|
||||
function sanitizeFileName(fileName: string): string {
|
||||
const base = path.basename(fileName).replace(/[^a-zA-Z0-9._-]+/g, "-");
|
||||
const normalized = base.replace(/^-+|-+$/g, "");
|
||||
return normalized || "download.bin";
|
||||
}
|
||||
|
||||
export function buildRandomTempFilePath(params: {
|
||||
prefix: string;
|
||||
extension?: string;
|
||||
@@ -37,3 +44,22 @@ export function buildRandomTempFilePath(params: {
|
||||
const uuid = params.uuid?.trim() || crypto.randomUUID();
|
||||
return path.join(params.tmpDir ?? os.tmpdir(), `${prefix}-${now}-${uuid}${extension}`);
|
||||
}
|
||||
|
||||
export async function withTempDownloadPath<T>(
|
||||
params: {
|
||||
prefix: string;
|
||||
fileName?: string;
|
||||
tmpDir?: string;
|
||||
},
|
||||
fn: (tmpPath: string) => Promise<T>,
|
||||
): Promise<T> {
|
||||
const tempRoot = params.tmpDir ?? os.tmpdir();
|
||||
const prefix = `${sanitizePrefix(params.prefix)}-`;
|
||||
const dir = await mkdtemp(path.join(tempRoot, prefix));
|
||||
const tmpPath = path.join(dir, sanitizeFileName(params.fileName ?? "download.bin"));
|
||||
try {
|
||||
return await fn(tmpPath);
|
||||
} finally {
|
||||
await rm(dir, { recursive: true, force: true }).catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user