Fix low-trust sandbox preflight ordering

This commit is contained in:
Dotta committed 2026-06-05 18:06:39 +00:00
1 parent 468b52a3e9
commit e8c17bec49
2 files changed
+201 -1

No files matched your search

@@ -11,14 +11,17 @@ import {
formatRuntimeWorkspaceWarningLog,
mergeExecutionWorkspaceMetadataForPersistence,
mergeCoalescedContextSnapshot,
preflightLowTrustWorkspaceIsolation,
prioritizeProjectWorkspaceCandidatesForRun,
parseSessionCompactionPolicy,
resolveNextSessionState,
resolveRuntimeSessionParamsForWorkspace,
stripHostWorkspaceProvisionForLowTrustSandbox,
stripWorkspaceRuntimeFromExecutionRunConfig,
shouldResetTaskSessionForWake,
type ResolvedWorkspaceForRun,
} from "../services/heartbeat.ts";
import type { TrustPresetResolution } from "../services/trust-preset-resolver.ts";
function buildResolvedWorkspace(overrides: Partial<ResolvedWorkspaceForRun> = {}): ResolvedWorkspaceForRun {
return {
@@ -85,6 +88,131 @@ const truncatingHermesSessionCodec = {
},
};
function lowTrustResolution(): TrustPresetResolution {
return {
kind: "low_trust_review",
preset: "low_trust_review",
boundary: {
mode: "low_trust_review",
companyId: "company-1",
rootIssueId: "issue-1",
},
sourcePresets: { agent: "low_trust_review" },
};
}
function standardTrustResolution(): TrustPresetResolution {
return {
kind: "standard",
preset: "standard",
boundary: null,
sourcePresets: {},
};
}
describe("stripHostWorkspaceProvisionForLowTrustSandbox", () => {
it("removes only the host-side provision command for sandbox-backed low-trust runs", () => {
const config = {
workspaceStrategy: {
type: "git_worktree",
branchTemplate: "{{issue.identifier}}-{{slug}}",
provisionCommand: "bash ./scripts/provision-worktree.sh",
teardownCommand: "bash ./scripts/teardown-worktree.sh",
},
workspaceRuntime: {
services: [{ name: "web" }],
},
};
const result = stripHostWorkspaceProvisionForLowTrustSandbox({
config,
trustPreset: lowTrustResolution(),
selectedEnvironmentDriver: "sandbox",
});
expect(result).not.toBe(config);
expect(result.workspaceStrategy).toEqual({
type: "git_worktree",
branchTemplate: "{{issue.identifier}}-{{slug}}",
teardownCommand: "bash ./scripts/teardown-worktree.sh",
});
expect(result.workspaceRuntime).toBe(config.workspaceRuntime);
expect(config.workspaceStrategy.provisionCommand).toBe("bash ./scripts/provision-worktree.sh");
});
it("preserves provision commands for standard-trust runs", () => {
const config = {
workspaceStrategy: {
type: "git_worktree",
provisionCommand: "bash ./scripts/provision-worktree.sh",
},
};
expect(stripHostWorkspaceProvisionForLowTrustSandbox({
config,
trustPreset: standardTrustResolution(),
selectedEnvironmentDriver: "sandbox",
})).toBe(config);
});
it("preserves provision commands when a low-trust run is not sandbox-backed", () => {
const config = {
workspaceStrategy: {
type: "git_worktree",
provisionCommand: "bash ./scripts/provision-worktree.sh",
},
};
expect(stripHostWorkspaceProvisionForLowTrustSandbox({
config,
trustPreset: lowTrustResolution(),
selectedEnvironmentDriver: "local",
})).toBe(config);
});
});
describe("preflightLowTrustWorkspaceIsolation", () => {
it("fails non-sandbox low-trust runs before the caller reaches host workspace side effects", async () => {
let hostWorkspaceSideEffectReached = false;
await expect((async () => {
await preflightLowTrustWorkspaceIsolation({
trustPreset: lowTrustResolution(),
isolatedWorkspacesEnabled: true,
effectiveExecutionWorkspaceMode: "isolated_workspace",
issue: {
companyId: "company-1",
id: "issue-1",
projectId: "project-1",
},
resolveSelectedEnvironmentDriver: async () => "local",
});
hostWorkspaceSideEffectReached = true;
})()).rejects.toMatchObject({
status: 422,
details: expect.objectContaining({
code: "low_trust_requires_sandbox_environment",
}),
});
expect(hostWorkspaceSideEffectReached).toBe(false);
});
it("returns the sandbox driver for sandbox-backed low-trust runs", async () => {
await expect(preflightLowTrustWorkspaceIsolation({
trustPreset: lowTrustResolution(),
isolatedWorkspacesEnabled: true,
effectiveExecutionWorkspaceMode: "isolated_workspace",
issue: {
companyId: "company-1",
id: "issue-1",
projectId: "project-1",
},
resolveSelectedEnvironmentDriver: async () => "sandbox",
})).resolves.toBe("sandbox");
});
});
describe("resolveRuntimeSessionParamsForWorkspace", () => {
it("migrates fallback workspace sessions to project workspace when project cwd becomes available", () => {
const agentId = "agent-123";
+73 -1
View File
@@ -754,6 +754,53 @@ function buildExecutionWorkspaceConfigSnapshot(
return hasSnapshot ? snapshot : null;
}
export function stripHostWorkspaceProvisionForLowTrustSandbox(input: {
config: Record<string, unknown>;
trustPreset: TrustPresetResolution;
selectedEnvironmentDriver: string | null | undefined;
}): Record<string, unknown> {
if (input.trustPreset.kind !== "low_trust_review") return input.config;
if (input.selectedEnvironmentDriver !== "sandbox") return input.config;
const workspaceStrategy = parseObject(input.config.workspaceStrategy);
if (typeof workspaceStrategy.provisionCommand !== "string") return input.config;
const nextWorkspaceStrategy = { ...workspaceStrategy };
delete nextWorkspaceStrategy.provisionCommand;
return {
...input.config,
workspaceStrategy: nextWorkspaceStrategy,
};
}
export async function preflightLowTrustWorkspaceIsolation(input: {
trustPreset: TrustPresetResolution;
isolatedWorkspacesEnabled: boolean;
effectiveExecutionWorkspaceMode: string | null | undefined;
issue: { companyId: string; id?: string | null; projectId?: string | null } | null;
resolveSelectedEnvironmentDriver: () => Promise<string | null | undefined>;
}): Promise<string | null> {
if (input.trustPreset.kind !== "denied" && input.trustPreset.kind !== "low_trust_review") {
return null;
}
const selectedEnvironmentDriver =
input.trustPreset.kind === "low_trust_review"
? await input.resolveSelectedEnvironmentDriver()
: null;
assertLowTrustWorkspaceIsolation({
resolution: input.trustPreset,
isolatedWorkspacesEnabled: input.isolatedWorkspacesEnabled,
effectiveExecutionWorkspaceMode: input.effectiveExecutionWorkspaceMode,
selectedEnvironmentDriver,
issue: input.issue,
});
return selectedEnvironmentDriver ?? null;
}
function deriveRepoNameFromRepoUrl(repoUrl: string | null): string | null {
const trimmed = repoUrl?.trim() ?? "";
if (!trimmed) return null;
@@ -7599,6 +7646,26 @@ export function heartbeatService(db: Db, options: HeartbeatServiceOptions = {})
? persistedExecutionWorkspaceMode
: requestedExecutionWorkspaceMode;
const selectedEnvironmentId = environmentResolution.environmentId;
const lowTrustPreflightEnvironmentDriver = await preflightLowTrustWorkspaceIsolation({
trustPreset,
isolatedWorkspacesEnabled,
effectiveExecutionWorkspaceMode,
issue: issueRef
? {
companyId: agent.companyId,
id: issueRef.id,
projectId: issueRef.projectId,
}
: null,
resolveSelectedEnvironmentDriver: async () => {
const preflightEnvironment = await envOrchestrator.resolveEnvironment({
companyId: agent.companyId,
selectedEnvironmentId,
defaultEnvironmentId: defaultEnvironment.id,
});
return preflightEnvironment.driver;
},
});
const workspaceManagedConfig = shouldReuseExisting
? { ...config }
: buildExecutionWorkspaceAdapterConfig({
@@ -7685,6 +7752,11 @@ export function heartbeatService(db: Db, options: HeartbeatServiceOptions = {})
...effectiveResolvedConfig,
paperclipRuntimeSkills: runtimeSkillEntries,
};
const hostExecutionWorkspaceConfig = stripHostWorkspaceProvisionForLowTrustSandbox({
config: runtimeConfig,
trustPreset,
selectedEnvironmentDriver: lowTrustPreflightEnvironmentDriver,
});
const workspaceOperationRecorder = workspaceOperationsSvc.createRecorder({
companyId: agent.companyId,
heartbeatRunId: run.id,
@@ -7733,7 +7805,7 @@ export function heartbeatService(db: Db, options: HeartbeatServiceOptions = {})
: null;
const executionWorkspace = reusedExecutionWorkspace ?? await realizeExecutionWorkspace({
base: executionWorkspaceBase,
config: runtimeConfig,
config: hostExecutionWorkspaceConfig,
issue: issueRef,
agent: {
id: agent.id,