hikma-engine
Version:
Code Knowledge Graph Indexer - A sophisticated TypeScript-based indexer that transforms Git repositories into multi-dimensional knowledge stores for AI agents
299 lines (298 loc) • 10.8 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.sanitizeInput = sanitizeInput;
exports.preventPathTraversal = preventPathTraversal;
exports.preventSQLInjection = preventSQLInjection;
exports.limitRequestSize = limitRequestSize;
exports.securityHeaders = securityHeaders;
exports.configureHelmet = configureHelmet;
exports.ipWhitelist = ipWhitelist;
exports.requestTimeout = requestTimeout;
exports.configureCORS = configureCORS;
exports.securityAuditLog = securityAuditLog;
const helmet_1 = __importDefault(require("helmet"));
const api_config_1 = require("../config/api-config");
const api_errors_1 = require("../errors/api-errors");
// Input sanitization middleware
function sanitizeInput(req, res, next) {
// Sanitize query parameters
if (req.query) {
for (const [key, value] of Object.entries(req.query)) {
if (typeof value === 'string') {
// Remove potentially dangerous characters
req.query[key] = value
.replace(/[<>]/g, '') // Remove angle brackets
.replace(/javascript:/gi, '') // Remove javascript: protocol
.replace(/on\w+=/gi, '') // Remove event handlers
.trim();
}
}
}
// Sanitize request body
if (req.body && typeof req.body === 'object') {
sanitizeObject(req.body);
}
next();
}
function sanitizeObject(obj) {
for (const [key, value] of Object.entries(obj)) {
if (typeof value === 'string') {
obj[key] = value
.replace(/[<>]/g, '')
.replace(/javascript:/gi, '')
.replace(/on\w+=/gi, '')
.trim();
}
else if (typeof value === 'object' && value !== null) {
sanitizeObject(value);
}
}
}
// Path traversal prevention
function preventPathTraversal(req, res, next) {
const dangerousPatterns = [
'../',
'..\\',
'%2e%2e%2f',
'%2e%2e%5c',
'..%2f',
'..%5c',
];
const checkValue = (value) => {
const lowerValue = value.toLowerCase();
return dangerousPatterns.some(pattern => lowerValue.includes(pattern));
};
// Check query parameters
if (req.query) {
for (const [key, value] of Object.entries(req.query)) {
if (typeof value === 'string' && checkValue(value)) {
throw api_errors_1.APIErrors.validation.invalid(key, value, 'Path traversal attempt detected');
}
}
}
// Check request body
if (req.body && typeof req.body === 'object') {
const checkObjectForPathTraversal = (obj, path = '') => {
for (const [key, value] of Object.entries(obj)) {
const currentPath = path ? `${path}.${key}` : key;
if (typeof value === 'string' && checkValue(value)) {
throw api_errors_1.APIErrors.validation.invalid(currentPath, value, 'Path traversal attempt detected');
}
else if (typeof value === 'object' && value !== null) {
checkObjectForPathTraversal(value, currentPath);
}
}
};
checkObjectForPathTraversal(req.body);
}
next();
}
// SQL injection prevention (basic patterns)
function preventSQLInjection(req, res, next) {
const sqlPatterns = [
/(\b(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC|UNION|SCRIPT)\b)/gi,
/(\b(OR|AND)\s+\d+\s*=\s*\d+)/gi,
/(--|\/\*|\*\/)/g,
/(\b(CHAR|NCHAR|VARCHAR|NVARCHAR)\s*\()/gi,
/(\b(CAST|CONVERT)\s*\()/gi,
];
const checkForSQLInjection = (value) => {
return sqlPatterns.some(pattern => pattern.test(value));
};
// Check query parameters
if (req.query) {
for (const [key, value] of Object.entries(req.query)) {
if (typeof value === 'string' && checkForSQLInjection(value)) {
throw api_errors_1.APIErrors.validation.invalid(key, value, 'Potential SQL injection detected');
}
}
}
// Check request body
if (req.body && typeof req.body === 'object') {
const checkObjectForSQL = (obj, path = '') => {
for (const [key, value] of Object.entries(obj)) {
const currentPath = path ? `${path}.${key}` : key;
if (typeof value === 'string' && checkForSQLInjection(value)) {
throw api_errors_1.APIErrors.validation.invalid(currentPath, value, 'Potential SQL injection detected');
}
else if (typeof value === 'object' && value !== null) {
checkObjectForSQL(value, currentPath);
}
}
};
checkObjectForSQL(req.body);
}
next();
}
// Request size limiting
function limitRequestSize(maxSize = '10mb') {
return (req, res, next) => {
const contentLength = req.get('content-length');
if (contentLength) {
const sizeInBytes = parseInt(contentLength, 10);
const maxSizeInBytes = parseSize(maxSize);
if (sizeInBytes > maxSizeInBytes) {
throw api_errors_1.APIErrors.validation.invalid('Content-Length', contentLength, `exceeds maximum allowed size ${maxSize}`);
}
}
next();
};
}
function parseSize(size) {
const units = {
b: 1,
kb: 1024,
mb: 1024 * 1024,
gb: 1024 * 1024 * 1024,
};
const match = size.toLowerCase().match(/^(\d+(?:\.\d+)?)\s*([a-z]+)?$/);
if (!match) {
throw new Error(`Invalid size format: ${size}`);
}
const value = parseFloat(match[1]);
const unit = match[2] || 'b';
const multiplier = units[unit];
if (!multiplier) {
throw new Error(`Unknown size unit: ${unit}`);
}
return Math.floor(value * multiplier);
}
// Security headers middleware
function securityHeaders(req, res, next) {
const securityConfig = api_config_1.apiConfig.getSecurityConfig();
if (securityConfig.headers.contentSecurityPolicy) {
res.setHeader('Content-Security-Policy', "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';");
}
if (securityConfig.headers.xFrameOptions) {
res.setHeader('X-Frame-Options', 'DENY');
}
if (securityConfig.headers.xContentTypeOptions) {
res.setHeader('X-Content-Type-Options', 'nosniff');
}
if (securityConfig.headers.referrerPolicy) {
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
}
// Additional security headers
res.setHeader('X-XSS-Protection', '1; mode=block');
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
res.setHeader('Permissions-Policy', 'geolocation=(), microphone=(), camera=()');
next();
}
// Helmet configuration
function configureHelmet() {
const securityConfig = api_config_1.apiConfig.getSecurityConfig();
return (0, helmet_1.default)({
contentSecurityPolicy: securityConfig.headers.contentSecurityPolicy ? {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
connectSrc: ["'self'"],
fontSrc: ["'self'"],
objectSrc: ["'none'"],
mediaSrc: ["'self'"],
frameSrc: ["'none'"],
},
} : false,
crossOriginEmbedderPolicy: false, // Disable for API compatibility
frameguard: securityConfig.headers.xFrameOptions ? {
action: 'deny'
} : false,
noSniff: securityConfig.headers.xContentTypeOptions,
referrerPolicy: securityConfig.headers.referrerPolicy ? {
policy: 'strict-origin-when-cross-origin'
} : false,
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true,
},
xssFilter: true,
});
}
// IP whitelist middleware
function ipWhitelist(allowedIPs = []) {
return (req, res, next) => {
if (allowedIPs.length === 0) {
return next(); // No IP restrictions
}
const clientIP = req.ip || req.connection.remoteAddress || req.socket.remoteAddress;
if (!clientIP || !allowedIPs.includes(clientIP)) {
throw api_errors_1.APIErrors.authorization(`Access denied from IP: ${clientIP}`);
}
next();
};
}
// Request timeout middleware
function requestTimeout(timeoutMs = 30000) {
return (req, res, next) => {
const timeout = setTimeout(() => {
if (!res.headersSent) {
throw api_errors_1.APIErrors.timeout(`Request timeout after ${timeoutMs}ms`);
}
}, timeoutMs);
res.on('finish', () => {
clearTimeout(timeout);
});
res.on('close', () => {
clearTimeout(timeout);
});
next();
};
}
// CORS configuration
function configureCORS() {
const corsConfig = api_config_1.apiConfig.getCorsConfig();
return {
origin: (origin, callback) => {
// Allow requests with no origin (like mobile apps or curl requests)
if (!origin)
return callback(null, true);
if (corsConfig.origins.includes('*') || corsConfig.origins.includes(origin)) {
callback(null, true);
}
else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: corsConfig.credentials,
methods: corsConfig.methods,
allowedHeaders: [
'Origin',
'X-Requested-With',
'Content-Type',
'Accept',
'Authorization',
'X-API-Key',
'X-Request-ID',
],
exposedHeaders: [
'X-Request-ID',
'X-Response-Time',
'X-Rate-Limit-Remaining',
'X-Rate-Limit-Reset',
],
};
}
// Security audit logging
function securityAuditLog(req, res, next) {
const securityEvents = [
'authentication_failure',
'authorization_failure',
'suspicious_activity',
'rate_limit_exceeded',
];
// This would integrate with your logging system
// For now, we'll just add the audit context to the request
req.auditContext = {
ip: req.ip,
userAgent: req.get('User-Agent'),
timestamp: new Date().toISOString(),
endpoint: `${req.method} ${req.path}`,
};
next();
}
;