mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-05 09:33:07 +00:00
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.
This commit is contained in:
@@ -18,6 +18,7 @@ import dotenv from 'dotenv';
|
|||||||
import { createEventEmitter, type EventEmitter } from './lib/events.js';
|
import { createEventEmitter, type EventEmitter } from './lib/events.js';
|
||||||
import { initAllowedPaths } from '@automaker/platform';
|
import { initAllowedPaths } from '@automaker/platform';
|
||||||
import { authMiddleware, validateWsConnectionToken, checkRawAuthentication } from './lib/auth.js';
|
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 { createAuthRoutes } from './routes/auth/index.js';
|
||||||
import { createFsRoutes } from './routes/fs/index.js';
|
import { createFsRoutes } from './routes/fs/index.js';
|
||||||
import { createHealthRoutes, createDetailedHandler } from './routes/health/index.js';
|
import { createHealthRoutes, createDetailedHandler } from './routes/health/index.js';
|
||||||
@@ -173,6 +174,10 @@ setInterval(() => {
|
|||||||
}
|
}
|
||||||
}, VALIDATION_CLEANUP_INTERVAL_MS);
|
}, 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
|
// Mount API routes - health and auth are unauthenticated
|
||||||
app.use('/api/health', createHealthRoutes());
|
app.use('/api/health', createHealthRoutes());
|
||||||
app.use('/api/auth', createAuthRoutes());
|
app.use('/api/auth', createAuthRoutes());
|
||||||
|
|||||||
50
apps/server/src/middleware/require-json-content-type.ts
Normal file
50
apps/server/src/middleware/require-json-content-type.ts
Normal file
@@ -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();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user