mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-24 10:13:11 +02:00
Preflight low-trust before workspace resolution
This commit is contained in:
1 parent
0e636a4030
commit
d78bd3b9a5
2 files changed
+85
-7
No files matched your search
@@ -15,6 +15,7 @@ import {
|
||||
prioritizeProjectWorkspaceCandidatesForRun,
|
||||
parseSessionCompactionPolicy,
|
||||
resolveNextSessionState,
|
||||
resolveWorkspaceAfterLowTrustPreflight,
|
||||
resolveRuntimeSessionParamsForWorkspace,
|
||||
stripHostWorkspaceProvisionForLowTrustSandbox,
|
||||
stripWorkspaceRuntimeFromExecutionRunConfig,
|
||||
@@ -213,6 +214,57 @@ describe("preflightLowTrustWorkspaceIsolation", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveWorkspaceAfterLowTrustPreflight", () => {
|
||||
it("fails non-sandbox low-trust runs before resolving workspaces", async () => {
|
||||
let workspaceResolverReached = false;
|
||||
|
||||
await expect(resolveWorkspaceAfterLowTrustPreflight({
|
||||
trustPreset: lowTrustResolution(),
|
||||
isolatedWorkspacesEnabled: true,
|
||||
effectiveExecutionWorkspaceMode: "isolated_workspace",
|
||||
issue: {
|
||||
companyId: "company-1",
|
||||
id: "issue-1",
|
||||
projectId: "project-1",
|
||||
},
|
||||
resolveSelectedEnvironmentDriver: async () => "local",
|
||||
resolveWorkspace: async () => {
|
||||
workspaceResolverReached = true;
|
||||
return buildResolvedWorkspace();
|
||||
},
|
||||
})).rejects.toMatchObject({
|
||||
status: 422,
|
||||
details: expect.objectContaining({
|
||||
code: "low_trust_requires_sandbox_environment",
|
||||
}),
|
||||
});
|
||||
|
||||
expect(workspaceResolverReached).toBe(false);
|
||||
});
|
||||
|
||||
it("preserves standard-trust workspace resolution", async () => {
|
||||
const workspace = buildResolvedWorkspace({ cwd: "/tmp/standard-workspace" });
|
||||
|
||||
await expect(resolveWorkspaceAfterLowTrustPreflight({
|
||||
trustPreset: standardTrustResolution(),
|
||||
isolatedWorkspacesEnabled: false,
|
||||
effectiveExecutionWorkspaceMode: "shared_workspace",
|
||||
issue: {
|
||||
companyId: "company-1",
|
||||
id: "issue-1",
|
||||
projectId: "project-1",
|
||||
},
|
||||
resolveSelectedEnvironmentDriver: async () => {
|
||||
throw new Error("standard trust should not inspect the environment driver");
|
||||
},
|
||||
resolveWorkspace: async () => workspace,
|
||||
})).resolves.toEqual({
|
||||
selectedEnvironmentDriver: null,
|
||||
workspace,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveRuntimeSessionParamsForWorkspace", () => {
|
||||
it("migrates fallback workspace sessions to project workspace when project cwd becomes available", () => {
|
||||
const agentId = "agent-123";
|
||||
|
||||
@@ -801,6 +801,28 @@ export async function preflightLowTrustWorkspaceIsolation(input: {
|
||||
return selectedEnvironmentDriver ?? null;
|
||||
}
|
||||
|
||||
export async function resolveWorkspaceAfterLowTrustPreflight<TWorkspace>(input: {
|
||||
trustPreset: TrustPresetResolution;
|
||||
isolatedWorkspacesEnabled: boolean;
|
||||
effectiveExecutionWorkspaceMode: string | null | undefined;
|
||||
issue: { companyId: string; id?: string | null; projectId?: string | null } | null;
|
||||
resolveSelectedEnvironmentDriver: () => Promise<string | null | undefined>;
|
||||
resolveWorkspace: () => Promise<TWorkspace>;
|
||||
}): Promise<{ selectedEnvironmentDriver: string | null; workspace: TWorkspace }> {
|
||||
const selectedEnvironmentDriver = await preflightLowTrustWorkspaceIsolation({
|
||||
trustPreset: input.trustPreset,
|
||||
isolatedWorkspacesEnabled: input.isolatedWorkspacesEnabled,
|
||||
effectiveExecutionWorkspaceMode: input.effectiveExecutionWorkspaceMode,
|
||||
issue: input.issue,
|
||||
resolveSelectedEnvironmentDriver: input.resolveSelectedEnvironmentDriver,
|
||||
});
|
||||
|
||||
return {
|
||||
selectedEnvironmentDriver,
|
||||
workspace: await input.resolveWorkspace(),
|
||||
};
|
||||
}
|
||||
|
||||
function deriveRepoNameFromRepoUrl(repoUrl: string | null): string | null {
|
||||
const trimmed = repoUrl?.trim() ?? "";
|
||||
if (!trimmed) return null;
|
||||
@@ -7485,12 +7507,6 @@ export function heartbeatService(db: Db, options: HeartbeatServiceOptions = {})
|
||||
trustPreset.kind === "low_trust_review" && resolvedExecutionWorkspaceMode === "shared_workspace"
|
||||
? "isolated_workspace"
|
||||
: resolvedExecutionWorkspaceMode;
|
||||
const resolvedWorkspace = await resolveWorkspaceForRun(
|
||||
agent,
|
||||
context,
|
||||
previousSessionParams,
|
||||
{ useProjectWorkspace: requestedExecutionWorkspaceMode !== "agent_default" },
|
||||
);
|
||||
const issueRef = issueContext
|
||||
? {
|
||||
id: issueContext.id,
|
||||
@@ -7646,7 +7662,10 @@ export function heartbeatService(db: Db, options: HeartbeatServiceOptions = {})
|
||||
? persistedExecutionWorkspaceMode
|
||||
: requestedExecutionWorkspaceMode;
|
||||
const selectedEnvironmentId = environmentResolution.environmentId;
|
||||
const lowTrustPreflightEnvironmentDriver = await preflightLowTrustWorkspaceIsolation({
|
||||
const {
|
||||
selectedEnvironmentDriver: lowTrustPreflightEnvironmentDriver,
|
||||
workspace: resolvedWorkspace,
|
||||
} = await resolveWorkspaceAfterLowTrustPreflight({
|
||||
trustPreset,
|
||||
isolatedWorkspacesEnabled,
|
||||
effectiveExecutionWorkspaceMode,
|
||||
@@ -7665,6 +7684,13 @@ export function heartbeatService(db: Db, options: HeartbeatServiceOptions = {})
|
||||
});
|
||||
return preflightEnvironment.driver;
|
||||
},
|
||||
resolveWorkspace: () =>
|
||||
resolveWorkspaceForRun(
|
||||
agent,
|
||||
context,
|
||||
previousSessionParams,
|
||||
{ useProjectWorkspace: requestedExecutionWorkspaceMode !== "agent_default" },
|
||||
),
|
||||
});
|
||||
const workspaceManagedConfig = shouldReuseExisting
|
||||
? { ...config }
|
||||
|
||||
Reference in new issue
Block a user