mira-consciousness
Version:
Memory & Intelligence Retention Archive - Preserving The Spark
164 lines • 5.25 kB
JavaScript
/**
* Input Validation and Sanitization Utilities
* ==========================================
*
* Provides comprehensive input validation to prevent injection attacks
* and ensure data integrity throughout the MIRA system.
*/
import * as path from 'path';
/**
* Validates and sanitizes file paths to prevent directory traversal attacks
*/
export function validateFilePath(filePath, allowedRoot) {
// Remove any null bytes
filePath = filePath.replace(/\0/g, '');
// Normalize the path
const normalizedPath = path.normalize(filePath);
// Check for directory traversal attempts
if (normalizedPath.includes('..')) {
throw new Error('Directory traversal attempt detected');
}
// If allowedRoot is specified, ensure the path is within it
if (allowedRoot) {
const resolvedPath = path.resolve(normalizedPath);
const resolvedRoot = path.resolve(allowedRoot);
if (!resolvedPath.startsWith(resolvedRoot)) {
throw new Error('Path is outside allowed directory');
}
}
return normalizedPath;
}
/**
* Validates command arguments to prevent command injection
*/
export function validateCommandArgs(args) {
return args.map(arg => {
// Remove null bytes
arg = arg.replace(/\0/g, '');
// Check for shell metacharacters that could lead to injection
const dangerousChars = /[;&|`$<>\\]/;
if (dangerousChars.test(arg)) {
throw new Error(`Potentially dangerous characters in argument: ${arg}`);
}
return arg;
});
}
/**
* Sanitizes user input for safe display (prevents XSS in terminal output)
*/
export function sanitizeForDisplay(input) {
return input
.replace(/\x1b\[[0-9;]*m/g, '') // Remove ANSI escape codes
.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '') // Remove control characters
.substring(0, 10000); // Limit length to prevent DoS
}
/**
* Validates environment variable names
*/
export function validateEnvVarName(name) {
// Environment variable names should only contain alphanumeric and underscore
return /^[A-Z_][A-Z0-9_]*$/i.test(name);
}
/**
* Validates and sanitizes JSON input
*/
export function validateJSON(input) {
try {
// Parse to validate structure
const parsed = JSON.parse(input);
// Re-stringify to remove any potential code injection
return JSON.parse(JSON.stringify(parsed));
}
catch (error) {
throw new Error('Invalid JSON input');
}
}
/**
* Validates Git branch/tag names
*/
export function validateGitRef(ref) {
// Git ref name rules
const invalidPatterns = [
/^\./, // Cannot start with dot
/\.$/, // Cannot end with dot
/\.\./, // Cannot contain ..
/\/\//, // Cannot contain //
/^\//, // Cannot start with /
/\/$/, // Cannot end with /
/@{/, // Cannot contain @{
/[~^:?*\[\]\\]/, // Cannot contain special chars
];
for (const pattern of invalidPatterns) {
if (pattern.test(ref)) {
throw new Error(`Invalid Git reference: ${ref}`);
}
}
return ref;
}
/**
* Rate limiting helper to prevent DoS attacks
*/
export class RateLimiter {
maxRequests;
windowMs;
requests = new Map();
constructor(maxRequests = 100, windowMs = 60000 // 1 minute
) {
this.maxRequests = maxRequests;
this.windowMs = windowMs;
}
isAllowed(identifier) {
const now = Date.now();
const requests = this.requests.get(identifier) || [];
// Remove old requests outside the window
const validRequests = requests.filter(time => now - time < this.windowMs);
if (validRequests.length >= this.maxRequests) {
return false;
}
validRequests.push(now);
this.requests.set(identifier, validRequests);
// Cleanup old entries periodically
if (Math.random() < 0.01) {
this.cleanup();
}
return true;
}
cleanup() {
const now = Date.now();
for (const [key, requests] of this.requests.entries()) {
const validRequests = requests.filter(time => now - time < this.windowMs);
if (validRequests.length === 0) {
this.requests.delete(key);
}
else {
this.requests.set(key, validRequests);
}
}
}
}
/**
* Validates memory content before storage
*/
export function validateMemoryContent(content) {
// Check size limit (1MB)
if (content.length > 1024 * 1024) {
throw new Error('Memory content exceeds size limit (1MB)');
}
// Remove any potential script tags or HTML
content = content.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '');
content = content.replace(/<[^>]+>/g, '');
return content;
}
/**
* Validates search queries
*/
export function validateSearchQuery(query) {
// Limit query length
if (query.length > 500) {
throw new Error('Search query too long');
}
// Remove special regex characters that could cause ReDoS
query = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
return query;
}
//# sourceMappingURL=inputValidation.js.map