/**
 * Bubblewrap argument builder.
 *
 * Pure function: all I/O was done at resolve time.
 * Takes a SandboxState and returns a bwrap CLI argument array.
 */
import { posix } from 'path';
/** /etc entries to individually bind-mount when network is enabled. */
const ETC_ENTRIES_FOR_NETWORK = ['/etc/passwd', '/etc/group', '/etc/hosts', '/etc/ssl', '/etc/apk'];
/** Default PATH inside the sandbox. */
const DEFAULT_PATH = '/usr/local/bin:/usr/bin:/bin';
/**
 * Build bwrap command-line arguments from resolved sandbox state.
 *
 * Pure function — all filesystem probing was done by resolveSandboxConfig().
 */
export function buildBwrapArgs(state, options) {
    const args = [];
    // System mounts (read-only)
    args.push('--ro-bind', '/usr', '/usr');
    args.push('--ro-bind', '/bin', '/bin');
    args.push('--ro-bind', '/lib', '/lib');
    if (state.hasLib64) {
        args.push('--ro-bind', '/lib64', '/lib64');
    }
    // /etc handling: WSL auto-generates /etc/resolv.conf as a symlink to
    // /mnt/wsl/resolv.conf. A plain --ro-bind /etc /etc mounts the static
    // rootfs /etc which doesn't have the symlink target, breaking DNS.
    // When network is needed, mount /etc as tmpfs and bind individual entries.
    if (state.networkMode !== 'none') {
        args.push('--tmpfs', '/etc');
        for (const entry of state.existingEtcEntries) {
            args.push('--ro-bind', entry, entry);
        }
        if (state.resolvedResolvConf) {
            args.push('--ro-bind', state.resolvedResolvConf, '/etc/resolv.conf');
        }
        // Seccomp BPF filter needs to be visible inside the sandbox
        if (state.seccompFilterPath) {
            const seccompDir = posix.dirname(state.seccompFilterPath);
            args.push('--ro-bind', seccompDir, seccompDir);
        }
    }
    else {
        args.push('--ro-bind', '/etc', '/etc');
    }
    // Kernel interfaces
    args.push('--proc', '/proc');
    args.push('--dev', '/dev');
    args.push('--tmpfs', '/tmp');
    // User-configured read-only mounts
    for (const path of state.readMounts) {
        args.push('--ro-bind', path, path);
    }
    // User-configured read-write mounts
    for (const path of state.writeMounts) {
        args.push('--bind', path, path);
    }
    // Mandatory deny paths: read-only overlays on top of writable mounts.
    // bwrap processes args in order, so --ro-bind after --bind takes precedence.
    for (const path of state.denyMounts) {
        args.push('--ro-bind', path, path);
    }
    // Network isolation
    if (state.networkMode === 'none') {
        args.push('--unshare-net');
    }
    else if (state.networkMode === 'proxy') {
        args.push('--unshare-net');
        // Bind-mount proxy Unix sockets into the sandbox
        if (options?.httpSocketPath) {
            args.push('--bind', options.httpSocketPath, options.httpSocketPath);
        }
        if (options?.socksSocketPath) {
            args.push('--bind', options.socksSocketPath, options.socksSocketPath);
        }
    }
    // 'full' mode: no --unshare-net — full unrestricted network
    // Environment variables: default PATH + caller-provided extras
    if (!('PATH' in state.env)) {
        args.push('--setenv', 'PATH', DEFAULT_PATH);
    }
    for (const [key, value] of Object.entries(state.env)) {
        args.push('--setenv', key, value);
    }
    // Cleanup on parent exit
    args.push('--die-with-parent');
    // Working directory (runtime override or resolved default)
    args.push('--chdir', options?.cwd ?? state.workdir);
    return args;
}
export { ETC_ENTRIES_FOR_NETWORK };
//# sourceMappingURL=bwrap.js.map