ken-you-code
Version:
Connect your codebase to Kimi: Ultra-fast AI code analysis with Kimi-K2 model via MCP
82 lines (68 loc) • 2.31 kB
text/typescript
import { readFile, stat } from 'fs/promises';
import path from 'path';
import { config } from '../config/index.js';
export interface ReadFileResult {
content: string;
size: number;
extension: string;
}
export class FileOperations {
private static isPathSafe(filePath: string): boolean {
const resolvedPath = path.resolve(filePath);
// Check if workspace root is configured and enforce it
if (config.workspaceRoot) {
const workspaceRoot = path.resolve(config.workspaceRoot);
if (!resolvedPath.startsWith(workspaceRoot)) {
return false;
}
}
// Check for forbidden paths
const forbiddenPaths = ['.env', '.git/', 'node_modules/', '__pycache__/'];
for (const forbidden of forbiddenPaths) {
if (resolvedPath.includes(forbidden)) {
return false;
}
}
return true;
}
private static isExtensionAllowed(filePath: string): boolean {
const ext = path.extname(filePath).toLowerCase();
return config.allowedExtensions.includes(ext);
}
static async readFileSecurely(filePath: string): Promise<ReadFileResult> {
// Validate path safety
if (!this.isPathSafe(filePath)) {
throw new Error(`Access denied: Path '${filePath}' is not allowed`);
}
// Validate file extension
if (!this.isExtensionAllowed(filePath)) {
throw new Error(`File type not allowed: ${path.extname(filePath)}`);
}
try {
// Check file exists and get stats
const fileStats = await stat(filePath);
if (!fileStats.isFile()) {
throw new Error(`Path is not a file: ${filePath}`);
}
// Check file size
const fileSizeMB = fileStats.size / (1024 * 1024);
if (fileSizeMB > config.maxFileSizeMB) {
throw new Error(
`File too large: ${fileSizeMB.toFixed(2)}MB (max: ${config.maxFileSizeMB}MB)`
);
}
// Read file content
const content = await readFile(filePath, 'utf-8');
return {
content,
size: fileStats.size,
extension: path.extname(filePath),
};
} catch (error) {
if (error instanceof Error) {
throw new Error(`Failed to read file '${filePath}': ${error.message}`);
}
throw new Error(`Failed to read file '${filePath}': Unknown error`);
}
}
}