"use strict";
/**
 * Remediation sandbox — PR-5a-2 real execution
 *
 * Executes codemod transform functions inside vm.runInNewContext() with a
 * restricted module allowlist. Denies dangerous modules per Shield H-3.
 *
 * Security model (Shield H-3):
 *   - Codemods execute in vm.runInNewContext() with a restricted module
 *     allowlist. Accessing denied modules throws RemediationSandboxViolation.
 *   - ALLOW: ts-morph, path, fs/promises (read-only mode — write is intercepted)
 *   - DENY: child_process, os, dgram, cluster, worker_threads, repl,
 *           net, tls, http, https, crypto (raw), vm (no re-entry)
 *   - Sandbox timeout: 5 seconds (configurable via SandboxContext.timeoutMs)
 *
 * @connexum/ai-governance PR-5a-2
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.runInSandbox = runInSandbox;
exports.buildRestrictedRequireForTest = buildRestrictedRequireForTest;
const vm = __importStar(require("vm"));
const path = __importStar(require("path"));
const fsPromises = __importStar(require("fs/promises"));
const types_1 = require("./types");
// ---------------------------------------------------------------------------
// Denied module list
// ---------------------------------------------------------------------------
/** Modules that are NEVER allowed inside a codemod sandbox. */
const DENIED_MODULES = new Set([
    'child_process',
    'os',
    'dgram',
    'cluster',
    'worker_threads',
    'repl',
    'net',
    'tls',
    'http',
    'https',
    'vm', // no re-entry
    'readline',
    'stream',
    'crypto', // raw crypto — use helpers instead
]);
// ---------------------------------------------------------------------------
// Sandbox executor
// ---------------------------------------------------------------------------
/**
 * Execute a codemod payload string inside a restricted vm context.
 *
 * The payload must be a CommonJS-style script. The transform function
 * is invoked as:
 *   module.exports.transform(sourceFile, helpers)  — for ts-morph
 *   module.exports.transform(sourceCode, helpers)  — for Python-style string transforms
 *
 * The sandbox provides:
 *   - require: restricted to ts-morph, path, fs (read-only proxy)
 *   - path: Node.js path module
 *   - fs: read-only proxy over fs/promises
 *   - console.log/warn/error: captured to logs[]
 *   - helpers: wrapToolCall, addImport, log, writeFile (captured to pendingWrites)
 *   - module / exports: CommonJS module object
 *
 * @param payload  The transform function source string (embedded in pack).
 * @param context  Execution context (agentDir, timeoutMs, dryRun).
 */
async function runInSandbox(payload, context) {
    const logs = [];
    const pendingWrites = [];
    const timeoutMs = context.timeoutMs ?? 5000;
    // Build the restricted require function that enforces the deny-list.
    const restrictedRequire = buildRestrictedRequire(DENIED_MODULES);
    // CommonJS module/exports object so the payload can use module.exports
    const moduleObj = { exports: {} };
    // Build the sandbox globals.
    const sandboxGlobals = vm.createContext({
        require: restrictedRequire,
        path,
        fs: buildReadOnlyFsProxy(fsPromises, context),
        console: {
            log: (...args) => logs.push(args.map(String).join(' ')),
            warn: (...args) => logs.push('[warn] ' + args.map(String).join(' ')),
            error: (...args) => logs.push('[error] ' + args.map(String).join(' ')),
        },
        helpers: buildHelpers(logs, pendingWrites, context),
        module: moduleObj,
        exports: moduleObj.exports,
        // Explicitly deny dangerous globals
        process: undefined,
        global: undefined,
        globalThis: undefined,
        __filename: '<codemod>',
        __dirname: context.agentDir,
    });
    // Compile and execute the payload in the restricted context.
    let script;
    try {
        script = new vm.Script(payload, { filename: '<codemod>' });
    }
    catch (err) {
        throw new types_1.RemediationSandboxViolation(`Codemod syntax error: ${err.message}`);
    }
    try {
        script.runInContext(sandboxGlobals, { timeout: timeoutMs });
    }
    catch (err) {
        if (err instanceof types_1.RemediationSandboxViolation)
            throw err;
        if (err instanceof Error && err.message.includes('Script execution timed out')) {
            throw new types_1.RemediationSandboxViolation(`Codemod execution timed out after ${timeoutMs}ms`);
        }
        throw new types_1.RemediationSandboxViolation(`Codemod execution error: ${err.message}`);
    }
    return { returnValue: moduleObj.exports, pendingWrites, logs };
}
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
/**
 * Build the restricted require function for the sandbox.
 * Exported for testing; callers outside sandbox.ts should not use this.
 * @internal
 */
function buildRestrictedRequireForTest(denied = DENIED_MODULES) {
    return buildRestrictedRequire(denied);
}
function buildRestrictedRequire(denied) {
    return function restrictedRequire(moduleName) {
        // Strip version qualifiers and node: prefix for the check
        const base = moduleName.replace(/^node:/, '').split('/')[0];
        if (denied.has(base)) {
            throw new types_1.RemediationSandboxViolation(`Module '${moduleName}' is not allowed in codemod sandbox. ` +
                `Denied list: ${[...denied].join(', ')}`);
        }
        // Allow: path (built-in)
        if (base === 'path')
            return path;
        // Allow: fs or fs/promises — return read-only proxy
        if (base === 'fs')
            return buildReadOnlyFsProxy(fsPromises, { agentDir: '' });
        // Allow: ts-morph
        if (base === 'ts-morph') {
            // eslint-disable-next-line @typescript-eslint/no-require-imports
            return require('ts-morph');
        }
        throw new types_1.RemediationSandboxViolation(`Module '${moduleName}' is not in the codemod sandbox allowlist. ` +
            `Allowed: path, fs/promises, ts-morph`);
    };
}
function buildReadOnlyFsProxy(fsP, _context) {
    return {
        readFile: fsP.readFile.bind(fsP),
        readdir: fsP.readdir.bind(fsP),
        stat: fsP.stat.bind(fsP),
        // write operations are intentionally omitted — use helpers.writeFile instead
    };
}
function buildHelpers(logs, pendingWrites, context) {
    return {
        log: (msg) => logs.push(String(msg)),
        /**
         * Capture a file write. In dry-run mode, only adds to pendingWrites.
         * In live mode, writes to disk AND adds to pendingWrites (for diff display).
         */
        writeFile: async (relPath, content) => {
            const absPath = path.resolve(context.agentDir, relPath);
            pendingWrites.push({ filePath: absPath, content });
            if (!context.dryRun) {
                await fsPromises.writeFile(absPath, content, 'utf-8');
            }
        },
        /**
         * Wrap a tool call expression with GovernedToolExecutor.
         * Used by ts-morph codemods to transform raw tool invocations.
         * The actual transformation is done by the codemod's ts-morph script;
         * this helper provides a standard naming convention.
         */
        wrapToolCall: (callExpr, wrapperStyle) => {
            logs.push(`[helpers.wrapToolCall] expr="${callExpr}" style="${wrapperStyle}"`);
            return `GovernedToolExecutor.current().invoke(${callExpr}, ${wrapperStyle})`;
        },
        /**
         * Add an import statement hint (logged for display in dry-run diff).
         */
        addImport: (stmt) => {
            logs.push(`[helpers.addImport] ${stmt}`);
        },
    };
}
//# sourceMappingURL=sandbox.js.map