context-optimizer-mcp-server
Version:
Context optimization tools MCP server for AI coding assistants - compatible with GitHub Copilot, Cursor AI, and other MCP-supporting assistants
211 lines • 8.42 kB
JavaScript
/**
* Path security validation utilities
*
* Prevents path traversal attacks and unauthorized file access
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.PathValidator = void 0;
const path = __importStar(require("path"));
const fs = __importStar(require("fs/promises"));
const manager_1 = require("../config/manager");
class PathValidator {
/**
* Validates a file path against security boundaries
*
* @param requestedPath - Path to validate
* @returns Validation result with resolved path or error
*/
static async validateFilePath(requestedPath) {
try {
const config = manager_1.ConfigurationManager.getConfig();
const resolvedPath = path.resolve(requestedPath);
// Check against allowed base paths
const isAllowed = config.security.allowedBasePaths.some(basePath => {
const resolvedBase = path.resolve(basePath);
// Use case-insensitive comparison on Windows
const normalizedPath = process.platform === 'win32' ? resolvedPath.toLowerCase() : resolvedPath;
const normalizedBase = process.platform === 'win32' ? resolvedBase.toLowerCase() : resolvedBase;
const basePlusSlash = normalizedBase + path.sep;
return normalizedPath.startsWith(basePlusSlash) || normalizedPath === normalizedBase;
});
if (!isAllowed) {
return {
valid: false,
error: `Path '${resolvedPath}' is outside allowed directories: ${config.security.allowedBasePaths.join(', ')}`
};
}
// Check if file exists
try {
const stats = await fs.stat(resolvedPath);
if (!stats.isFile()) {
return {
valid: false,
error: `Path '${resolvedPath}' is not a file`
};
}
// Check file size
if (stats.size > config.security.maxFileSize) {
return {
valid: false,
error: `File '${resolvedPath}' exceeds maximum size limit (${config.security.maxFileSize} bytes)`
};
}
}
catch {
return {
valid: false,
error: `File '${resolvedPath}' does not exist or is not accessible`
};
}
return {
valid: true,
resolvedPath
};
}
catch (error) {
return {
valid: false,
error: `Path validation error: ${error instanceof Error ? error.message : String(error)}`
};
}
}
/**
* Validates a working directory path against security boundaries
*
* @param requestedPath - Directory path to validate
* @returns Validation result with resolved path or error
*/
static async validateWorkingDirectory(requestedPath) {
try {
const config = manager_1.ConfigurationManager.getConfig();
const resolvedPath = path.resolve(requestedPath);
// Check against allowed base paths
const isAllowed = config.security.allowedBasePaths.some(basePath => {
const resolvedBase = path.resolve(basePath);
// Use case-insensitive comparison on Windows
const normalizedPath = process.platform === 'win32' ? resolvedPath.toLowerCase() : resolvedPath;
const normalizedBase = process.platform === 'win32' ? resolvedBase.toLowerCase() : resolvedBase;
const basePlusSlash = normalizedBase + path.sep;
return normalizedPath.startsWith(basePlusSlash) || normalizedPath === normalizedBase;
});
if (!isAllowed) {
return {
valid: false,
error: `Working directory '${resolvedPath}' is outside allowed paths: ${config.security.allowedBasePaths.join(', ')}`
};
}
// Check if directory exists
try {
const stats = await fs.stat(resolvedPath);
if (!stats.isDirectory()) {
return {
valid: false,
error: `Path '${resolvedPath}' is not a directory`
};
}
}
catch {
return {
valid: false,
error: `Directory '${resolvedPath}' does not exist or is not accessible`
};
}
return {
valid: true,
resolvedPath
};
}
catch (error) {
return {
valid: false,
error: `Directory validation error: ${error instanceof Error ? error.message : String(error)}`
};
}
}
/**
* Helper method to check if a path is within allowed boundaries (without file system checks)
*
* @param requestedPath - Path to check
* @returns Whether the path is within allowed boundaries
*/
static isPathAllowed(requestedPath) {
try {
const config = manager_1.ConfigurationManager.getConfig();
const resolvedPath = path.resolve(requestedPath);
return config.security.allowedBasePaths.some(basePath => {
const resolvedBase = path.resolve(basePath);
return resolvedPath.startsWith(resolvedBase + path.sep) || resolvedPath === resolvedBase;
});
}
catch {
return false;
}
}
/**
* Normalize and sanitize a path for logging purposes
*
* @param filePath - Path to sanitize
* @returns Sanitized path safe for logging
*/
static sanitizePathForLogging(filePath) {
try {
// Check for obviously invalid characters first
if (/[<>:"|?*\0]/.test(filePath)) {
return '[INVALID_PATH]';
}
const resolved = path.resolve(filePath);
// Replace all directory components except the last one with placeholders
const parts = resolved.split(path.sep);
if (parts.length <= 2) {
return resolved; // Too short to sanitize meaningfully
}
// Keep first part (drive on Windows, root on Unix) and last part (filename)
const sanitized = parts.map((part, index) => {
if (index === 0 || index === parts.length - 1) {
return part;
}
return '****';
}).join(path.sep);
return sanitized;
}
catch {
return '[INVALID_PATH]';
}
}
}
exports.PathValidator = PathValidator;
//# sourceMappingURL=pathValidator.js.map
;