blob: 79a9af6fe607a6b75ee7930d6a30a0b0334ba939 [file]
import assert from 'node:assert/strict';
import { describe, it } from 'node:test';
import { mkdir, mkdtemp } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import {
createDangerFullAccessPermissionProfile,
createReadOnlyPermissionProfile,
createWorkspaceWritePermissionProfile,
type PermissionProfile,
} from '@maka/core/permission-profile';
import {
LinuxBubblewrapBackend,
buildBubblewrapArgv,
buildNetworkSeccompFilter,
discoverNestedProtectedMetadataPaths,
linuxExecutableRoots,
} from '../sandbox/linux-sandbox.js';
import { detectLinuxSandboxCapability } from '../sandbox/linux-capability.js';
import {
LINUX_BWRAP_PROBE_ARGS,
LINUX_BWRAP_REQUIRED_OPTIONS,
} from '../sandbox/linux-capability.js';
import type { SandboxPathContext, SandboxTransformRequest } from '../sandbox/types.js';
function workspaceRequest(profile: PermissionProfile): SandboxTransformRequest {
return {
platform: 'linux',
command: {
program: '/bin/sh',
args: ['-lc', 'echo hi'],
cwd: '/repo/project',
profile,
pathContext: {
workspaceRoots: ['/repo/project'],
tmpdir: '/var/tmp/maka',
slashTmp: '/tmp',
},
},
};
}
function enabledNetworkProfile(): PermissionProfile {
const profile = createWorkspaceWritePermissionProfile();
return { ...profile, network: { kind: 'enabled' } };
}
function deniedChildProfile(): PermissionProfile {
const profile = createWorkspaceWritePermissionProfile();
return {
...profile,
fileSystem: {
...profile.fileSystem,
entries: [
...profile.fileSystem.entries,
{ kind: 'path', access: 'deny', path: '/repo/project/secret' },
],
},
};
}
describe('detectLinuxSandboxCapability', () => {
it('probes every namespace used by production and requires seccomp support', () => {
for (const flag of [
'--unshare-user',
'--unshare-pid',
'--unshare-ipc',
'--unshare-uts',
'--unshare-cgroup',
'--unshare-net',
]) {
assert.ok((LINUX_BWRAP_PROBE_ARGS as readonly string[]).includes(flag));
}
assert.ok(LINUX_BWRAP_REQUIRED_OPTIONS.includes('--seccomp'));
});
it('reports non-Linux platforms without probing bwrap', () => {
assert.deepEqual(detectLinuxSandboxCapability({ platform: 'win32' }), {
available: false,
reason: 'non-linux',
});
});
it('reports a missing configured bwrap executable', () => {
assert.deepEqual(
detectLinuxSandboxCapability({
platform: 'linux',
bwrapPath: '/definitely/missing/maka-bwrap',
}),
{
available: false,
reason: 'missing-bwrap',
bwrapPath: '/definitely/missing/maka-bwrap',
},
);
});
it('reports an executable that cannot create the bubblewrap sandbox', () => {
const capability = detectLinuxSandboxCapability({
platform: 'linux',
bwrapPath: process.execPath,
});
assert.equal(capability.available, false);
if (!capability.available) {
assert.equal(capability.reason, 'probe-failed');
assert.equal(capability.bwrapPath, process.execPath);
}
});
});
describe('buildBubblewrapArgv', () => {
it('mounts read-only profiles without a writable workspace bind', () => {
const request = workspaceRequest(createReadOnlyPermissionProfile());
const argv = buildBubblewrapArgv({ bwrapPath: '/usr/bin/bwrap', command: request.command });
assert.ok(hasTriple(argv, '--ro-bind', '/repo/project', '/repo/project'));
assert.equal(hasTriple(argv, '--bind', '/repo/project', '/repo/project'), false);
});
it('materializes workspace, temp, protected metadata, cwd, and network restrictions', () => {
const request = workspaceRequest(createWorkspaceWritePermissionProfile());
const argv = buildBubblewrapArgv({
bwrapPath: '/usr/bin/bwrap',
command: request.command,
});
assert.equal(argv[0], '/usr/bin/bwrap');
assert.ok(argv.includes('--die-with-parent'));
assert.ok(argv.includes('--new-session'));
assert.ok(argv.includes('--unshare-net'));
assert.ok(argv.includes('--unshare-user'));
assert.ok(hasPair(argv, '--seccomp', '3'));
assert.ok(hasTriple(argv, '--bind', '/repo/project', '/repo/project'));
assert.ok(hasTriple(argv, '--ro-bind-try', '/repo/project/.git', '/repo/project/.git'));
assert.ok(hasPair(argv, '--tmpfs', '/tmp'));
assert.ok(hasPair(argv, '--tmpfs', '/var/tmp/maka'));
assert.ok(hasPair(argv, '--chdir', '/repo/project'));
assert.deepEqual(argv.slice(-4), ['--', '/bin/sh', '-lc', 'echo hi']);
});
it('keeps the host network namespace when network is enabled', () => {
const request = workspaceRequest(enabledNetworkProfile());
const argv = buildBubblewrapArgv({ bwrapPath: '/usr/bin/bwrap', command: request.command });
assert.equal(argv.includes('--unshare-net'), false);
assert.equal(argv.includes('--seccomp'), false);
});
it('mounts an absolute program directory outside the default host paths', () => {
const request = workspaceRequest(createWorkspaceWritePermissionProfile());
const programDirectory = '/opt/hostedtoolcache/node/22.23.1/x64/bin';
const argv = buildBubblewrapArgv({
bwrapPath: '/usr/bin/bwrap',
command: {
...request.command,
program: `${programDirectory}/node`,
},
});
assert.ok(hasPair(argv, '--dir', '/opt'));
assert.ok(hasPair(argv, '--dir', '/opt/hostedtoolcache/node/22.23.1/x64'));
assert.ok(hasTriple(argv, '--ro-bind', programDirectory, programDirectory));
});
it('mounts runtime roots needed by a shell-launched executable', () => {
const request = workspaceRequest(createWorkspaceWritePermissionProfile());
const runtimeRoot = '/opt/hostedtoolcache/node/22.23.1/x64';
const argv = buildBubblewrapArgv({
bwrapPath: '/usr/bin/bwrap',
command: {
...request.command,
pathContext: {
...request.command.pathContext,
minimalRoots: [runtimeRoot],
},
},
});
assert.ok(hasPair(argv, '--dir', '/opt'));
assert.ok(hasTriple(argv, '--ro-bind-try', runtimeRoot, runtimeRoot));
});
it('mounts required worker resources and uses a trusted parent for a missing exact write', () => {
const profile: PermissionProfile = {
type: 'managed',
name: 'custom',
fileSystem: {
kind: 'restricted',
entries: [{ kind: 'path', access: 'write', path: '/outside/new.txt', match: 'exact' }],
},
network: { kind: 'restricted' },
};
const request = workspaceRequest(profile);
const pathContext: SandboxPathContext = {
...request.command.pathContext,
runtimeReadableRoots: ['/runtime/filesystem-worker.js'],
executableRoots: ['/opt/node/bin/node', '/opt/rg/bin/rg'],
runtimeWritableRoots: ['/outside'],
};
const argv = buildBubblewrapArgv({
bwrapPath: '/usr/bin/bwrap',
command: {
...request.command,
pathContext,
},
});
assert.ok(
hasTriple(
argv,
'--ro-bind',
'/runtime/filesystem-worker.js',
'/runtime/filesystem-worker.js',
),
);
assert.ok(hasTriple(argv, '--ro-bind', '/opt/node/bin/node', '/opt/node/bin/node'));
assert.ok(hasTriple(argv, '--ro-bind', '/opt/rg/bin/rg', '/opt/rg/bin/rg'));
assert.ok(hasTriple(argv, '--bind', '/outside', '/outside'));
assert.equal(hasTriple(argv, '--bind', '/outside/new.txt', '/outside/new.txt'), false);
});
it('binds pinned exact-write files through inherited descriptors', () => {
const request = workspaceRequest({
type: 'managed',
name: 'custom',
fileSystem: {
kind: 'restricted',
entries: [{ kind: 'path', access: 'write', path: '/outside/new.txt', match: 'exact' }],
},
network: { kind: 'restricted' },
});
const command = {
...request.command,
pathContext: {
...request.command.pathContext,
pinnedWritableFiles: [{ path: '/outside/new.txt', fd: 4, sourceFd: 27 }],
},
} as SandboxTransformRequest['command'];
const backend = new LinuxBubblewrapBackend({
capability: { available: true, bwrapPath: '/usr/bin/bwrap' },
});
const argv = buildBubblewrapArgv({ bwrapPath: '/usr/bin/bwrap', command });
assert.ok(hasTriple(argv, '--bind', '/proc/self/fd/4', '/outside/new.txt'));
assert.equal(hasTriple(argv, '--bind', '/outside/new.txt', '/outside/new.txt'), false);
const transformed = backend.transform({ platform: 'linux', command });
assert.equal(transformed.ok, true);
if (!transformed.ok) throw new Error('Linux transform failed');
assert.deepEqual(
transformed.exec.fdInputs?.find(
(input) => (input as { sourceFd?: number }).sourceFd !== undefined,
),
{ fd: 4, sourceFd: 27 },
);
});
it('materializes an otherwise-unmounted worker cwd without exposing its contents', () => {
const profile: PermissionProfile = {
type: 'managed',
name: 'custom',
fileSystem: {
kind: 'restricted',
entries: [{ kind: 'path', access: 'read', path: '/outside/allowed.txt', match: 'exact' }],
},
network: { kind: 'restricted' },
};
const request = workspaceRequest(profile);
const argv = buildBubblewrapArgv({
bwrapPath: '/usr/bin/bwrap',
command: {
...request.command,
cwd: '/workspace/session',
},
});
assert.ok(hasPair(argv, '--dir', '/workspace/session'));
assert.equal(hasTriple(argv, '--ro-bind', '/workspace/session', '/workspace/session'), false);
assert.equal(hasTriple(argv, '--bind', '/workspace/session', '/workspace/session'), false);
});
});
describe('linuxExecutableRoots', () => {
it('keeps the Node installation root and absolute PATH entries without nested duplicates', () => {
assert.deepEqual(
linuxExecutableRoots({
execPath: '/opt/hostedtoolcache/node/22.23.1/x64/bin/node',
path: '/opt/hostedtoolcache/node/22.23.1/x64/bin:/home/runner/.local/bin:relative-bin',
}),
['/opt/hostedtoolcache/node/22.23.1/x64', '/home/runner/.local/bin'],
);
});
});
describe('buildNetworkSeccompFilter', () => {
it('builds a cBPF program for supported Linux architectures', () => {
const x64 = buildNetworkSeccompFilter('x64');
const arm64 = buildNetworkSeccompFilter('arm64');
assert.ok(x64.length > 0);
assert.equal(x64.length % 8, 0);
assert.ok(arm64.length > 0);
assert.equal(arm64.length % 8, 0);
assert.notDeepEqual(x64, arm64);
});
it('fails closed for architectures without audited syscall numbers', () => {
assert.throws(() => buildNetworkSeccompFilter('ia32'), /unsupported.*architecture/i);
});
});
describe('discoverNestedProtectedMetadataPaths', () => {
it('finds protected metadata at any existing nested path segment', async () => {
const root = await mkdtemp(join(tmpdir(), 'maka-protected-scan-'));
await mkdir(join(root, 'packages', 'pkg', '.git'), { recursive: true });
await mkdir(join(root, '.git'), { recursive: true });
const paths = discoverNestedProtectedMetadataPaths({
writableRoots: [root],
names: ['.git', '.agents', '.codex'],
});
assert.equal(paths.length, 1);
assert.match(paths[0] ?? '', /packages[/\\]pkg[/\\]\.git$/);
});
});
describe('LinuxBubblewrapBackend', () => {
it('wraps a managed restricted command when bwrap is available', () => {
const backend = new LinuxBubblewrapBackend({
capability: { available: true, bwrapPath: '/usr/bin/bwrap' },
});
const result = backend.transform(workspaceRequest(createWorkspaceWritePermissionProfile()));
assert.equal(result.ok, true);
if (result.ok) {
assert.equal(result.exec.sandboxType, 'linux');
assert.equal(result.exec.argv[0], '/usr/bin/bwrap');
assert.deepEqual(result.exec.argv.slice(-3), ['/bin/sh', '-lc', 'echo hi']);
assert.equal(result.exec.fdInputs?.[0]?.fd, 3);
const seccompInput = result.exec.fdInputs?.[0];
assert.ok(seccompInput && 'data' in seccompInput && seccompInput.data.byteLength > 0);
}
});
it('re-applies discovered nested protected metadata as read-only', () => {
const nested = '/repo/project/packages/pkg/.git';
const backend = new LinuxBubblewrapBackend({
capability: { available: true, bwrapPath: '/usr/bin/bwrap' },
discoverProtectedMetadataPaths: () => [nested],
});
const result = backend.transform(workspaceRequest(createWorkspaceWritePermissionProfile()));
assert.equal(result.ok, true);
if (result.ok) {
assert.ok(hasTriple(result.exec.argv, '--ro-bind', nested, nested));
}
});
it('fails closed with a clear result when bwrap is unavailable', () => {
const backend = new LinuxBubblewrapBackend({
capability: { available: false, reason: 'missing-bwrap', bwrapPath: '/usr/bin/bwrap' },
});
const result = backend.transform(workspaceRequest(createWorkspaceWritePermissionProfile()));
assert.equal(result.ok, false);
if (!result.ok) {
assert.equal(result.reason, 'backend_not_available');
assert.equal(result.sandboxType, 'linux');
assert.match(result.message ?? '', /bubblewrap.*not available/i);
}
});
it('rejects deny entries that cannot be represented without weakening policy', () => {
const backend = new LinuxBubblewrapBackend({
capability: { available: true, bwrapPath: '/usr/bin/bwrap' },
});
const result = backend.transform(workspaceRequest(deniedChildProfile()));
assert.equal(result.ok, false);
if (!result.ok) {
assert.equal(result.reason, 'invalid_request');
assert.match(result.message ?? '', /deny entries/i);
}
});
it('rejects profiles that should have selected no sandbox', () => {
const backend = new LinuxBubblewrapBackend({
capability: { available: true, bwrapPath: '/usr/bin/bwrap' },
});
const result = backend.transform(workspaceRequest(createDangerFullAccessPermissionProfile()));
assert.equal(result.ok, false);
if (!result.ok) assert.equal(result.reason, 'invalid_request');
});
});
function hasPair(argv: readonly string[], flag: string, value: string): boolean {
return argv.some((item, index) => item === flag && argv[index + 1] === value);
}
function hasTriple(argv: readonly string[], flag: string, left: string, right: string): boolean {
return argv.some(
(item, index) => item === flag && argv[index + 1] === left && argv[index + 2] === right,
);
}