/**
 * Permission Configuration System
 *
 * Based on Cursor CLI's permission model:
 * - JSON-based configuration
 * - Path restrictions
 * - Command allowlists/blocklists
 * - Network access control
 * - Sandboxed execution support
 *
 * Provides fine-grained control over what the AI agent can do.
 */
import * as fs from 'fs';
import * as path from 'path';
import { EventEmitter } from 'events';
import { logger } from "../utils/logger.js";
// ============================================================================
// Default Configuration
// ============================================================================
const DEFAULT_PERMISSION_CONFIG = {
    version: '1.0.0',
    fileSystem: {
        allowedReadPaths: ['**/*'],
        allowedWritePaths: ['**/*'],
        blockedPaths: [
            '**/node_modules/**',
            '**/.git/objects/**',
            '**/.env',
            '**/*.pem',
            '**/*.key',
            '**/secrets/**',
            '**/credentials/**',
        ],
        maxFileSize: 10 * 1024 * 1024, // 10MB
        allowCreate: true,
        allowDelete: true,
    },
    commands: {
        allowedCommands: [
            'git *',
            'npm *',
            'npx *',
            'yarn *',
            'pnpm *',
            'node *',
            'python *',
            'pip *',
            'cargo *',
            'go *',
            'make *',
            'ls *',
            'cat *',
            'grep *',
            'find *',
            'head *',
            'tail *',
            'wc *',
            'sort *',
            'uniq *',
            'diff *',
            'curl *',
            'wget *',
        ],
        blockedCommands: [
            'rm -rf /',
            'rm -rf ~',
            'rm -rf /*',
            ':(){:|:&};:', // Fork bomb
            '> /dev/sda',
            'dd if=/dev/zero',
            'mkfs.*',
            'chmod 777',
            'curl * | bash',
            'wget * | bash',
        ],
        allowArbitraryCommands: false,
        maxExecutionTime: 300000, // 5 minutes
        allowSudo: false,
    },
    network: {
        allowOutgoing: true,
        allowedHosts: ['*'],
        blockedHosts: [],
        allowLocalhost: true,
    },
    tools: {
        requireConfirmation: [
            'bash',
            'str_replace_editor',
            'create_file',
            'delete_file',
        ],
        disabled: [],
        autoApproved: [
            'view_file',
            'search',
            'list_files',
        ],
    },
    safety: {
        sandboxMode: false,
        confirmDestructive: true,
        dryRunMode: false,
        maxOperationsPerSession: 1000,
    },
};
// ============================================================================
// Permission Manager
// ============================================================================
export class PermissionManager extends EventEmitter {
    config;
    configPath;
    operationCount = 0;
    constructor(configPath) {
        super();
        this.configPath = configPath || '.codebuddy/permissions.json';
        this.config = this.loadConfig();
    }
    /**
     * Load configuration from file or use defaults
     */
    loadConfig() {
        try {
            if (fs.existsSync(this.configPath)) {
                const content = fs.readFileSync(this.configPath, 'utf-8');
                const loaded = JSON.parse(content);
                return this.mergeWithDefaults(loaded);
            }
        }
        catch (error) {
            logger.error(`Error loading permission config: ${error}`);
        }
        // Return a deep copy of defaults to prevent mutation of the original
        return this.mergeWithDefaults({});
    }
    /**
     * Merge loaded config with defaults (for missing fields)
     */
    mergeWithDefaults(loaded) {
        return {
            ...DEFAULT_PERMISSION_CONFIG,
            ...loaded,
            fileSystem: { ...DEFAULT_PERMISSION_CONFIG.fileSystem, ...loaded.fileSystem },
            commands: { ...DEFAULT_PERMISSION_CONFIG.commands, ...loaded.commands },
            network: { ...DEFAULT_PERMISSION_CONFIG.network, ...loaded.network },
            tools: { ...DEFAULT_PERMISSION_CONFIG.tools, ...loaded.tools },
            safety: { ...DEFAULT_PERMISSION_CONFIG.safety, ...loaded.safety },
        };
    }
    /**
     * Save configuration to file
     */
    saveConfig() {
        try {
            const dir = path.dirname(this.configPath);
            if (!fs.existsSync(dir)) {
                fs.mkdirSync(dir, { recursive: true });
            }
            fs.writeFileSync(this.configPath, JSON.stringify(this.config, null, 2));
            this.emit('config:saved', this.configPath);
        }
        catch (error) {
            this.emit('config:error', error);
        }
    }
    /**
     * Check if a file path is allowed for reading
     */
    checkReadPermission(filePath) {
        const normalized = path.normalize(filePath);
        // Check blocked paths first
        if (this.matchesAnyPattern(normalized, this.config.fileSystem.blockedPaths)) {
            return { allowed: false, reason: 'Path is in blocked list' };
        }
        // Check allowed paths
        if (!this.matchesAnyPattern(normalized, this.config.fileSystem.allowedReadPaths)) {
            return { allowed: false, reason: 'Path not in allowed read list' };
        }
        return { allowed: true };
    }
    /**
     * Check if a file path is allowed for writing
     */
    checkWritePermission(filePath, isCreate = false) {
        const normalized = path.normalize(filePath);
        // Check operation count
        if (this.operationCount >= this.config.safety.maxOperationsPerSession) {
            return { allowed: false, reason: 'Maximum operations per session reached' };
        }
        // Check blocked paths
        if (this.matchesAnyPattern(normalized, this.config.fileSystem.blockedPaths)) {
            return { allowed: false, reason: 'Path is in blocked list' };
        }
        // Check allowed paths
        if (!this.matchesAnyPattern(normalized, this.config.fileSystem.allowedWritePaths)) {
            return { allowed: false, reason: 'Path not in allowed write list' };
        }
        // Check create permission
        if (isCreate && !this.config.fileSystem.allowCreate) {
            return { allowed: false, reason: 'File creation is disabled' };
        }
        // Check if confirmation required
        const requiresConfirmation = this.config.safety.confirmDestructive;
        return { allowed: true, requiresConfirmation };
    }
    /**
     * Check if a command is allowed
     */
    checkCommandPermission(command) {
        const normalized = command.trim();
        // Check blocked commands first
        for (const blocked of this.config.commands.blockedCommands) {
            if (this.commandMatches(normalized, blocked)) {
                return { allowed: false, reason: `Command matches blocked pattern: ${blocked}` };
            }
        }
        // Check sudo
        if (!this.config.commands.allowSudo && /^sudo\s/.test(normalized)) {
            return { allowed: false, reason: 'Sudo commands are not allowed' };
        }
        // Check if arbitrary commands allowed
        if (this.config.commands.allowArbitraryCommands) {
            return { allowed: true, requiresConfirmation: this.config.safety.confirmDestructive };
        }
        // Check allowed commands
        for (const allowed of this.config.commands.allowedCommands) {
            if (this.commandMatches(normalized, allowed)) {
                return { allowed: true, requiresConfirmation: this.config.safety.confirmDestructive };
            }
        }
        return { allowed: false, reason: 'Command not in allowed list' };
    }
    /**
     * Check if a tool can be used
     */
    checkToolPermission(toolName) {
        // Check if disabled
        if (this.config.tools.disabled.includes(toolName)) {
            return { allowed: false, reason: 'Tool is disabled' };
        }
        // Check if auto-approved
        if (this.config.tools.autoApproved.includes(toolName)) {
            return { allowed: true, requiresConfirmation: false };
        }
        // Check if requires confirmation
        const requiresConfirmation = this.config.tools.requireConfirmation.includes(toolName);
        return { allowed: true, requiresConfirmation };
    }
    /**
     * Check network permission for a host
     */
    checkNetworkPermission(host) {
        if (!this.config.network.allowOutgoing) {
            return { allowed: false, reason: 'Outgoing network requests are disabled' };
        }
        // Check localhost
        if (['localhost', '127.0.0.1', '::1'].includes(host)) {
            if (!this.config.network.allowLocalhost) {
                return { allowed: false, reason: 'Localhost access is disabled' };
            }
            return { allowed: true };
        }
        // Check blocked hosts
        if (this.config.network.blockedHosts.some(b => host.includes(b))) {
            return { allowed: false, reason: 'Host is blocked' };
        }
        // Check allowed hosts
        const allowAll = this.config.network.allowedHosts.includes('*');
        if (!allowAll && !this.config.network.allowedHosts.some(a => host.includes(a))) {
            return { allowed: false, reason: 'Host not in allowed list' };
        }
        return { allowed: true };
    }
    /**
     * Record an operation (for rate limiting)
     */
    recordOperation() {
        this.operationCount++;
        this.emit('operation:recorded', this.operationCount);
    }
    /**
     * Reset operation count
     */
    resetOperationCount() {
        this.operationCount = 0;
    }
    /**
     * Check if path matches any of the patterns
     */
    matchesAnyPattern(filePath, patterns) {
        for (const pattern of patterns) {
            if (this.pathMatches(filePath, pattern)) {
                return true;
            }
        }
        return false;
    }
    /**
     * Check if path matches a glob pattern
     */
    pathMatches(filePath, pattern) {
        // Convert glob to regex
        const regex = pattern
            .replace(/[.+^${}()|[\]\\]/g, '\\$&')
            .replace(/\*\*/g, '{{GLOBSTAR}}')
            .replace(/\*/g, '[^/]*')
            .replace(/{{GLOBSTAR}}/g, '.*');
        return new RegExp(`^${regex}$`).test(filePath);
    }
    /**
     * Check if command matches a pattern
     */
    commandMatches(command, pattern) {
        // Simple pattern matching with * wildcard
        const regex = pattern
            .replace(/[.+^${}()|[\]\\]/g, '\\$&')
            .replace(/\*/g, '.*');
        return new RegExp(`^${regex}$`).test(command);
    }
    /**
     * Get current configuration
     */
    getConfig() {
        return { ...this.config };
    }
    /**
     * Update configuration
     */
    updateConfig(updates) {
        this.config = this.mergeWithDefaults({ ...this.config, ...updates });
        this.emit('config:updated', this.config);
    }
    /**
     * Enable sandbox mode
     */
    enableSandbox() {
        this.config.safety.sandboxMode = true;
        this.config.commands.allowArbitraryCommands = false;
        this.config.commands.allowSudo = false;
        this.config.fileSystem.allowDelete = false;
        this.config.safety.confirmDestructive = true;
        this.emit('sandbox:enabled');
    }
    /**
     * Enable dry-run mode
     */
    enableDryRun() {
        this.config.safety.dryRunMode = true;
        this.emit('dryrun:enabled');
    }
    /**
     * Check if in sandbox mode
     */
    isSandboxed() {
        return this.config.safety.sandboxMode;
    }
    /**
     * Check if in dry-run mode
     */
    isDryRun() {
        return this.config.safety.dryRunMode;
    }
    /**
     * Dispose and cleanup resources
     */
    dispose() {
        this.removeAllListeners();
    }
}
// ============================================================================
// Singleton
// ============================================================================
let permissionManagerInstance = null;
export function getPermissionManager(configPath) {
    if (!permissionManagerInstance) {
        permissionManagerInstance = new PermissionManager(configPath);
    }
    return permissionManagerInstance;
}
export function resetPermissionManager() {
    if (permissionManagerInstance) {
        permissionManagerInstance.dispose();
    }
    permissionManagerInstance = null;
}
//# sourceMappingURL=permission-config.js.map