ailock
Version:
AI-Proof File Guard - Protect sensitive files from accidental AI modifications
150 lines • 5.17 kB
JavaScript
import { Platform } from '../platform.js';
import { BasePlatformAdapter } from './BasePlatformAdapter.js';
import path from 'path';
import { execSync } from 'child_process';
/**
* Unix/Linux/macOS platform adapter for file locking
*/
export class UnixAdapter extends BasePlatformAdapter {
platformType = Platform.UNIX;
isMacOS = process.platform === 'darwin';
isLinux = process.platform === 'linux';
/**
* Lock a file on Unix-like systems
*/
async lockFile(filePath) {
const absolutePath = path.resolve(filePath);
// Validate path
await this.pathValidator.validateAndSanitizePath(absolutePath);
// First, make the file read-only
await this.makeReadOnly(absolutePath);
// Then apply immutable attribute if supported
if (this.supportsImmutable()) {
await this.applyImmutableAttribute(absolutePath);
}
}
/**
* Unlock a file on Unix-like systems
*/
async unlockFile(filePath) {
const absolutePath = path.resolve(filePath);
// Validate path
await this.pathValidator.validateAndSanitizePath(absolutePath);
// First, remove immutable attribute if it exists
if (this.supportsImmutable()) {
await this.removeImmutableAttribute(absolutePath);
}
// Then make the file writable
await this.makeWritable(absolutePath);
}
/**
* Check if a file is locked on Unix-like systems
*/
async isLocked(filePath) {
const absolutePath = path.resolve(filePath);
// Check if file is read-only
const readOnly = await this.isReadOnly(absolutePath);
// A file is considered locked if it's read-only
// The immutable flag is an additional layer of protection
return readOnly;
}
/**
* Check if the platform supports immutable file attributes
*/
supportsImmutable() {
// macOS supports chflags, Linux supports chattr
// We check for command availability
if (this.isMacOS) {
return this.commandExists('chflags');
}
if (this.isLinux) {
return this.commandExists('chattr');
}
return false;
}
/**
* Check if a file has immutable attributes
*/
async checkImmutable(filePath) {
if (!this.supportsImmutable()) {
return false;
}
try {
if (this.isMacOS) {
// On macOS, use ls -lO to check for uchg flag
const result = await this.commandExecutor.executeCommand('ls', ['-lO', filePath]);
return result.stdout.includes('uchg');
}
if (this.isLinux) {
// On Linux, use lsattr to check for immutable flag
const result = await this.commandExecutor.executeCommand('lsattr', [filePath]);
return result.stdout.includes('i');
}
}
catch {
// If we can't check, assume not immutable
return false;
}
return false;
}
/**
* Apply immutable attribute to a file
*/
async applyImmutableAttribute(filePath) {
if (!this.supportsImmutable()) {
return;
}
try {
if (this.isMacOS) {
// On macOS, use chflags to set uchg (user immutable) flag
await this.commandExecutor.executeCommand('chflags', ['uchg', filePath]);
}
else if (this.isLinux) {
// On Linux, use chattr to set immutable flag
// Note: This might require sudo
await this.commandExecutor.executeCommand('chattr', ['+i', filePath]);
}
}
catch (error) {
// If we can't set immutable (e.g., no sudo), continue with just read-only
console.debug(`Could not set immutable attribute: ${error}`);
}
}
/**
* Remove immutable attribute from a file
*/
async removeImmutableAttribute(filePath) {
if (!this.supportsImmutable()) {
return;
}
try {
if (this.isMacOS) {
// On macOS, use chflags to remove uchg flag
await this.commandExecutor.executeCommand('chflags', ['nouchg', filePath]);
}
else if (this.isLinux) {
// On Linux, use chattr to remove immutable flag
// Note: This might require sudo
await this.commandExecutor.executeCommand('chattr', ['-i', filePath]);
}
}
catch (error) {
// If we can't remove immutable (e.g., no sudo), try to continue
console.debug(`Could not remove immutable attribute: ${error}`);
}
}
/**
* Check if a command exists on the system
* Made synchronous to maintain interface compatibility
*/
commandExists(command) {
try {
execSync(`which ${command}`, { stdio: 'ignore' });
return true;
}
catch {
return false;
}
}
}
//# sourceMappingURL=UnixAdapter.js.map