Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
3bfda10
fix(runtime): allow bounded Apple Git startup
Dante-dan Sep 13, 2026
7894f8b
fix(runtime): tighten Apple toolchain grants
Dante-dan Sep 14, 2026
d7ceeb1
fix(runtime): require Apple signer for developer roots
Dante-dan Sep 17, 2026
de5757c
docs: register Apple signer platform test
Dante-dan Sep 17, 2026
5957562
chore: merge main and refresh Windows test inventory
Dante-dan Sep 18, 2026
78f2785
test(runtime): cover bounded Apple toolchain probes
Dante-dan Sep 19, 2026
c6d7549
chore: merge main and refresh Windows test inventory
Dante-dan Sep 22, 2026
54e7219
chore: merge main and refresh Windows test inventory
Dante-dan Sep 26, 2026
dca830e
chore: sync latest main
Dante-dan Sep 26, 2026
02489b5
chore: sync latest main
Dante-dan Sep 26, 2026
24fd9fc
chore: sync latest main
Dante-dan Sep 26, 2026
ab11ad1
fix(runtime): keep Apple toolchain roots within selected bundle
Dante-dan Sep 28, 2026
9f8d3d7
chore: sync latest main
Dante-dan Sep 28, 2026
d5ffe03
chore: sync current main and regenerate Windows test inventory
Dante-dan Sep 30, 2026
22af4d5
fix(runtime): await Apple toolchain verification without blocking
Dante-dan Sep 30, 2026
5b15444
chore: merge main and regenerate Windows test inventory
Dante-dan Oct 3, 2026
5e99752
fix(runtime): bound Apple toolchain probes and accept Xcode bundles
Dante-dan Oct 4, 2026
57229ba
fix(runtime): explain failed Apple toolchain discovery
Dante-dan Oct 4, 2026
213c4ad
fix(runtime): deduplicate Apple toolchain diagnostics
Dante-dan Oct 4, 2026
5ab3b10
chore: merge main and refresh Windows test inventory
Dante-dan Oct 12, 2026
cfb655e
chore: merge main and regenerate Windows test inventory
Dante-dan Oct 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/windows-test-inventory.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ Locations intentionally omit line numbers so unrelated edits do not invalidate t
|---|---:|
| windows-backend-gap | 27 |
| portable-candidate | 35 |
| platform-contract | 38 |
| platform-contract | 39 |

Total Windows-excluded declarations: **100**
Total Windows-excluded declarations: **101**

## Inventory

Expand Down Expand Up @@ -77,6 +77,7 @@ Total Windows-excluded declarations: **100**
| platform-contract | `packages/runtime/src/__tests__/filesystem-worker-process-runner.test.ts` filesystem worker rejects boundedly when a detached descendant retains stdout | `process.platform === 'win32' ? 'POSIX detached process-group semantics required' : false` |
| platform-contract | `packages/runtime/src/__tests__/filesystem-worker-smoke.test.ts` macOS filesystem worker smoke | `process.platform !== 'darwin'` |
| platform-contract | `packages/runtime/src/__tests__/glob-search.test.ts` both Glob paths report permission failures and recover after permissions are restored | `process.platform === 'win32' \|\| process.getuid?.() === 0` |
| platform-contract | `packages/runtime/src/__tests__/macos-command-paths.test.ts` Apple signer validation | `process.platform !== 'darwin'` |
| portable-candidate | `packages/runtime/src/__tests__/node-pty-write-lifecycle.test.ts` does not carry queued Unix PTY writes past native exit | `process.platform === 'win32' ? 'Unix PTY file-descriptor lifecycle only' : false` |
| portable-candidate | `packages/runtime/src/__tests__/shell-exec.test.ts` writes a legacy WSL Bash command through stdin | `process.platform === 'win32' ? 'uses /bin/sh as a portable stdin probe' : false` |
| platform-contract | `packages/runtime/src/__tests__/shell-exec.test.ts` bounds output drain after the root exits while a detached descendant retains stdout | `process.platform === 'win32' ? 'POSIX detached process-group semantics required' : false` |
Expand Down
65 changes: 65 additions & 0 deletions packages/runtime/src/__tests__/builtin-tools.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1528,6 +1528,71 @@ describe('builtin Bash streaming output', () => {
}
});

