archetecture security fix

This commit is contained in:
root
2026-06-11 03:22:12 -04:00
parent 6def9993da
commit 9483750161
3126 changed files with 177194 additions and 37211 deletions
+3
View File
@@ -0,0 +1,3 @@
import { Request, Response, NextFunction } from 'express';
export declare function errorMiddleware(err: any, req: Request, res: Response, _next: NextFunction): Response<any, Record<string, any>> | undefined;
//# sourceMappingURL=errorMiddleware.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"errorMiddleware.d.ts","sourceRoot":"","sources":["../../../src/http/errors/errorMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAOzD,wBAAgB,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,kDAwCzF"}
+44
View File
@@ -0,0 +1,44 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.errorMiddleware = errorMiddleware;
const index_1 = require("./index");
function withRequestId(req, payload) {
return { ...payload, requestId: req.requestId };
}
function errorMiddleware(err, req, res, _next) {
if (err.name === 'ZodError') {
return res.status(400).json(withRequestId(req, {
error: 'validation_error',
message: 'Invalid request body',
issues: err.issues,
statusCode: 400,
}));
}
if (err.code === 'P2025') {
return res.status(404).json(withRequestId(req, { error: 'not_found', message: 'Resource not found', statusCode: 404 }));
}
if (err.code === 'P2002') {
return res.status(409).json(withRequestId(req, { error: 'conflict', message: 'A resource with this value already exists', statusCode: 409 }));
}
if (err instanceof index_1.AppError) {
if (err.statusCode >= 500)
console.error('[API Error]', { requestId: req.requestId, err });
return res.status(err.statusCode).json(withRequestId(req, {
error: err.error,
message: err.statusCode >= 500 ? 'Internal server error' : err.message,
statusCode: err.statusCode,
...(err.statusCode >= 500 ? {} : err.data),
}));
}
const statusCode = typeof err.statusCode === 'number' ? err.statusCode : 500;
const safeStatusCode = statusCode >= 400 && statusCode < 600 ? statusCode : 500;
if (safeStatusCode >= 500) {
console.error('[API Error]', { requestId: req.requestId, err });
}
res.status(safeStatusCode).json(withRequestId(req, {
error: safeStatusCode >= 500 ? 'internal_error' : (err.code ?? 'request_error'),
message: safeStatusCode >= 500 ? 'Internal server error' : (err.message ?? 'Request failed'),
statusCode: safeStatusCode,
}));
}
//# sourceMappingURL=errorMiddleware.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"errorMiddleware.js","sourceRoot":"","sources":["../../../src/http/errors/errorMiddleware.ts"],"names":[],"mappings":"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"}
+22
View File
@@ -0,0 +1,22 @@
export declare class AppError extends Error {
readonly statusCode: number;
readonly error: string;
readonly data?: Record<string, unknown>;
constructor(message: string, statusCode: number, error: string, data?: Record<string, unknown>);
}
export declare class ValidationError extends AppError {
constructor(message?: string);
}
export declare class NotFoundError extends AppError {
constructor(message?: string);
}
export declare class ConflictError extends AppError {
constructor(message?: string);
}
export declare class ForbiddenError extends AppError {
constructor(message?: string);
}
export declare class UnauthorizedError extends AppError {
constructor(message?: string);
}
//# sourceMappingURL=index.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/http/errors/index.ts"],"names":[],"mappings":"AAAA,qBAAa,QAAS,SAAQ,KAAK;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;gBAE3B,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAO/F;AAED,qBAAa,eAAgB,SAAQ,QAAQ;gBAC/B,OAAO,SAAqB;CAGzC;AAED,qBAAa,aAAc,SAAQ,QAAQ;gBAC7B,OAAO,SAAuB;CAG3C;AAED,qBAAa,aAAc,SAAQ,QAAQ;gBAC7B,OAAO,SAA8C;CAGlE;AAED,qBAAa,cAAe,SAAQ,QAAQ;gBAC9B,OAAO,SAAc;CAGlC;AAED,qBAAa,iBAAkB,SAAQ,QAAQ;gBACjC,OAAO,SAAiB;CAGrC"}
+47
View File
@@ -0,0 +1,47 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.UnauthorizedError = exports.ForbiddenError = exports.ConflictError = exports.NotFoundError = exports.ValidationError = exports.AppError = void 0;
class AppError extends Error {
statusCode;
error;
data;
constructor(message, statusCode, error, data) {
super(message);
this.statusCode = statusCode;
this.error = error;
this.data = data;
this.name = this.constructor.name;
}
}
exports.AppError = AppError;
class ValidationError extends AppError {
constructor(message = 'Validation error') {
super(message, 400, 'validation_error');
}
}
exports.ValidationError = ValidationError;
class NotFoundError extends AppError {
constructor(message = 'Resource not found') {
super(message, 404, 'not_found');
}
}
exports.NotFoundError = NotFoundError;
class ConflictError extends AppError {
constructor(message = 'A resource with this value already exists') {
super(message, 409, 'conflict');
}
}
exports.ConflictError = ConflictError;
class ForbiddenError extends AppError {
constructor(message = 'Forbidden') {
super(message, 403, 'forbidden');
}
}
exports.ForbiddenError = ForbiddenError;
class UnauthorizedError extends AppError {
constructor(message = 'Unauthorized') {
super(message, 401, 'unauthorized');
}
}
exports.UnauthorizedError = UnauthorizedError;
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/http/errors/index.ts"],"names":[],"mappings":";;;AAAA,MAAa,QAAS,SAAQ,KAAK;IACxB,UAAU,CAAQ;IAClB,KAAK,CAAQ;IACb,IAAI,CAA0B;IAEvC,YAAY,OAAe,EAAE,UAAkB,EAAE,KAAa,EAAE,IAA8B;QAC5F,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAC5B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;QAClB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAA;IACnC,CAAC;CACF;AAZD,4BAYC;AAED,MAAa,eAAgB,SAAQ,QAAQ;IAC3C,YAAY,OAAO,GAAG,kBAAkB;QACtC,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,kBAAkB,CAAC,CAAA;IACzC,CAAC;CACF;AAJD,0CAIC;AAED,MAAa,aAAc,SAAQ,QAAQ;IACzC,YAAY,OAAO,GAAG,oBAAoB;QACxC,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,WAAW,CAAC,CAAA;IAClC,CAAC;CACF;AAJD,sCAIC;AAED,MAAa,aAAc,SAAQ,QAAQ;IACzC,YAAY,OAAO,GAAG,2CAA2C;QAC/D,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,UAAU,CAAC,CAAA;IACjC,CAAC;CACF;AAJD,sCAIC;AAED,MAAa,cAAe,SAAQ,QAAQ;IAC1C,YAAY,OAAO,GAAG,WAAW;QAC/B,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,WAAW,CAAC,CAAA;IAClC,CAAC;CACF;AAJD,wCAIC;AAED,MAAa,iBAAkB,SAAQ,QAAQ;IAC7C,YAAY,OAAO,GAAG,cAAc;QAClC,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,cAAc,CAAC,CAAA;IACrC,CAAC;CACF;AAJD,8CAIC"}
+5
View File
@@ -0,0 +1,5 @@
import { Response } from 'express';
export declare function ok<T>(res: Response, data: T): void;
export declare function created<T>(res: Response, data: T): void;
export declare function noContent(res: Response): void;
//# sourceMappingURL=index.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/http/respond/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAElC,wBAAgB,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAElD;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAEvD;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,CAE7C"}
+15
View File
@@ -0,0 +1,15 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ok = ok;
exports.created = created;
exports.noContent = noContent;
function ok(res, data) {
res.json({ data });
}
function created(res, data) {
res.status(201).json({ data });
}
function noContent(res) {
res.status(204).end();
}
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/http/respond/index.ts"],"names":[],"mappings":";;AAEA,gBAEC;AAED,0BAEC;AAED,8BAEC;AAVD,SAAgB,EAAE,CAAI,GAAa,EAAE,IAAO;IAC1C,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAA;AACpB,CAAC;AAED,SAAgB,OAAO,CAAI,GAAa,EAAE,IAAO;IAC/C,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAA;AAChC,CAAC;AAED,SAAgB,SAAS,CAAC,GAAa;IACrC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAA;AACvB,CAAC"}
+21
View File
@@ -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
View File
@@ -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"}
+78
View File
@@ -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
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/http/upload/index.ts"],"names":[],"mappings":"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"}
+6
View File
@@ -0,0 +1,6 @@
import type { Request } from 'express';
import type { ZodTypeAny, output } from 'zod';
export declare function parseBody<TSchema extends ZodTypeAny>(schema: TSchema, req: Request): output<TSchema>;
export declare function parseQuery<TSchema extends ZodTypeAny>(schema: TSchema, req: Request): output<TSchema>;
export declare function parseParams<TSchema extends ZodTypeAny>(schema: TSchema, req: Request): output<TSchema>;
//# sourceMappingURL=index.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/http/validate/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,KAAK,CAAA;AAE7C,wBAAgB,SAAS,CAAC,OAAO,SAAS,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,CAEpG;AAED,wBAAgB,UAAU,CAAC,OAAO,SAAS,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,CAErG;AAED,wBAAgB,WAAW,CAAC,OAAO,SAAS,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,CAEtG"}
+15
View File
@@ -0,0 +1,15 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseBody = parseBody;
exports.parseQuery = parseQuery;
exports.parseParams = parseParams;
function parseBody(schema, req) {
return schema.parse(req.body);
}
function parseQuery(schema, req) {
return schema.parse(req.query);
}
function parseParams(schema, req) {
return schema.parse(req.params);
}
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/http/validate/index.ts"],"names":[],"mappings":";;AAGA,8BAEC;AAED,gCAEC;AAED,kCAEC;AAVD,SAAgB,SAAS,CAA6B,MAAe,EAAE,GAAY;IACjF,OAAO,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;AAC/B,CAAC;AAED,SAAgB,UAAU,CAA6B,MAAe,EAAE,GAAY;IAClF,OAAO,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;AAChC,CAAC;AAED,SAAgB,WAAW,CAA6B,MAAe,EAAE,GAAY;IACnF,OAAO,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;AACjC,CAAC"}
+4
View File
@@ -0,0 +1,4 @@
import type { Request } from 'express';
export declare function getRawBodyString(req: Request): string;
export declare function parseRawJsonBody<T = unknown>(req: Request): T;
//# sourceMappingURL=webhooks.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../src/http/webhooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAGtC,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,UAK5C;AAED,wBAAgB,gBAAgB,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,OAAO,GAAG,CAAC,CAO7D"}
+21
View File
@@ -0,0 +1,21 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getRawBodyString = getRawBodyString;
exports.parseRawJsonBody = parseRawJsonBody;
const errors_1 = require("./errors");
function getRawBodyString(req) {
if (!Buffer.isBuffer(req.body)) {
throw new errors_1.ValidationError('Webhook route must be mounted with express.raw before JSON parsing');
}
return req.body.toString('utf8');
}
function parseRawJsonBody(req) {
const rawBody = getRawBodyString(req);
try {
return JSON.parse(rawBody);
}
catch {
throw new errors_1.ValidationError('Malformed webhook JSON');
}
}
//# sourceMappingURL=webhooks.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../src/http/webhooks.ts"],"names":[],"mappings":";;AAGA,4CAKC;AAED,4CAOC;AAhBD,qCAA0C;AAE1C,SAAgB,gBAAgB,CAAC,GAAY;IAC3C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,wBAAe,CAAC,oEAAoE,CAAC,CAAA;IACjG,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;AAClC,CAAC;AAED,SAAgB,gBAAgB,CAAc,GAAY;IACxD,MAAM,OAAO,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAA;IACrC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAM,CAAA;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,wBAAe,CAAC,wBAAwB,CAAC,CAAA;IACrD,CAAC;AACH,CAAC"}