/**
 * @license
 * Copyright 2026 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import fs from 'node:fs';
import path from 'node:path';
import os from 'node:os';
import { fileURLToPath } from 'node:url';
import { GOVERNANCE_FILES, sanitizePaths, } from '../../services/sandboxManager.js';
import { sanitizeEnvironment, getSecureSanitizationConfig, } from '../../services/environmentSanitization.js';
import { debugLogger } from '../../utils/debugLogger.js';
import { spawnAsync } from '../../utils/shell-utils.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
/**
 * A SandboxManager implementation for Windows that uses Restricted Tokens,
 * Job Objects, and Low Integrity levels for process isolation.
 * Uses a native C# helper to bypass PowerShell restrictions.
 */
export class WindowsSandboxManager {
    options;
    helperPath;
    initialized = false;
    lowIntegrityCache = new Set();
    constructor(options) {
        this.options = options;
        this.helperPath = path.resolve(__dirname, 'GeminiSandbox.exe');
    }
    /**
     * Ensures a file or directory exists.
     */
    touch(filePath, isDirectory) {
        try {
            // If it exists (even as a broken symlink), do nothing
            if (fs.lstatSync(filePath))
                return;
        }
        catch {
            // Ignore ENOENT
        }
        if (isDirectory) {
            fs.mkdirSync(filePath, { recursive: true });
        }
        else {
            const dir = path.dirname(filePath);
            if (!fs.existsSync(dir)) {
                fs.mkdirSync(dir, { recursive: true });
            }
            fs.closeSync(fs.openSync(filePath, 'a'));
        }
    }
    async ensureInitialized() {
        if (this.initialized)
            return;
        if (os.platform() !== 'win32') {
            this.initialized = true;
            return;
        }
        try {
            if (!fs.existsSync(this.helperPath)) {
                debugLogger.log(`WindowsSandboxManager: Helper not found at ${this.helperPath}. Attempting to compile...`);
                // If the exe doesn't exist, we try to compile it from the .cs file
                const sourcePath = this.helperPath.replace(/\.exe$/, '.cs');
                if (fs.existsSync(sourcePath)) {
                    const systemRoot = process.env['SystemRoot'] || 'C:\\Windows';
                    const cscPaths = [
                        'csc.exe', // Try in PATH first
                        path.join(systemRoot, 'Microsoft.NET', 'Framework64', 'v4.0.30319', 'csc.exe'),
                        path.join(systemRoot, 'Microsoft.NET', 'Framework', 'v4.0.30319', 'csc.exe'),
                        // Added newer framework paths
                        path.join(systemRoot, 'Microsoft.NET', 'Framework64', 'v4.8', 'csc.exe'),
                        path.join(systemRoot, 'Microsoft.NET', 'Framework', 'v4.8', 'csc.exe'),
                        path.join(systemRoot, 'Microsoft.NET', 'Framework64', 'v3.5', 'csc.exe'),
                    ];
                    let compiled = false;
                    for (const csc of cscPaths) {
                        try {
                            debugLogger.log(`WindowsSandboxManager: Trying to compile using ${csc}...`);
                            // We use spawnAsync but we don't need to capture output
                            await spawnAsync(csc, ['/out:' + this.helperPath, sourcePath]);
                            debugLogger.log(`WindowsSandboxManager: Successfully compiled sandbox helper at ${this.helperPath}`);
                            compiled = true;
                            break;
                        }
                        catch (e) {
                            debugLogger.log(`WindowsSandboxManager: Failed to compile using ${csc}: ${e instanceof Error ? e.message : String(e)}`);
                        }
                    }
                    if (!compiled) {
                        debugLogger.log('WindowsSandboxManager: Failed to compile sandbox helper from any known CSC path.');
                    }
                }
                else {
                    debugLogger.log(`WindowsSandboxManager: Source file not found at ${sourcePath}. Cannot compile helper.`);
                }
            }
            else {
                debugLogger.log(`WindowsSandboxManager: Found helper at ${this.helperPath}`);
            }
        }
        catch (e) {
            debugLogger.log('WindowsSandboxManager: Failed to initialize sandbox helper:', e);
        }
        this.initialized = true;
    }
    /**
     * Prepares a command for sandboxed execution on Windows.
     */
    async prepareCommand(req) {
        await this.ensureInitialized();
        const sanitizationConfig = getSecureSanitizationConfig(req.policy?.sanitizationConfig);
        const sanitizedEnv = sanitizeEnvironment(req.env, sanitizationConfig);
        // 1. Handle filesystem permissions for Low Integrity
        // Grant "Low Mandatory Level" write access to the workspace.
        await this.grantLowIntegrityAccess(this.options.workspace);
        // Grant "Low Mandatory Level" read access to allowedPaths.
        const allowedPaths = sanitizePaths(req.policy?.allowedPaths) || [];
        for (const allowedPath of allowedPaths) {
            await this.grantLowIntegrityAccess(allowedPath);
        }
        // TODO: handle forbidden paths
        // 2. Protected governance files
        // These must exist on the host before running the sandbox to prevent
        // the sandboxed process from creating them with Low integrity.
        // By being created as Medium integrity, they are write-protected from Low processes.
        for (const file of GOVERNANCE_FILES) {
            const filePath = path.join(this.options.workspace, file.path);
            this.touch(filePath, file.isDirectory);
            // We resolve real paths to ensure protection for both the symlink and its target.
            try {
                const realPath = fs.realpathSync(filePath);
                if (realPath !== filePath) {
                    // If it's a symlink, the target is already implicitly protected
                    // if it's outside the Low integrity workspace (likely Medium).
                    // If it's inside, we ensure it's not accidentally Low.
                }
            }
            catch {
                // Ignore realpath errors
            }
        }
        // 3. Construct the helper command
        // GeminiSandbox.exe <network:0|1> <cwd> <command> [args...]
        const program = this.helperPath;
        // If the command starts with __, it's an internal command for the sandbox helper itself.
        const args = [
            req.policy?.networkAccess ? '1' : '0',
            req.cwd,
            req.command,
            ...req.args,
        ];
        return {
            program,
            args,
            env: sanitizedEnv,
        };
    }
    /**
     * Grants "Low Mandatory Level" access to a path using icacls.
     */
    async grantLowIntegrityAccess(targetPath) {
        if (os.platform() !== 'win32') {
            return;
        }
        const resolvedPath = path.resolve(targetPath);
        if (this.lowIntegrityCache.has(resolvedPath)) {
            return;
        }
        // Never modify integrity levels for system directories
        const systemRoot = process.env['SystemRoot'] || 'C:\\Windows';
        const programFiles = process.env['ProgramFiles'] || 'C:\\Program Files';
        const programFilesX86 = process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)';
        if (resolvedPath.toLowerCase().startsWith(systemRoot.toLowerCase()) ||
            resolvedPath.toLowerCase().startsWith(programFiles.toLowerCase()) ||
            resolvedPath.toLowerCase().startsWith(programFilesX86.toLowerCase())) {
            return;
        }
        try {
            await spawnAsync('icacls', [resolvedPath, '/setintegritylevel', 'Low']);
            this.lowIntegrityCache.add(resolvedPath);
        }
        catch (e) {
            debugLogger.log('WindowsSandboxManager: icacls failed for', resolvedPath, e);
        }
    }
}
//# sourceMappingURL=WindowsSandboxManager.js.map