test('does not turn Git config references into host-discovered sandbox grants', async () => {
const root = await mkdtemp(join(tmpdir(), 'maka-bash-no-git-config-scan-'));
try {
const config = join(root, 'outside.config');
await writeFile(config, `[include]\n\tpath = ${join(root, 'included.config')}\n`);
let input: Parameters<ShellRunLauncher['runForegroundBash']>[0] | undefined;
const shellRuns: ShellRunLauncher = {
async runForegroundBash(value) {
input = value;
return {
kind: 'terminal',
cwd: value.cwd,
cmd: value.command,
status: 'completed',
exitCode: 0,
output: {
mode: 'pipes',
stdout: '',
stderr: '',
stdoutTruncated: false,
stderrTruncated: false,
redacted: false,
},
};
},
async runBackgroundBash() {
throw new Error('not used');
},
};
const bash = buildBuiltinTools({
shellRuns,
shellEnvironment: { GIT_CONFIG_GLOBAL: config },
permissionProfile: createWorkspaceWritePermissionProfile(),
sandboxManager: new SandboxManager([new MacosSeatbeltBackend()]),
sandboxPlatform: 'darwin',
}).find((candidate) => candidate.name === 'Bash');
if (!bash) throw new Error('Bash tool missing');

await bash.impl(
{ command: '/usr/bin/git status --short' },
{
sessionId: 'session-1',
turnId: 'turn-1',
toolCallId: 'tool-1',
cwd: root,
permissionMode: 'ask',
abortSignal: new AbortController().signal,
emitOutput: () => {},
},
);

assert.ok(input?.argv);
assert.equal(
input.argv.some((argument) => argument.includes(config)),
false,
);
assert.equal(
input.argv.some((argument) => argument.includes('RUNTIME_READABLE_FILE')),
false,
);
} finally {
await rm(root, { recursive: true, force: true });
}
});

