const assert = require("assert");
const fs = require("fs");
const os = require("os");
const path = require("path");
const {
cleanupUploadedFiles,
safeDownloadFilename,
validateUploadedFile,
validateUploadedFiles
} = require("../src/services/upload-security");
const sandbox = fs.mkdtempSync(path.join(os.tmpdir(), "lumi-upload-security-"));
function upload(name, mime, bytes) {
const filePath = path.join(sandbox, `${Date.now()}-${Math.random()}`);
fs.writeFileSync(filePath, bytes);
return { path: filePath, originalname: name, mimetype: mime, size: bytes.length };
}
try {
const png = upload("icon.png", "image/png", Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00]));
validateUploadedFile(png, { allowedTypes: ["png"], maxBytes: 1024, requireExtension: true });
assert.equal(png.detectedType, "png");
const disguised = upload("not-really.png", "image/png", Buffer.from("not an image"));
assert.throws(() => validateUploadedFiles([disguised], { allowedTypes: ["png"], maxBytes: 1024 }), /contents/);
assert.equal(fs.existsSync(disguised.path), false);
const mismatch = upload("photo.jpg", "image/jpeg", Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]));
assert.throws(() => validateUploadedFiles([mismatch], { allowedTypes: ["png", "jpeg"] }), /browser|extension/);
assert.equal(fs.existsSync(mismatch.path), false);
const unsafeSvg = upload("icon.svg", "image/svg+xml", Buffer.from(''));
assert.throws(() => validateUploadedFiles([unsafeSvg], { allowedTypes: ["svg"] }), /contents/);
assert.equal(fs.existsSync(unsafeSvg.path), false);
const safeSvg = upload("icon.svg", "image/svg+xml", Buffer.from(''));
validateUploadedFile(safeSvg, { allowedTypes: ["svg"], requireExtension: true });
const oversized = upload("large.txt", "text/plain", Buffer.from("12345"));
assert.throws(() => validateUploadedFiles([oversized], { allowedTypes: ["text"], maxBytes: 4 }), /larger/);
assert.equal(fs.existsSync(oversized.path), false);
assert.equal(safeDownloadFilename('../../bad\r\n"name.txt'), "badname.txt");
assert.equal(safeDownloadFilename("..\\..\\secret.txt"), "secret.txt");
const serverSource = fs.readFileSync(path.join(__dirname, "..", "src", "web", "server.js"), "utf8");
const moderationSource = fs.readFileSync(path.join(__dirname, "..", "plugins", "moderation", "index.js"), "utf8");
const economySource = fs.readFileSync(path.join(__dirname, "..", "plugins", "economy-framework", "index.js"), "utf8");
assert(serverSource.includes('allowedTypes: ["zip"]'));
assert(serverSource.includes('allowedTypes: ["png", "svg"]'));
assert(serverSource.includes('safeDownloadFilename(attachment.original_name'));
assert(serverSource.includes('"X-Content-Type-Options", "nosniff"'));
assert(moderationSource.includes("validateUploadedFiles(req.files"));
assert(moderationSource.indexOf('router.post("/actions", (req, res, next)') < moderationSource.indexOf("evidenceUpload, async"));
assert(economySource.includes("currencyIconUpload"));
assert(economySource.indexOf('router.post("/settings/icon", (req, res, next)') < economySource.indexOf("currencyIconUpload, (req, res)"));
cleanupUploadedFiles([png, safeSvg]);
console.log("Upload security verification passed.");
} finally {
fs.rmSync(sandbox, { recursive: true, force: true });
}