UNPKG

ailock

Version:

AI-Proof File Guard - Protect sensitive files from accidental AI modifications

189 lines 7.61 kB
import { stat, access, constants } from 'fs/promises'; import { platform } from 'os'; import { SecureCommandExecutor } from '../security/CommandExecutor.js'; /** * File diagnostics utility for troubleshooting unlock issues */ export class FileDiagnostics { commandExecutor; constructor() { this.commandExecutor = new SecureCommandExecutor(['stat', 'chflags', 'chattr', 'lsattr', 'ls']); } /** * Get comprehensive file permission information */ async getFilePermissions(filePath) { const stats = await stat(filePath); const mode = stats.mode; return { mode, octal: (mode & parseInt('777', 8)).toString(8).padStart(3, '0'), readable: (mode & constants.R_OK) !== 0, writable: (mode & constants.W_OK) !== 0, executable: (mode & constants.X_OK) !== 0, owner: stats.uid.toString(), group: stats.gid.toString(), size: stats.size, lastModified: stats.mtime }; } /** * Check file flags and extended attributes */ async getFileFlags(filePath) { const currentPlatform = platform(); const flagInfo = { platform: currentPlatform }; try { if (currentPlatform === 'darwin') { // Check macOS chflags using ls -lO try { // First try with GNU ls (if available) const result = await this.commandExecutor.executeCommand('ls', ['-lO', filePath], { timeout: 3000 }); flagInfo.chflagsOutput = result.stdout.trim(); flagInfo.hasImmutableFlag = result.stdout.includes('uchg') || result.stdout.includes('immutable'); flagInfo.hasSystemFlag = result.stdout.includes('schg'); } catch (error) { try { // Fallback: try using stat with correct format for file flags const result = await this.commandExecutor.executeCommand('stat', ['-f', '%f', filePath], { timeout: 3000 }); const flags = parseInt(result.stdout.trim(), 16); flagInfo.chflagsOutput = `Flags: 0x${flags.toString(16)}`; // Check for UF_IMMUTABLE (0x2) and UF_APPEND (0x4) flagInfo.hasImmutableFlag = (flags & 0x2) !== 0; flagInfo.hasSystemFlag = (flags & 0x20000) !== 0; // SF_IMMUTABLE } catch (statError) { flagInfo.error = `macOS flag check failed: ${error}, stat fallback: ${statError}`; } } } else if (currentPlatform === 'linux') { // Check Linux chattr/lsattr try { const result = await this.commandExecutor.executeCommand('lsattr', [filePath], { timeout: 3000 }); flagInfo.chattrOutput = result.stdout.trim(); flagInfo.hasImmutableFlag = result.stdout.includes('i'); } catch (error) { flagInfo.error = `lsattr check failed: ${error}`; } } } catch (error) { flagInfo.error = `Platform-specific flag check failed: ${error}`; } return flagInfo; } /** * Check if atomic lock file exists */ async checkAtomicLock(filePath) { try { const lockDir = '.ailock-locks'; const basename = filePath.split('/').pop() || 'unknown'; const lockFilePath = `${lockDir}/${basename}.lock`; await access(lockFilePath, constants.F_OK); return true; } catch { return false; } } /** * Perform comprehensive diagnostics for unlock issues */ async diagnoseUnlockIssues(filePath) { const permissions = await this.getFilePermissions(filePath); const flags = await this.getFileFlags(filePath); const atomicLockExists = await this.checkAtomicLock(filePath); const diagnosis = []; const recommendations = []; // Analyze permissions if (permissions.octal === '444') { diagnosis.push('File has read-only permissions (444)'); } else if (permissions.octal === '644') { diagnosis.push('File has normal write permissions (644)'); } else { diagnosis.push(`File has unusual permissions (${permissions.octal})`); } // Analyze flags if (flags.hasImmutableFlag) { diagnosis.push(`File has immutable flag set (${flags.platform})`); if (flags.platform === 'darwin') { recommendations.push('Run: chflags nouchg <file>'); } else if (flags.platform === 'linux') { recommendations.push('Run: chattr -i <file>'); } } // Analyze atomic locks if (atomicLockExists) { diagnosis.push('Atomic lock file exists'); recommendations.push('Release atomic lock or wait for timeout'); } // Provide general recommendations if (permissions.octal === '444' && !flags.hasImmutableFlag) { recommendations.push('Run: chmod 644 <file>'); } if (diagnosis.length === 0) { diagnosis.push('No obvious unlock issues detected'); } return { filePath, permissions, flags, atomicLockExists, diagnosis, recommendations }; } /** * Format diagnostics for display */ formatDiagnostics(diagnostics) { const lines = []; lines.push(`🔍 Unlock Diagnostics for: ${diagnostics.filePath}`); lines.push(''); lines.push('📋 File Permissions:'); lines.push(` Mode: ${diagnostics.permissions.octal} (${diagnostics.permissions.mode})`); lines.push(` Owner: ${diagnostics.permissions.owner}:${diagnostics.permissions.group}`); lines.push(` Size: ${diagnostics.permissions.size} bytes`); lines.push(` Modified: ${diagnostics.permissions.lastModified.toISOString()}`); lines.push(''); lines.push('🏷️ File Flags:'); lines.push(` Platform: ${diagnostics.flags.platform}`); if (diagnostics.flags.chflagsOutput) { lines.push(` Flags: ${diagnostics.flags.chflagsOutput}`); } if (diagnostics.flags.chattrOutput) { lines.push(` Attributes: ${diagnostics.flags.chattrOutput}`); } if (diagnostics.flags.error) { lines.push(` Error: ${diagnostics.flags.error}`); } lines.push(''); lines.push('🔒 Lock Status:'); lines.push(` Atomic lock: ${diagnostics.atomicLockExists ? 'EXISTS' : 'Not found'}`); lines.push(''); lines.push('🔍 Diagnosis:'); diagnostics.diagnosis.forEach(item => lines.push(` • ${item}`)); lines.push(''); if (diagnostics.recommendations.length > 0) { lines.push('💡 Recommendations:'); diagnostics.recommendations.forEach(item => lines.push(` • ${item}`)); } return lines.join('\n'); } } //# sourceMappingURL=FileDiagnostics.js.map