test('Read treats runtime background task refs as whole resources', async () => {
const calls: unknown[] = [];
const runtimeResources = {
Expand Down
287 changes: 287 additions & 0 deletions packages/runtime/src/__tests__/macos-command-paths.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import { describe, it } from 'node:test';
import {
mkdirSync,
mkdtempSync,
realpathSync,
rmSync,
symlinkSync,
unlinkSync,
writeFileSync,
} from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';

import {
createReadOnlyPermissionProfile,
createWorkspaceWritePermissionProfile,
} from '@maka/core/permission-profile';

import {
resolveMacosCommandPaths,
resolveMacosDeveloperExecutableRoots,
type MacosDeveloperCommandRunner,
} from '../sandbox/macos-command-paths.js';

describe('resolveMacosDeveloperExecutableRoots', () => {
it('accepts canonical and symlinked CommandLineTools layouts', () => {
const scratch = mkdtempSync(join(tmpdir(), 'maka-clt-'));
const developer = join(scratch, 'CommandLineTools');
const library = join(developer, 'usr', 'lib');
const alias = join(scratch, 'selected');
mkdirSync(library, { recursive: true });
writeFileSync(join(library, 'libxcrun.dylib'), 'fixture');
symlinkSync(developer, alias);
try {
assert.deepEqual(
resolveMacosDeveloperExecutableRoots({
developerDir: developer,
validateAppleBinary: () => true,
}),
[realpathSync(library)],
);
assert.deepEqual(
resolveMacosDeveloperExecutableRoots({
developerDir: alias,
validateAppleBinary: () => true,
}),
[realpathSync(library)],
);
} finally {
rmSync(scratch, { recursive: true, force: true });
}
});

it('accepts only the library and SharedFrameworks directories from an Xcode layout', () => {
const scratch = mkdtempSync(join(tmpdir(), 'maka-xcode-'));
const contents = join(scratch, 'Xcode-beta.app', 'Contents');
const developer = join(contents, 'Developer');
const library = join(developer, 'usr', 'lib');
const frameworks = join(contents, 'SharedFrameworks');
mkdirSync(library, { recursive: true });
mkdirSync(frameworks);
writeFileSync(join(library, 'libxcrun.dylib'), 'fixture');
try {
assert.deepEqual(
resolveMacosDeveloperExecutableRoots({
developerDir: developer,
validateAppleBinary: () => true,
}),
[realpathSync(library), realpathSync(frameworks)],
);
} finally {
rmSync(scratch, { recursive: true, force: true });
}
});

it('rejects root, home, ordinary directories, and unresolved symlinks', () => {
const scratch = mkdtempSync(join(tmpdir(), 'maka-invalid-developer-'));
const ordinary = join(scratch, 'ordinary');
const dangling = join(scratch, 'dangling');
mkdirSync(ordinary);
symlinkSync(join(scratch, 'missing'), dangling);
try {
assert.deepEqual(resolveMacosDeveloperExecutableRoots({ developerDir: '/' }), []);
assert.deepEqual(
resolveMacosDeveloperExecutableRoots({ developerDir: scratch, homeDir: scratch }),
[],
);
assert.deepEqual(resolveMacosDeveloperExecutableRoots({ developerDir: ordinary }), []);
assert.deepEqual(resolveMacosDeveloperExecutableRoots({ developerDir: dangling }), []);
} finally {
rmSync(scratch, { recursive: true, force: true });
}
});

it('uses DEVELOPER_DIR before consulting xcode-select', () => {
let selected = false;
resolveMacosDeveloperExecutableRoots({
developerDir: '/',
selectDeveloperDir: () => {
selected = true;
return undefined;
},
});
assert.equal(selected, false);
});

it('bounds both discovery subprocesses to one second and fails closed on timeout', () => {
const scratch = mkdtempSync(join(tmpdir(), 'maka-bounded-toolchain-'));
const developer = join(scratch, 'CommandLineTools');
const library = join(developer, 'usr', 'lib');
mkdirSync(library, { recursive: true });
writeFileSync(join(library, 'libxcrun.dylib'), 'fixture');
const calls: Array<{ executable: string; args: readonly string[]; timeout: number }> = [];
const runCommand: MacosDeveloperCommandRunner = (executable, args, options) => {
calls.push({ executable, args, timeout: options.timeout });
if (executable === '/usr/bin/xcode-select') {
return { status: 0, stdout: `${developer}\n` };
}
return { status: null };
};
try {
assert.deepEqual(resolveMacosDeveloperExecutableRoots({ runCommand }), []);
assert.deepEqual(calls, [
{ executable: '/usr/bin/xcode-select', args: ['-p'], timeout: 1_000 },
{
executable: '/usr/bin/codesign',
args: [
'--verify',
'--strict',
'-R=anchor apple',
realpathSync(join(library, 'libxcrun.dylib')),
],
timeout: 1_000,
},
]);
} finally {
rmSync(scratch, { recursive: true, force: true });
}
});

it('fails closed when developer-directory discovery times out', () => {
const calls: string[] = [];
const runCommand: MacosDeveloperCommandRunner = (executable) => {
calls.push(executable);
return { status: null };
};

assert.deepEqual(resolveMacosDeveloperExecutableRoots({ runCommand }), []);
assert.deepEqual(calls, ['/usr/bin/xcode-select']);
});

it('returns a canonical root that is unaffected by later selector alias replacement', () => {
const scratch = mkdtempSync(join(tmpdir(), 'maka-replaced-selection-'));
const first = join(scratch, 'first', 'CommandLineTools');
const second = join(scratch, 'second', 'CommandLineTools');
const alias = join(scratch, 'selected');
for (const developer of [first, second]) {
const library = join(developer, 'usr', 'lib');
mkdirSync(library, { recursive: true });
writeFileSync(join(library, 'libxcrun.dylib'), 'fixture');
}
symlinkSync(first, alias);
try {
const roots = resolveMacosDeveloperExecutableRoots({
developerDir: alias,
validateAppleBinary: () => true,
});
unlinkSync(alias);
symlinkSync(second, alias);
assert.deepEqual(roots, [realpathSync(join(first, 'usr', 'lib'))]);
} finally {
rmSync(scratch, { recursive: true, force: true });
}
});

it('rejects a structurally plausible toolchain containing a non-Mach-O libxcrun', () => {
const scratch = mkdtempSync(join(tmpdir(), 'maka-unsigned-clt-'));
const developer = join(scratch, 'CommandLineTools');
const library = join(developer, 'usr', 'lib');
mkdirSync(library, { recursive: true });
writeFileSync(join(library, 'libxcrun.dylib'), 'not signed');
try {
assert.deepEqual(resolveMacosDeveloperExecutableRoots({ developerDir: developer }), []);
} finally {
rmSync(scratch, { recursive: true, force: true });
}
});
});

describe('Apple signer validation', { skip: process.platform !== 'darwin' }, () => {
it('rejects a valid ad-hoc-signed dylib in a plausible toolchain', () => {
const scratch = mkdtempSync(join(tmpdir(), 'maka-adhoc-clt-'));
const developer = join(scratch, 'CommandLineTools');
const library = join(developer, 'usr', 'lib');
const binary = join(library, 'libxcrun.dylib');
mkdirSync(library, { recursive: true });
try {
const source = join(scratch, 'fixture.c');
writeFileSync(source, 'int fixture(void) { return 0; }\n');
const compile = spawnSync('/usr/bin/clang', ['-dynamiclib', source, '-o', binary], {
encoding: 'utf8',
});
assert.equal(compile.status, 0, compile.stderr);
const sign = spawnSync('/usr/bin/codesign', ['--force', '--sign', '-', binary], {
encoding: 'utf8',
});
assert.equal(sign.status, 0, sign.stderr);
const verify = spawnSync('/usr/bin/codesign', ['--verify', '--strict', binary], {
encoding: 'utf8',
});
assert.equal(verify.status, 0, verify.stderr);
assert.deepEqual(resolveMacosDeveloperExecutableRoots({ developerDir: developer }), []);
} finally {
rmSync(scratch, { recursive: true, force: true });
}
});
});

describe('resolveMacosCommandPaths', () => {
it('does not add selected developer roots to restricted read-only profiles', () => {
let validated = false;
const scratch = mkdtempSync(join(tmpdir(), 'maka-read-only-toolchain-'));
const developer = join(scratch, 'CommandLineTools');
const library = join(developer, 'usr', 'lib');
mkdirSync(library, { recursive: true });
writeFileSync(join(library, 'libxcrun.dylib'), 'fixture');
try {
assert.deepEqual(
resolveMacosCommandPaths(
createReadOnlyPermissionProfile(),
{ DEVELOPER_DIR: developer },
{
validateAppleBinary: () => {
validated = true;
return true;
},
},
),
{ executableRoots: [] },
);
assert.equal(validated, false);
} finally {
rmSync(scratch, { recursive: true, force: true });
}
});

it('adds only validated developer roots to writable command profiles', () => {
const scratch = mkdtempSync(join(tmpdir(), 'maka-writable-toolchain-'));
const developer = join(scratch, 'CommandLineTools');
const library = join(developer, 'usr', 'lib');
mkdirSync(library, { recursive: true });
writeFileSync(join(library, 'libxcrun.dylib'), 'fixture');
try {
assert.deepEqual(
resolveMacosCommandPaths(
createWorkspaceWritePermissionProfile(),
{ DEVELOPER_DIR: developer },
{ validateAppleBinary: () => true },
),
{ executableRoots: [realpathSync(library)] },
);
} finally {
rmSync(scratch, { recursive: true, force: true });
}
});
});
Loading