- Created .gitignore to exclude sensitive files and directories. - Added API documentation in API_DOCUMENTATION.md. - Included deployment instructions in DEPLOYMENT.md. - Established project structure documentation in PROJECT_STRUCTURE.md. - Updated README.md with project status and team information. - Added recommendations and status tracking documents. - Introduced testing guidelines in TESTING.md. - Set up CI workflow in .github/workflows/ci.yml. - Created Dockerfile for backend and frontend setups. - Added various service and utility files for backend functionality. - Implemented frontend components and pages for user interface. - Included mobile app structure and services. - Established scripts for deployment across multiple chains.
120 lines
3.0 KiB
TypeScript
120 lines
3.0 KiB
TypeScript
import { Request, Response, NextFunction } from 'express';
|
|
import helmet from 'helmet';
|
|
import rateLimit from 'express-rate-limit';
|
|
|
|
/**
|
|
* Security middleware configurations
|
|
*/
|
|
|
|
// Rate limiting configurations
|
|
export const createRateLimiter = (windowMs: number, max: number) => {
|
|
return rateLimit({
|
|
windowMs,
|
|
max,
|
|
message: 'Too many requests from this IP, please try again later.',
|
|
standardHeaders: true,
|
|
legacyHeaders: false,
|
|
});
|
|
};
|
|
|
|
// Specific rate limiters
|
|
export const authRateLimiter = createRateLimiter(15 * 60 * 1000, 5); // 5 requests per 15 minutes
|
|
export const apiRateLimiter = createRateLimiter(60 * 1000, 100); // 100 requests per minute
|
|
export const strictRateLimiter = createRateLimiter(60 * 1000, 10); // 10 requests per minute
|
|
|
|
/**
|
|
* Enhanced security headers
|
|
*/
|
|
export const securityHeaders = helmet({
|
|
contentSecurityPolicy: {
|
|
directives: {
|
|
defaultSrc: ["'self'"],
|
|
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
scriptSrc: ["'self'"],
|
|
imgSrc: ["'self'", 'data:', 'https:'],
|
|
connectSrc: ["'self'"],
|
|
fontSrc: ["'self'"],
|
|
objectSrc: ["'none'"],
|
|
mediaSrc: ["'self'"],
|
|
frameSrc: ["'none'"],
|
|
},
|
|
},
|
|
hsts: {
|
|
maxAge: 31536000,
|
|
includeSubDomains: true,
|
|
preload: true,
|
|
},
|
|
frameguard: {
|
|
action: 'deny',
|
|
},
|
|
noSniff: true,
|
|
xssFilter: true,
|
|
});
|
|
|
|
/**
|
|
* CORS configuration based on environment
|
|
*/
|
|
export const corsConfig = {
|
|
origin: (origin: string | undefined, callback: (err: Error | null, allow?: boolean) => void) => {
|
|
const allowedOrigins = process.env.CORS_ORIGINS?.split(',') || [process.env.CORS_ORIGIN || '*'];
|
|
|
|
if (process.env.NODE_ENV === 'production') {
|
|
if (!origin || allowedOrigins.includes(origin) || allowedOrigins.includes('*')) {
|
|
callback(null, true);
|
|
} else {
|
|
callback(new Error('Not allowed by CORS'));
|
|
}
|
|
} else {
|
|
callback(null, true);
|
|
}
|
|
},
|
|
credentials: true,
|
|
optionsSuccessStatus: 200,
|
|
};
|
|
|
|
/**
|
|
* Input validation middleware
|
|
*/
|
|
export const validateInput = (schema: any) => {
|
|
return (req: Request, res: Response, next: NextFunction) => {
|
|
try {
|
|
schema.parse(req.body);
|
|
next();
|
|
} catch (error: any) {
|
|
res.status(400).json({
|
|
error: 'Validation error',
|
|
details: error.errors,
|
|
});
|
|
}
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Sanitize input to prevent XSS
|
|
*/
|
|
export const sanitizeInput = (req: Request, res: Response, next: NextFunction) => {
|
|
if (req.body && typeof req.body === 'object') {
|
|
const sanitize = (obj: any): any => {
|
|
if (typeof obj === 'string') {
|
|
// Remove potentially dangerous characters
|
|
return obj.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
|
|
}
|
|
if (Array.isArray(obj)) {
|
|
return obj.map(sanitize);
|
|
}
|
|
if (obj && typeof obj === 'object') {
|
|
const sanitized: any = {};
|
|
for (const key in obj) {
|
|
sanitized[key] = sanitize(obj[key]);
|
|
}
|
|
return sanitized;
|
|
}
|
|
return obj;
|
|
};
|
|
|
|
req.body = sanitize(req.body);
|
|
}
|
|
next();
|
|
};
|
|
|