mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-03 01:27:09 +01:00
Fix routine restore assignee authorization
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
cbf3b268ba
commit
ec8c769e46
2 files changed
+65
-1
No files matched your search
@@ -361,6 +361,57 @@ describeEmbeddedPostgres("routine service live-execution coalescing", () => {
|
||||
await expect(db.select().from(routineRuns).where(eq(routineRuns.id, run.id))).resolves.toHaveLength(1);
|
||||
});
|
||||
|
||||
it("blocks agents from restoring routine revisions assigned to another agent", async () => {
|
||||
const { companyId, routine, svc } = await seedFixture();
|
||||
const otherAgentId = randomUUID();
|
||||
await db.insert(agents).values({
|
||||
id: otherAgentId,
|
||||
companyId,
|
||||
name: "OtherCoder",
|
||||
role: "engineer",
|
||||
status: "active",
|
||||
adapterType: "codex_local",
|
||||
adapterConfig: {},
|
||||
runtimeConfig: {},
|
||||
permissions: {},
|
||||
});
|
||||
const revision1Id = routine.latestRevisionId!;
|
||||
|
||||
await svc.update(routine.id, { assigneeAgentId: otherAgentId }, {});
|
||||
|
||||
await expect(
|
||||
svc.restoreRevision(routine.id, revision1Id, { agentId: otherAgentId }),
|
||||
).rejects.toMatchObject({
|
||||
status: 403,
|
||||
message: "Agents can only restore routine revisions assigned to themselves",
|
||||
});
|
||||
await expect(svc.get(routine.id)).resolves.toMatchObject({
|
||||
assigneeAgentId: otherAgentId,
|
||||
latestRevisionNumber: 2,
|
||||
});
|
||||
});
|
||||
|
||||
it("blocks restoring routine revisions assigned to agents that are no longer assignable", async () => {
|
||||
const { agentId, routine, svc } = await seedFixture();
|
||||
const revision1Id = routine.latestRevisionId!;
|
||||
await svc.update(routine.id, { description: "revision 2" }, {});
|
||||
await db
|
||||
.update(agents)
|
||||
.set({ status: "terminated" })
|
||||
.where(eq(agents.id, agentId));
|
||||
|
||||
await expect(
|
||||
svc.restoreRevision(routine.id, revision1Id, { userId: "board-user" }),
|
||||
).rejects.toMatchObject({
|
||||
status: 409,
|
||||
message: "Cannot assign routines to terminated agents",
|
||||
});
|
||||
await expect(svc.get(routine.id)).resolves.toMatchObject({
|
||||
description: "revision 2",
|
||||
latestRevisionNumber: 2,
|
||||
});
|
||||
});
|
||||
|
||||
it("appends safe trigger metadata revisions without leaking webhook secrets", async () => {
|
||||
const { routine, svc } = await seedFixture();
|
||||
const created = await svc.createTrigger(routine.id, {
|
||||
|
||||
@@ -605,6 +605,17 @@ export function routineService(
|
||||
if (agent.status === "terminated") throw conflict("Cannot assign routines to terminated agents");
|
||||
}
|
||||
|
||||
async function assertRestorableAssignee(
|
||||
companyId: string,
|
||||
assigneeAgentId: string | null | undefined,
|
||||
actor: Actor,
|
||||
) {
|
||||
await assertAssignableAgent(companyId, assigneeAgentId);
|
||||
if (actor.agentId && assigneeAgentId !== actor.agentId) {
|
||||
throw forbidden("Agents can only restore routine revisions assigned to themselves");
|
||||
}
|
||||
}
|
||||
|
||||
async function assertProject(companyId: string, projectId: string | null | undefined) {
|
||||
if (!projectId) return;
|
||||
const project = await db
|
||||
@@ -1830,6 +1841,9 @@ export function routineService(
|
||||
}
|
||||
|
||||
const snapshot = targetRevision.snapshot as RoutineRevisionSnapshotV1;
|
||||
const routineSnapshot = snapshot.routine;
|
||||
await assertRestorableAssignee(existingRoutine.companyId, routineSnapshot.assigneeAgentId, actor);
|
||||
|
||||
const currentTriggers = await db
|
||||
.select({ id: routineTriggers.id })
|
||||
.from(routineTriggers)
|
||||
@@ -1862,7 +1876,6 @@ export function routineService(
|
||||
.then((rows) => rows[0] ?? null);
|
||||
if (!locked) throw notFound("Routine not found");
|
||||
|
||||
const routineSnapshot = snapshot.routine;
|
||||
const now = new Date();
|
||||
const [restoredRoutine] = await txDb
|
||||
.update(routines)
|
||||
|
||||
Reference in new issue
Block a user