From bc9460bd65a78ce7f92952e7e411a08a47269144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Thu, 25 Jun 2026 07:51:39 +0200 Subject: [PATCH] fix: disable nested sandboxing for ios runner builds --- scripts/build-xcuitest-apple.sh | 4 ++ scripts/write-xcuitest-cache-metadata.mjs | 10 +++++ .../ios/__tests__/runner-client.test.ts | 41 +++++++++++++++++++ src/platforms/ios/runner-xctestrun.ts | 14 +++++++ 4 files changed, 69 insertions(+) diff --git a/scripts/build-xcuitest-apple.sh b/scripts/build-xcuitest-apple.sh index 626eaac11..fa8586c5c 100644 --- a/scripts/build-xcuitest-apple.sh +++ b/scripts/build-xcuitest-apple.sh @@ -102,6 +102,10 @@ xcodebuild build-for-testing \ AGENT_DEVICE_IOS_RUNNER_TEST_BUNDLE_ID="$RUNNER_TEST_BUNDLE_ID" \ COMPILER_INDEX_STORE_ENABLE=NO \ ENABLE_CODE_COVERAGE=NO \ + -IDEPackageSupportDisableManifestSandbox=1 \ + -IDEPackageSupportDisablePluginExecutionSandbox=1 \ + ENABLE_USER_SCRIPT_SANDBOXING=NO \ + OTHER_SWIFT_FLAGS='$(inherited) -disable-sandbox' \ $SIGNING_BUILD_SETTINGS node --experimental-strip-types scripts/patch-xcuitest-runner-icon.ts "$DERIVED_PATH" diff --git a/scripts/write-xcuitest-cache-metadata.mjs b/scripts/write-xcuitest-cache-metadata.mjs index f27d7f3c0..e09e11f17 100644 --- a/scripts/write-xcuitest-cache-metadata.mjs +++ b/scripts/write-xcuitest-cache-metadata.mjs @@ -197,6 +197,15 @@ function resolveSigningBuildSettings() { ]; } +function resolveSandboxBuildArgs() { + return [ + '-IDEPackageSupportDisableManifestSandbox=1', + '-IDEPackageSupportDisablePluginExecutionSandbox=1', + 'ENABLE_USER_SCRIPT_SANDBOXING=NO', + 'OTHER_SWIFT_FLAGS=$(inherited) -disable-sandbox', + ]; +} + const appBundleId = resolveRunnerAppBundleId(); const testBundleId = resolveRunnerTestBundleId(); const metadata = { @@ -214,6 +223,7 @@ const metadata = { ], runnerSigningBuildSettings: resolveSigningBuildSettings(), runnerPerformanceBuildSettings: ['COMPILER_INDEX_STORE_ENABLE=NO', 'ENABLE_CODE_COVERAGE=NO'], + runnerSandboxBuildArgs: resolveSandboxBuildArgs(), }; const artifacts = resolveRunnerCacheArtifacts(); diff --git a/src/platforms/ios/__tests__/runner-client.test.ts b/src/platforms/ios/__tests__/runner-client.test.ts index 971f7609a..ec891bb6e 100644 --- a/src/platforms/ios/__tests__/runner-client.test.ts +++ b/src/platforms/ios/__tests__/runner-client.test.ts @@ -55,6 +55,7 @@ import { resolveRunnerDerivedPath, resolveRunnerCacheMetadataPath, resolveRunnerPerformanceBuildSettings, + resolveRunnerSandboxBuildArgs, shouldDeleteRunnerDerivedRootEntry, writeRunnerCacheMetadata, xctestrunReferencesProjectRoot, @@ -475,6 +476,15 @@ test('resolveRunnerPerformanceBuildSettings disables indexing and code coverage' ]); }); +test('resolveRunnerSandboxBuildArgs disables nested Xcode and Swift sandboxing', () => { + assert.deepEqual(resolveRunnerSandboxBuildArgs(), [ + '-IDEPackageSupportDisableManifestSandbox=1', + '-IDEPackageSupportDisablePluginExecutionSandbox=1', + 'ENABLE_USER_SCRIPT_SANDBOXING=NO', + 'OTHER_SWIFT_FLAGS=$(inherited) -disable-sandbox', + ]); +}); + test('resolveRunnerBundleBuildSettings returns default bundle identifiers', () => { assert.deepEqual(resolveRunnerBundleBuildSettings({}), [ 'AGENT_DEVICE_IOS_RUNNER_APP_BUNDLE_ID=com.callstack.agentdevice.runner', @@ -1195,6 +1205,37 @@ test('ensureXctestrun rebuilds cached runner when metadata package version misma assert.equal(rebuiltMetadata.artifacts?.xctestrunPath, rebuiltXctestrunPath); }); +test('ensureXctestrunArtifact passes sandbox-disabling settings to xcodebuild', async () => { + const projectRoot = repoRoot; + const tmpDir = await makeProjectTmpDir(); + const derivedPath = path.join(tmpDir, 'custom-derived'); + const rebuiltXctestrunPath = path.join(derivedPath, 'Build', 'Products', 'rebuilt.xctestrun'); + + withRunnerDerivedPathEnv(derivedPath); + + mockRunCmdStreaming.mockImplementationOnce(async () => { + await fs.promises.mkdir(path.join(derivedPath, 'Build', 'Products', 'Runner.app'), { + recursive: true, + }); + writeXctestrunFixture(rebuiltXctestrunPath, { + projectRoot, + productRelativePaths: ['Runner.app'], + }); + }); + + const result = await ensureXctestrunArtifact(iosSimulator, { + forceRunnerXctestrunRebuild: true, + }); + + assert.equal(result.xctestrunPath, rebuiltXctestrunPath); + assert.equal(mockRunCmdStreaming.mock.calls.length, 1); + const args = mockRunCmdStreaming.mock.calls[0]?.[1] ?? []; + assert.equal(args.includes('-IDEPackageSupportDisableManifestSandbox=1'), true); + assert.equal(args.includes('-IDEPackageSupportDisablePluginExecutionSandbox=1'), true); + assert.equal(args.includes('ENABLE_USER_SCRIPT_SANDBOXING=NO'), true); + assert.equal(args.includes('OTHER_SWIFT_FLAGS=$(inherited) -disable-sandbox'), true); +}); + test('ensureXctestrunArtifact stress-recovers after a bad restored artifact', async () => { const projectRoot = repoRoot; const tmpDir = await makeProjectTmpDir(); diff --git a/src/platforms/ios/runner-xctestrun.ts b/src/platforms/ios/runner-xctestrun.ts index d8ba13810..41af7e6fa 100644 --- a/src/platforms/ios/runner-xctestrun.ts +++ b/src/platforms/ios/runner-xctestrun.ts @@ -52,6 +52,12 @@ const RUNNER_XCTESTRUN_CAPTURE_OPTIONS = { SystemAttachmentLifetime: 'keepNever', UserAttachmentLifetime: 'keepNever', } as const; +const RUNNER_SANDBOX_BUILD_ARGS = [ + '-IDEPackageSupportDisableManifestSandbox=1', + '-IDEPackageSupportDisablePluginExecutionSandbox=1', + 'ENABLE_USER_SCRIPT_SANDBOXING=NO', + 'OTHER_SWIFT_FLAGS=$(inherited) -disable-sandbox', +] as const; const runnerXctestrunBuildLocks = new Map>(); const badRunnerArtifactsForRun = new Set(); @@ -107,6 +113,7 @@ export type RunnerXctestrunCacheMetadata = { runnerBundleBuildSettings: string[]; runnerSigningBuildSettings: string[]; runnerPerformanceBuildSettings: string[]; + runnerSandboxBuildArgs: string[]; artifacts?: RunnerXctestrunCacheArtifacts; }; @@ -758,6 +765,7 @@ export function resolveExpectedRunnerCacheMetadata( device.platform, ), runnerPerformanceBuildSettings: resolveRunnerPerformanceBuildSettings(), + runnerSandboxBuildArgs: resolveRunnerSandboxBuildArgs(), }; } @@ -1363,6 +1371,7 @@ async function buildRunnerXctestrun( ); const provisioningArgs = device.kind === 'device' ? ['-allowProvisioningUpdates'] : []; const performanceBuildSettings = resolveRunnerPerformanceBuildSettings(); + const sandboxBuildArgs = resolveRunnerSandboxBuildArgs(); const simulatorSetRedirect = await acquireXcodebuildSimulatorSetRedirect(device); try { await runCmdStreaming( @@ -1382,6 +1391,7 @@ async function buildRunnerXctestrun( '-derivedDataPath', derived, ...performanceBuildSettings, + ...sandboxBuildArgs, ...runnerBundleBuildSettings, ...provisioningArgs, ...signingBuildSettings, @@ -1486,6 +1496,10 @@ export function resolveRunnerPerformanceBuildSettings(): string[] { return ['COMPILER_INDEX_STORE_ENABLE=NO', 'ENABLE_CODE_COVERAGE=NO']; } +export function resolveRunnerSandboxBuildArgs(): string[] { + return [...RUNNER_SANDBOX_BUILD_ARGS]; +} + function shouldCleanDerived(): boolean { return isEnvTruthy(process.env.AGENT_DEVICE_IOS_CLEAN_DERIVED); }