import path from 'node:path';
import { config } from '../config.js';
export class SandboxViolationError extends Error {
    constructor(message) {
        super(message);
        this.name = 'SandboxViolationError';
    }
}
/**
 * Resolves `relativePath` against `root` and guarantees the result stays
 * inside `root`. Throws SandboxViolationError on any attempt to escape
 * (e.g. via `..`, absolute paths outside the root, or symlink-style tricks
 * handled at the OS level by the caller when it actually touches the fs).
 */
export function resolveWorkspacePath(root, relativePath) {
    const absoluteRoot = path.resolve(root);
    const target = path.resolve(absoluteRoot, relativePath || '.');
    const normalizedRoot = absoluteRoot.toLowerCase();
    const normalizedTarget = target.toLowerCase();
    if (normalizedTarget !== normalizedRoot && !normalizedTarget.startsWith(normalizedRoot + path.sep)) {
        throw new SandboxViolationError(`Refusing to access "${relativePath}": it resolves outside the workspace root (${absoluteRoot}).`);
    }
    return target;
}
function basenameNoExt(binary) {
    const base = path.basename(binary);
    return base.replace(/\.(exe|cmd|bat|ps1)$/i, '');
}
/**
 * Throws unless `binary` is on the configured allow-list. Command
 * construction elsewhere in the codebase always passes `args` as a separate
 * array (never a single shell string), so this allow-list is the only line
 * of defense needed against arbitrary command execution.
 */
export function assertAllowedBinary(binary) {
    const name = basenameNoExt(binary).toLowerCase();
    const allowed = config.allowedBinaries.some((candidate) => candidate.toLowerCase() === name);
    if (!allowed) {
        throw new SandboxViolationError(`Refusing to run "${binary}": it is not on the allow-list (${config.allowedBinaries.join(', ')}).`);
    }
}
//# sourceMappingURL=sandbox.js.map