archetecture security fix
This commit is contained in:
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
import multer from 'multer';
|
||||
/**
|
||||
* Shared multer instance used by all upload endpoints.
|
||||
* Files are kept in memory so services receive a Buffer — no temp-file cleanup needed.
|
||||
*/
|
||||
export declare const imageUpload: multer.Multer;
|
||||
type DetectedFile = {
|
||||
mime: string;
|
||||
ext: string;
|
||||
};
|
||||
export declare function detectImageType(buffer: Buffer): DetectedFile | null;
|
||||
/**
|
||||
* Asserts that a file was provided and is an image by content, not by client claims.
|
||||
*/
|
||||
export declare function assertImageFile(file: Express.Multer.File | undefined, fieldLabel?: string): asserts file is Express.Multer.File;
|
||||
/**
|
||||
* Asserts that at least one file was provided and all files are image content.
|
||||
*/
|
||||
export declare function assertImageFiles(files: Express.Multer.File[], fieldLabel?: string): void;
|
||||
export {};
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/http/upload/index.ts"],"names":[],"mappings":"AACA,OAAO,MAAM,MAAM,QAAQ,CAAA;AAW3B;;;GAGG;AACH,eAAO,MAAM,WAAW,eAGtB,CAAA;AAEF,KAAK,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAA;AAEjD,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAuBnE;AAmBD;;GAEG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,GAAG,SAAS,EACrC,UAAU,SAAS,GAClB,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,CAGrC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,UAAU,SAAW,GAAG,IAAI,CAG1F"}
|
||||
Vendored
+78
@@ -0,0 +1,78 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.imageUpload = void 0;
|
||||
exports.detectImageType = detectImageType;
|
||||
exports.assertImageFile = assertImageFile;
|
||||
exports.assertImageFiles = assertImageFiles;
|
||||
const path_1 = __importDefault(require("path"));
|
||||
const multer_1 = __importDefault(require("multer"));
|
||||
const errors_1 = require("../errors");
|
||||
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 MB
|
||||
const ALLOWED_IMAGE_TYPES = new Map([
|
||||
['image/jpeg', ['.jpg', '.jpeg']],
|
||||
['image/png', ['.png']],
|
||||
['image/webp', ['.webp']],
|
||||
['image/gif', ['.gif']],
|
||||
]);
|
||||
/**
|
||||
* Shared multer instance used by all upload endpoints.
|
||||
* Files are kept in memory so services receive a Buffer — no temp-file cleanup needed.
|
||||
*/
|
||||
exports.imageUpload = (0, multer_1.default)({
|
||||
storage: multer_1.default.memoryStorage(),
|
||||
limits: { fileSize: MAX_FILE_SIZE, files: 20 },
|
||||
});
|
||||
function detectImageType(buffer) {
|
||||
if (buffer.length >= 3 && buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff) {
|
||||
return { mime: 'image/jpeg', ext: '.jpg' };
|
||||
}
|
||||
if (buffer.length >= 8 &&
|
||||
buffer[0] === 0x89 && buffer[1] === 0x50 && buffer[2] === 0x4e && buffer[3] === 0x47 &&
|
||||
buffer[4] === 0x0d && buffer[5] === 0x0a && buffer[6] === 0x1a && buffer[7] === 0x0a) {
|
||||
return { mime: 'image/png', ext: '.png' };
|
||||
}
|
||||
if (buffer.length >= 12 && buffer.subarray(0, 4).toString('ascii') === 'RIFF' && buffer.subarray(8, 12).toString('ascii') === 'WEBP') {
|
||||
return { mime: 'image/webp', ext: '.webp' };
|
||||
}
|
||||
if (buffer.length >= 6) {
|
||||
const sig = buffer.subarray(0, 6).toString('ascii');
|
||||
if (sig === 'GIF87a' || sig === 'GIF89a')
|
||||
return { mime: 'image/gif', ext: '.gif' };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function assertSafeImageContent(file) {
|
||||
const detected = detectImageType(file.buffer);
|
||||
if (!detected || !ALLOWED_IMAGE_TYPES.has(detected.mime)) {
|
||||
throw new errors_1.ValidationError('Unsupported or spoofed image file');
|
||||
}
|
||||
if (file.mimetype !== detected.mime) {
|
||||
throw new errors_1.ValidationError(`MIME type does not match file content for "${file.originalname}"`);
|
||||
}
|
||||
const ext = path_1.default.extname(file.originalname).toLowerCase();
|
||||
const allowedExtensions = ALLOWED_IMAGE_TYPES.get(detected.mime) ?? [];
|
||||
if (ext && !allowedExtensions.includes(ext)) {
|
||||
throw new errors_1.ValidationError(`File extension does not match file content for "${file.originalname}"`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Asserts that a file was provided and is an image by content, not by client claims.
|
||||
*/
|
||||
function assertImageFile(file, fieldLabel = 'file') {
|
||||
if (!file)
|
||||
throw new errors_1.ValidationError(`A ${fieldLabel} is required`);
|
||||
assertSafeImageContent(file);
|
||||
}
|
||||
/**
|
||||
* Asserts that at least one file was provided and all files are image content.
|
||||
*/
|
||||
function assertImageFiles(files, fieldLabel = 'photos') {
|
||||
if (!files || files.length === 0)
|
||||
throw new errors_1.ValidationError(`At least one ${fieldLabel} file is required`);
|
||||
for (const file of files)
|
||||
assertSafeImageContent(file);
|
||||
}
|
||||
//# sourceMappingURL=index.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/http/upload/index.ts"],"names":[],"mappings":"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"}
|
||||
Reference in New Issue
Block a user