From adfc353b2df400b9fd56c636206b5c5eff967c95 Mon Sep 17 00:00:00 2001 From: Test User Date: Mon, 29 Dec 2025 19:21:56 -0500 Subject: [PATCH] feat: add middleware to enforce JSON Content-Type for API requests - Introduced `requireJsonContentType` middleware to ensure that all POST, PUT, and PATCH requests have the Content-Type set to application/json. - This enhancement improves security by preventing CSRF and content-type confusion attacks, ensuring only properly formatted requests are processed. --- apps/server/src/index.ts | 5 ++ .../middleware/require-json-content-type.ts | 50 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 apps/server/src/middleware/require-json-content-type.ts diff --git a/apps/server/src/index.ts b/apps/server/src/index.ts index 79327a9e..7360cbd7 100644 --- a/apps/server/src/index.ts +++ b/apps/server/src/index.ts @@ -18,6 +18,7 @@ import dotenv from 'dotenv'; import { createEventEmitter, type EventEmitter } from './lib/events.js'; import { initAllowedPaths } from '@automaker/platform'; import { authMiddleware, validateWsConnectionToken, checkRawAuthentication } from './lib/auth.js'; +import { requireJsonContentType } from './middleware/require-json-content-type.js'; import { createAuthRoutes } from './routes/auth/index.js'; import { createFsRoutes } from './routes/fs/index.js'; import { createHealthRoutes, createDetailedHandler } from './routes/health/index.js'; @@ -173,6 +174,10 @@ setInterval(() => { } }, VALIDATION_CLEANUP_INTERVAL_MS); +// Require Content-Type: application/json for all API POST/PUT/PATCH requests +// This helps prevent CSRF and content-type confusion attacks +app.use('/api', requireJsonContentType); + // Mount API routes - health and auth are unauthenticated app.use('/api/health', createHealthRoutes()); app.use('/api/auth', createAuthRoutes()); diff --git a/apps/server/src/middleware/require-json-content-type.ts b/apps/server/src/middleware/require-json-content-type.ts new file mode 100644 index 00000000..ea02f480 --- /dev/null +++ b/apps/server/src/middleware/require-json-content-type.ts @@ -0,0 +1,50 @@ +/** + * Middleware to enforce Content-Type: application/json for request bodies + * + * This security middleware prevents malicious requests by requiring proper + * Content-Type headers for all POST, PUT, and PATCH requests. + * + * Rejecting requests without proper Content-Type helps prevent: + * - CSRF attacks via form submissions (which use application/x-www-form-urlencoded) + * - Content-type confusion attacks + * - Malformed request exploitation + */ + +import type { Request, Response, NextFunction } from 'express'; + +// HTTP methods that typically include request bodies +const METHODS_REQUIRING_JSON = ['POST', 'PUT', 'PATCH']; + +/** + * Middleware that requires Content-Type: application/json for POST/PUT/PATCH requests + * + * Returns 415 Unsupported Media Type if: + * - The request method is POST, PUT, or PATCH + * - AND the Content-Type header is missing or not application/json + * + * Allows requests to pass through if: + * - The request method is GET, DELETE, OPTIONS, HEAD, etc. + * - OR the Content-Type is properly set to application/json (with optional charset) + */ +export function requireJsonContentType(req: Request, res: Response, next: NextFunction): void { + // Skip validation for methods that don't require a body + if (!METHODS_REQUIRING_JSON.includes(req.method)) { + next(); + return; + } + + const contentType = req.headers['content-type']; + + // Check if Content-Type header exists and contains application/json + // Allows for charset parameter: "application/json; charset=utf-8" + if (!contentType || !contentType.toLowerCase().includes('application/json')) { + res.status(415).json({ + success: false, + error: 'Unsupported Media Type', + message: 'Content-Type header must be application/json', + }); + return; + } + + next(); +}