From b6b8282b314018fa7b8f4eb22385344f461337da Mon Sep 17 00:00:00 2001 From: hetaoBackend Date: Tue, 29 Sep 2026 23:50:01 +0800 Subject: [PATCH 1/2] test(sandbox): pin the probe PATH instead of inheriting the ambient one The real sandbox-exec probes resolve their command through PATH, so inheriting the caller's let any wrapper ahead of /bin decide what `rm` means. On a machine that has run the agent, that is the recoverable-delete shim MCode installs into agent shells: `rm` resolves to the shim, the shim execs mavis-trash, the sandbox denies that trash move as a write outside the policy under test, and a correct allow-probe exits 1. The suite then reports a sandbox regression where the sandbox behaved correctly. Pin all three probe environments to the system binaries the probes actually use, matching the wrapper-argument test above them. This also drops the unguarded `process.env.PATH` in wrapProfile, which had no fallback and could hand a real sandbox spawn an undefined PATH. Fixes #394. --- .../service/sandbox/backend/srt-macos.test.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/local-runtime-v2/src/service/sandbox/backend/srt-macos.test.ts b/packages/local-runtime-v2/src/service/sandbox/backend/srt-macos.test.ts index dc60bad33..427d9f895 100644 --- a/packages/local-runtime-v2/src/service/sandbox/backend/srt-macos.test.ts +++ b/packages/local-runtime-v2/src/service/sandbox/backend/srt-macos.test.ts @@ -1487,6 +1487,17 @@ async function expectMandatoryDenySurface( } } +/** + * Probes must measure the sandbox, not the caller's shell. Every probe spawns a + * real process and resolves its command through `PATH`, so inheriting the + * ambient one lets a wrapper ahead of `/bin` decide what `rm` means — for + * example the recoverable-delete shim MCode installs into agent shells, whose + * trash move the sandbox then denies, turning a correct allow-probe into a + * reported sandbox regression. Pin the probes to the system binaries they + * actually use (`cat`, `rm`, `rmdir`, `find`, `mv`, `printf`, `git`, `true`). + */ +const PROBE_PATH = "/usr/bin:/bin"; + async function expectProbeResult( command: string, filesystem: SandboxInvocationFilesystemPolicy, @@ -1520,7 +1531,7 @@ async function runProbe( cwd: fixture.workspace, baseEnv: { HOME: process.env.HOME ?? "/var/empty", - PATH: process.env.PATH ?? "/usr/bin:/bin", + PATH: PROBE_PATH, }, sandboxTempDir: fixture.sessionTemp, commandId, @@ -1593,7 +1604,7 @@ function createGitProbeFixture(): GitProbeFixture { function gitProbeEnv(fixture: GitProbeFixture): Record { return { HOME: fixture.home, - PATH: process.env.PATH ?? "/usr/bin:/bin", + PATH: PROBE_PATH, GIT_CONFIG_NOSYSTEM: "1", GIT_AUTHOR_NAME: "probe", GIT_AUTHOR_EMAIL: "probe@example.invalid", @@ -1712,7 +1723,7 @@ async function wrapProfile( await profileBackend.wrap({ command: "true", cwd: profileFixture, - baseEnv: { PATH: process.env.PATH }, + baseEnv: { PATH: PROBE_PATH }, sandboxTempDir: profileFixture, commandId: opaqueId, commandText: opaqueId, From 1d7ca0528586ed3a77e94430266155c26337487b Mon Sep 17 00:00:00 2001 From: hetaoBackend Date: Tue, 29 Sep 2026 23:50:01 +0800 Subject: [PATCH 2/2] test(byok): scale the serial BYOK budget by platform The first case is 30+ serial CLI spawns, each booting the whole runtime, so its wall-clock cost tracks machine speed rather than the transport it asserts. The flat 90s budget only held with roughly 1.5x headroom on an idle machine, so a parallel build, a laptop under load, or a shared CI runner turned a passing transport into a `testTimeoutFailure` that named a product failure no assertion had actually observed. Budget it the way smoke.test.mjs already budgets runtime startup: a base allowance plus a Windows multiplier for slower process spawn. The test is not slowed down by a larger ceiling; it only stops failing for reasons unrelated to what it checks. Fixes #395. --- test/byok.test.mjs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/byok.test.mjs b/test/byok.test.mjs index d02d010c5..d92c28412 100644 --- a/test/byok.test.mjs +++ b/test/byok.test.mjs @@ -22,10 +22,18 @@ import { parse as parseYaml, stringify as stringifyYaml } from "yaml"; import { withoutProxyEnvironment } from "./offline-environment.mjs"; const cli = process.env.MCODE_TEST_CLI ?? fileURLToPath(new URL("../dist/cli.js", import.meta.url)); +// This case is 30+ serial CLI spawns, each booting the whole runtime, so its +// wall-clock cost tracks machine speed rather than the transport it asserts. +// A flat budget sized for an idle laptop turns any busy runner (parallel build, +// shared CI host) into a `testTimeoutFailure` that reports a product failure +// while every assertion would have passed. Budget it the way smoke.test.mjs +// budgets runtime startup: a base allowance plus a Windows multiplier for +// slower process spawn, rather than a number that only holds on one machine. +const byokSerialTimeoutMs = process.platform === "win32" ? 360000 : 180000; // This fixture validates BYOK transport and real Runtime persistence, not model quality. test( "BYOK runs without managed login and resumes its saved conversation", - { timeout: 90000 }, + { timeout: byokSerialTimeoutMs }, async (t) => { const fixtureDir = mkdtempSync(path.join(tmpdir(), "minimax-code-byok-")); const dataDir = path.join(fixtureDir, "data");