mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-27 11:20:42 +01:00
Read native plugin task output files on the execution host (#17285)
This commit is contained in:
1 parent
d69631b01b
commit
412914cfa7
15 files changed
+202
-24
No files matched your search
@@ -21,7 +21,7 @@ const row = () => ({
|
||||
});
|
||||
function database(value: ReturnType<typeof row> | null = row(), projectRows = [{ id: projectId }], workspaceRows: unknown[] = []) {
|
||||
let selectedRows: unknown[] = projectRows;
|
||||
const results = [value ? [value] : [], [{ id: "issue" }]];
|
||||
const results = [value ? [value] : [], [{ id: "issue" }], value ? [value] : []];
|
||||
const query: any = { from: (table: unknown) => { selectedRows = table === executionWorkspaces ? workspaceRows : projectRows; return query; }, leftJoin: () => query, where: vi.fn(() => query), limit: () => Promise.resolve(results.shift()), then: (resolve: (rows: typeof projectRows) => unknown) => Promise.resolve(selectedRows).then(resolve as (rows: unknown[]) => unknown) };
|
||||
const sets: Record<string, unknown>[] = [];
|
||||
const update: any = { set: (value: Record<string, unknown>) => { sets.push(value); return update; }, where: () => update,
|
||||
@@ -80,7 +80,7 @@ describe("plugin-provided Runner execution", () => {
|
||||
await expect(executeEnvironmentTask(database(value, [], [workspace]).db, denied as never, { companyId: "company", leaseId, operation: { ...submit, projectIds: [] } })).rejects.toThrow("binding changed");
|
||||
expect(denied.call).not.toHaveBeenCalled();
|
||||
});
|
||||
it("accepts artifact identity and excludes filesystem transfer from the contract", () => {
|
||||
it("accepts artifact identity without arbitrary filesystem or execution settings", () => {
|
||||
const operation = environmentTaskOperationSchema.parse({ kind: "describe" });
|
||||
const description = { kind: "description", taskId: "attempt-1", runner: { version: "fixture", digest: `sha256:${"a".repeat(64)}` } };
|
||||
expect(parseEnvironmentTaskResult(operation, "attempt-1", description)).toEqual(description);
|
||||
@@ -91,6 +91,28 @@ describe("plugin-provided Runner execution", () => {
|
||||
expect(environmentTaskOperationSchema.safeParse({ kind }).success).toBe(false);
|
||||
}
|
||||
});
|
||||
it("bounds file reads to the active task identity and validated project scope", async () => {
|
||||
const value=row();
|
||||
const identity={runnerInstanceId:"runner",environmentLeaseId:leaseId,runId:"run",normalizedSessionId:"session",turnId:"turn",itemId:"item"};
|
||||
Object.assign(value.lease.metadata,{runnerTaskIdentity:{...submit.runner}});
|
||||
const operation={kind:"read_file" as const,identity,projectIds:[projectId],path:"proof.txt",maxBytes:1024};
|
||||
const result={kind:"file",taskId:"attempt-1",base64:"",byteSize:0,sha256:"a".repeat(64)};
|
||||
const workers=worker(result);
|
||||
await expect(executeEnvironmentTask(database(value).db,workers as never,{companyId:"company",leaseId,operation})).resolves.toEqual(result);
|
||||
workers.call.mockClear();
|
||||
for(const invalid of [{...operation,projectIds:[]},{...operation,identity:{...identity,itemId:"foreign"}}]) {
|
||||
await expect(executeEnvironmentTask(database(value).db,workers as never,{companyId:"company",leaseId,operation:invalid})).rejects.toThrow();
|
||||
}
|
||||
value.lease.expiresAt=new Date(0);
|
||||
await expect(executeEnvironmentTask(database(value).db,workers as never,{companyId:"company",leaseId,operation})).rejects.toThrow("not active");
|
||||
expect(workers.call).not.toHaveBeenCalled();
|
||||
value.lease.expiresAt=null;
|
||||
workers.call.mockImplementation(async()=>{value.run!.status="cancelled";return result;});
|
||||
await expect(executeEnvironmentTask(database(value).db,workers as never,{companyId:"company",leaseId,operation})).rejects.toThrow("operation unavailable");
|
||||
for(const path of ["//etc/passwd","../escape","a/../b","a\\b","https://example.test/file"]) expect(environmentTaskOperationSchema.safeParse({...operation,path}).success).toBe(false);
|
||||
expect(environmentTaskOperationSchema.safeParse({...operation,maxBytes:11*1024*1024}).success).toBe(false);
|
||||
expect(()=>parseEnvironmentTaskResult(operation,"attempt-1",{...result,taskId:"other"})).toThrow();
|
||||
});
|
||||
it("reads only the current task's saved identity and rejects inactive checkpoint operations", async () => {
|
||||
const value = row();
|
||||
const identity = { runnerInstanceId: "runner", environmentLeaseId: "logical-lease", runId: "run",
|
||||
|
||||
Reference in new issue
Block a user