/**
 * @license
 * Copyright 2026 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { resolveSandboxPaths, } from '../../services/sandboxManager.js';
import { sanitizeEnvironment, getSecureSanitizationConfig, } from '../../services/environmentSanitization.js';
import { buildSeatbeltProfile } from './seatbeltArgsBuilder.js';
import { initializeShellParsers } from '../../utils/shell-utils.js';
import { isKnownSafeCommand, isDangerousCommand, } from '../utils/commandSafety.js';
import { verifySandboxOverrides, getCommandName as getFullCommandName, isStrictlyApproved, } from '../utils/commandUtils.js';
import { parsePosixSandboxDenials, createSandboxDenialCache, } from '../utils/sandboxDenialUtils.js';
import { handleReadWriteCommands } from '../utils/sandboxReadWriteUtils.js';
export class MacOsSandboxManager {
    options;
    denialCache = createSandboxDenialCache();
    constructor(options) {
        this.options = options;
    }
    isKnownSafeCommand(args) {
        const toolName = args[0];
        const approvedTools = this.options.modeConfig?.approvedTools ?? [];
        if (toolName && approvedTools.includes(toolName)) {
            return true;
        }
        return isKnownSafeCommand(args);
    }
    isDangerousCommand(args) {
        return isDangerousCommand(args);
    }
    parseDenials(result) {
        return parsePosixSandboxDenials(result, this.denialCache);
    }
    getWorkspace() {
        return this.options.workspace;
    }
    getOptions() {
        return this.options;
    }
    async prepareCommand(req) {
        await initializeShellParsers();
        const sanitizationConfig = getSecureSanitizationConfig(req.policy?.sanitizationConfig);
        const sanitizedEnv = sanitizeEnvironment(req.env, sanitizationConfig);
        const isReadonlyMode = this.options.modeConfig?.readonly ?? true;
        const allowOverrides = this.options.modeConfig?.allowOverrides ?? true;
        // Reject override attempts in plan mode
        verifySandboxOverrides(allowOverrides, req.policy);
        let command = req.command;
        let args = req.args;
        // Translate virtual commands for sandboxed file system access
        if (command === '__read') {
            command = '/bin/cat';
        }
        else if (command === '__write') {
            command = '/bin/sh';
            args = ['-c', 'cat > "$1"', '_', ...args];
        }
        const currentReq = { ...req, command, args };
        // If not in readonly mode OR it's a strictly approved pipeline, allow workspace writes
        const isApproved = allowOverrides
            ? await isStrictlyApproved(currentReq, this.options.modeConfig?.approvedTools)
            : false;
        const isYolo = this.options.modeConfig?.yolo ?? false;
        const workspaceWrite = !isReadonlyMode || isApproved || isYolo;
        const defaultNetwork = this.options.modeConfig?.network || req.policy?.networkAccess || isYolo;
        // Fetch persistent approvals for this command
        const commandName = await getFullCommandName(currentReq);
        const persistentPermissions = allowOverrides
            ? this.options.policyManager?.getCommandPermissions(commandName)
            : undefined;
        const mergedAdditional = {
            fileSystem: {
                read: [
                    ...(persistentPermissions?.fileSystem?.read ?? []),
                    ...(req.policy?.additionalPermissions?.fileSystem?.read ?? []),
                ],
                write: [
                    ...(persistentPermissions?.fileSystem?.write ?? []),
                    ...(req.policy?.additionalPermissions?.fileSystem?.write ?? []),
                ],
            },
            network: defaultNetwork ||
                persistentPermissions?.network ||
                req.policy?.additionalPermissions?.network ||
                false,
        };
        const { command: finalCommand, args: finalArgs } = handleReadWriteCommands(req, mergedAdditional, this.options.workspace, [
            ...(req.policy?.allowedPaths || []),
            ...(this.options.includeDirectories || []),
        ]);
        const resolvedPaths = await resolveSandboxPaths(this.options, req, mergedAdditional);
        const sandboxArgs = buildSeatbeltProfile({
            resolvedPaths,
            networkAccess: mergedAdditional.network,
            workspaceWrite,
        });
        const tempFile = this.writeProfileToTempFile(sandboxArgs);
        return {
            program: '/usr/bin/sandbox-exec',
            args: ['-f', tempFile, '--', finalCommand, ...finalArgs],
            env: sanitizedEnv,
            cwd: req.cwd,
            cleanup: () => {
                try {
                    fs.unlinkSync(tempFile);
                }
                catch {
                    // Ignore cleanup errors
                }
            },
        };
    }
    writeProfileToTempFile(profile) {
        const tempFile = path.join(os.tmpdir(), `gemini-cli-seatbelt-${Date.now()}-${Math.random().toString(36).slice(2)}.sb`);
        fs.writeFileSync(tempFile, profile, { mode: 0o600 });
        return tempFile;
    }
}
//# sourceMappingURL=MacOsSandboxManager.js.map