feat: authorize workflow contract authoring

This commit is contained in:
lda
2026-08-14 17:43:14 +07:00 Verified
parent bc23a722c3
commit bd226a2fa7
20 changed files with 1506 additions and 14 deletions
+38
View File
@@ -260,6 +260,10 @@ describe("POST /api/rpc", () => {
operation: "workflow.draft_workspaces.get",
params: { workspace_id: "draft-1" },
},
{
operation: "workflow.draft_workspaces.inspect_authoring_contract",
params: { workspace_id: "draft-1", revision: 1 },
},
{
operation: "workflow.draft_workspaces.create_empty",
params: { workspace_id: "draft-1", name: "draft.workflow" },
@@ -299,6 +303,14 @@ describe("POST /api/rpc", () => {
target: "__end__",
},
},
{
operation: "workflow.draft_workspaces.set_contract",
params: { workspace_id: "draft-1", revision: 1, outcomes: ["ok"] },
},
{
operation: "workflow.draft_workspaces.set_start",
params: { workspace_id: "draft-1", revision: 1, step_id: "echo" },
},
{
operation: "workflow.draft_workspaces.set_step_input_bindings",
params: {
@@ -321,6 +333,14 @@ describe("POST /api/rpc", () => {
operation: "workflow.draft_workspaces.validate",
params: { workspace_id: "draft-1" },
},
{
operation: "workflow.draft_workspaces.set_workflow_output_bindings",
params: {
workspace_id: "draft-1",
revision: 1,
bindings: [{ source: "result", target: "state.result" }],
},
},
{ operation: "workflow.runs.start", params: {} },
{ operation: "workflow.runs.resume", params: {} },
] as const)("authorizes the allowlisted operation $operation", async ({
@@ -467,6 +487,24 @@ describe("POST /api/rpc", () => {
expect(body.error.code).toBe("unknown_operation");
});
it.each(["workflow.draft_workspaces.patch", "workflow.draft_workspaces.replace_document"])(
"does not authorize adjacent generic operation %s",
async (operation) => {
const res = await app.request("/api/rpc", {
method: "POST",
headers: validConsoleHeaders,
body: JSON.stringify({
operation,
target: "http://127.0.0.1:8000/rpc",
}),
});
expect(res.status).toBe(400);
const body = await res.json();
expect(body.error.code).toBe("unknown_operation");
},
);
it("returns 400 for invalid JSON body", async () => {
const res = await app.request("/api/rpc", {
method: "POST",
@@ -15,14 +15,18 @@ describe("browser operation policy", () => {
"workflow.capabilities.inspect",
"workflow.draft_workspaces.list",
"workflow.draft_workspaces.get",
"workflow.draft_workspaces.inspect_authoring_contract",
"workflow.draft_workspaces.create_empty",
"workflow.draft_workspaces.create_from_capability",
"workflow.draft_workspaces.add_step_from_capability",
"workflow.draft_workspaces.update_capability_step",
"workflow.draft_workspaces.set_route",
"workflow.draft_workspaces.set_contract",
"workflow.draft_workspaces.set_start",
"workflow.draft_workspaces.set_step_input_bindings",
"workflow.draft_workspaces.set_step_output_bindings",
"workflow.draft_workspaces.validate",
"workflow.draft_workspaces.set_workflow_output_bindings",
"workflow.artifacts.list",
"workflow.artifacts.inspect",
"workflow.deployments.list",
@@ -41,6 +45,9 @@ describe("browser operation policy", () => {
expect(browserAllowedOperationNames).not.toContain(
"workflow.draft_workspaces.replace_document",
);
expect(browserAllowedOperationNames).not.toContain(
"workflow.draft_workspaces.patch",
);
expect(browserAllowedOperationNames).not.toContain(
"workflow.draft_workspaces.remove_step",
);
@@ -13,14 +13,18 @@ export const browserAllowedOperationNames = defineBrowserOperationNames([
"workflow.capabilities.inspect",
"workflow.draft_workspaces.list",
"workflow.draft_workspaces.get",
"workflow.draft_workspaces.inspect_authoring_contract",
"workflow.draft_workspaces.create_empty",
"workflow.draft_workspaces.create_from_capability",
"workflow.draft_workspaces.add_step_from_capability",
"workflow.draft_workspaces.update_capability_step",
"workflow.draft_workspaces.set_route",
"workflow.draft_workspaces.set_contract",
"workflow.draft_workspaces.set_start",
"workflow.draft_workspaces.set_step_input_bindings",
"workflow.draft_workspaces.set_step_output_bindings",
"workflow.draft_workspaces.validate",
"workflow.draft_workspaces.set_workflow_output_bindings",
"workflow.artifacts.list",
"workflow.artifacts.inspect",
"workflow.deployments.list",