Skip to content

fix(locks): enforce workflow/folder locks on the agent + close manual-UI create gaps#5107

Merged
waleedlatif1 merged 4 commits into
stagingfrom
audit/mothership-respect-locks
Jun 17, 2026
Merged

fix(locks): enforce workflow/folder locks on the agent + close manual-UI create gaps#5107
waleedlatif1 merged 4 commits into
stagingfrom
audit/mothership-respect-locks

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

  • The copilot/agent workflow & folder mutation tools (lib/copilot/tools/handlers/workflow/mutations.ts) and the edit_workflow tool (edit-workflow/index.ts) bypassed lock enforcement entirely — they authorized by workspace permission but never checked lock status. This let the agent (Sim) edit a locked workflow and move/create workflows into a locked folder (the ITSM_PROD report).
  • Added assertWorkflowMutable / assertFolderMutable guards (from @sim/workflow-authz) to every agent mutation path: edit, create, rename, update, move, delete (workflow + folder), set-variables, and set-block-enabled. This mirrors exactly what the REST API routes already do.
  • Closed two parity gaps on the manual-UI REST side that were also unguarded: creating a workflow into a locked folder (api/workflows) and creating a subfolder under a locked parent (api/folders).
  • The realtime collaborative canvas was already protected — it calls assertWorkflowMutable server-side in its operation/subblock/variable handlers. No change needed there.

Type of Change

  • Bug fix

Testing

  • Added unit tests on both surfaces: agent handlers (locked workflow → no persist; move into locked folder → rejected) and REST routes (create-into-locked-folder / locked-parent → 423).
  • All affected suites pass (67 tests across the touched files); biome clean.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

…-UI create gaps

The copilot/agent workflow & folder mutation tools and the edit_workflow
tool bypassed lock enforcement, so the agent could edit a locked workflow
and move/create workflows into a locked folder. Add assertWorkflowMutable/
assertFolderMutable guards (from @sim/workflow-authz) to every agent
mutation path, mirroring the REST API.

Also close two parity gaps on the manual-UI REST side: creating a workflow
into a locked folder and creating a subfolder under a locked parent were
previously unguarded. The realtime collaborative canvas already enforced
workflow-level locks server-side.
@vercel

vercel Bot commented Jun 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jun 17, 2026 3:22am

Request Review

@cursor

cursor Bot commented Jun 17, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Touches many mutation entry points for workflows and folders; incorrect guard placement could block legitimate edits or leave a bypass, but changes mirror existing REST lock patterns with new tests.

Overview
Copilot and agent mutation paths now call assertWorkflowMutable and assertFolderMutable from @sim/workflow-authz before persisting changes—covering create/rename/update/move/delete for workflows and folders, global variables, block enablement, and the server edit_workflow tool. Locked workflows or target folders fail early with the same semantics as existing REST routes instead of only checking workspace write access.

Manual UI REST gaps are closed on POST /api/workflows and POST /api/folders: creation into a locked folder or under a locked parent is blocked via assertFolderMutable, with FolderLockedError mapped to 423 responses.

Tests were added/updated for locked-parent folder creation, locked-folder workflow creation, and agent handlers (no persist on locked workflow; move into locked folder rejected).

Reviewed by Cursor Bugbot for commit 84affcf. Configure here.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@greptile-apps

greptile-apps Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR closes the lock-enforcement gap in the agent/copilot layer and two REST API routes. Previously, the copilot mutation handlers and edit_workflow tool checked workspace permissions but never verified lock status, allowing the agent to mutate locked workflows and folders. The REST API was also missing folder-lock checks when creating a workflow or subfolder inside a locked parent.

  • Agent layer (mutations.ts): assertWorkflowMutable / assertFolderMutable guards added to every mutation path (edit, create, rename, update, move, delete, set-variables, set-block-enabled for both workflows and folders). A previously flagged redundant ensureWorkflowAccess round-trip in executeRenameWorkflow was collapsed, and the folder lock check in executeMoveWorkflow was correctly hoisted before the per-workflow loop.
  • edit-workflow/index.ts: assertWorkflowMutable added immediately after the workspace authorization check, blocking all downstream expensive work for locked workflows.
  • REST routes (/api/workflows, /api/folders): assertFolderMutable added before workflow / subfolder creation; FolderLockedError caught and mapped to HTTP 423 in both routes.

Confidence Score: 5/5

Safe to merge — all mutation paths now enforce lock status before touching the database, and the guard function handles null folder IDs correctly.

The changes are additive guard insertions on well-understood paths. The authz package's assertWorkflowMutable / assertFolderMutable are already used by the realtime canvas layer and behave correctly for null inputs. Previous review findings (redundant DB round-trip in rename, per-iteration folder check in bulk move, mock scoping in tests) have all been addressed. No mutation path was left unguarded.

No files require special attention.

Important Files Changed

Filename Overview
apps/sim/lib/copilot/tools/handlers/workflow/mutations.ts Lock guards applied to all 10+ mutation paths; redundant ensureWorkflowAccess in rename collapsed; folder check hoisted before loop in move — all previously flagged issues resolved.
apps/sim/lib/copilot/tools/server/workflow/edit-workflow/index.ts assertWorkflowMutable added after auth check and before all expensive processing (state load, ops apply, persist), correctly short-circuiting locked workflows.
apps/sim/app/api/workflows/route.ts assertFolderMutable added before workflow creation; FolderLockedError caught and returned as 423; change is minimal and correct.
apps/sim/app/api/folders/route.ts assertFolderMutable added before subfolder creation; FolderLockedError caught and returned as 423; change is minimal and correct.
apps/sim/lib/copilot/tools/handlers/workflow/mutations.test.ts New lock-enforcement describe block covers locked-workflow (no persist) and locked-folder (move rejected) scenarios with mockRejectedValueOnce scoping; representative coverage for the agent surface.
apps/sim/app/api/folders/route.test.ts New test verifies locked parent folder returns 423 and does not reach the DB transaction; mockRejectedValueOnce used correctly.
apps/sim/app/api/workflows/route.test.ts New test verifies locked folder returns 423 on workflow creation and does not reach the DB insert; mockRejectedValueOnce used correctly.

Reviews (5): Last reviewed commit: "refactor(locks): drop redundant ensureWo..." | Re-trigger Greptile

Comment thread apps/sim/lib/copilot/tools/handlers/workflow/mutations.test.ts Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit f7a6d31. Configure here.

@greptile-apps

greptile-apps Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR enforces workflow and folder lock status across two previously unguarded mutation surfaces: the copilot/agent tool handlers in mutations.ts and the edit_workflow tool, plus two REST-side CREATE gaps (api/workflows POST and api/folders POST). Lock guards (assertWorkflowMutable / assertFolderMutable) from @sim/workflow-authz are injected after existing workspace-permission checks, mirroring the realtime canvas protections that were already in place.

  • Agent mutations: Every handler (create, rename, update, move, delete, set-variables, set-block-enabled for workflows and folders) now calls the appropriate assert guard before persisting. The bulk-delete handlers wrap the per-item lock check in a try/catch so one locked item doesn't abort the entire batch.
  • REST create gaps: assertFolderMutable(folderId ?? null) is added to the api/workflows and api/folders POST routes before the create logic, with FolderLockedError caught and mapped to HTTP 423.
  • Tests: Unit tests cover the agent handler path (locked workflow skips persist, locked target folder rejects move) and the REST route path (POST into locked folder returns 423).

Confidence Score: 4/5

Safe to merge — the lock guards are correctly placed after existing auth checks on every agent mutation path and on the two newly-identified REST CREATE gaps.

The implementation is thorough and the guard semantics are correct across all touched paths. The two inline notes are minor: one is a redundant DB call in executeRenameWorkflow (the discard-and-re-fetch pattern), and the other is a per-iteration folder-lock query inside the bulk-move loop that could be hoisted. Neither affects correctness.

mutations.ts — the executeRenameWorkflow double-fetch and the executeMoveWorkflow per-iteration folder-lock query are worth a quick look before merge.

Important Files Changed

Filename Overview
apps/sim/lib/copilot/tools/handlers/workflow/mutations.ts Lock guards added to all 11 mutation paths; guard placement is correct and the inner try/catch pattern in executeDeleteFolder cleanly handles per-item lock failures without aborting the whole batch.
apps/sim/lib/copilot/tools/server/workflow/edit-workflow/index.ts assertWorkflowMutable added immediately after workspace-permission check; WorkflowLockedError propagates as a thrown exception, consistent with every other error in this execute function.
apps/sim/app/api/workflows/route.ts assertFolderMutable added before performCreateWorkflow; FolderLockedError caught and returned as 423; correct that WorkflowLockedError is not caught here since only assertFolderMutable is called.
apps/sim/app/api/folders/route.ts assertFolderMutable(parentId ?? null) added before performCreateFolder; FolderLockedError caught and returns 423; pattern mirrors the workflow route fix.
apps/sim/lib/copilot/tools/handlers/workflow/mutations.test.ts Two new lock-enforcement test cases cover the variable-write and move-workflow paths; mock setup correctly resets both assertWorkflowMutable and assertFolderMutable in beforeEach.
apps/sim/app/api/workflows/route.test.ts New test verifies POST to a locked folder returns 423 and skips the DB insert; workflowAuthzMockFns correctly used.
apps/sim/app/api/folders/route.test.ts New test verifies POST subfolder under a locked parent returns 423 and skips the transaction; mockAssertFolderMutable used for one-shot rejection.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    subgraph Agent["Copilot / Agent path (mutations.ts + edit-workflow)"]
        A1[ensureWorkspaceAccess / ensureWorkflowAccess] --> A2{assertWorkflowMutable\nor assertFolderMutable}
        A2 -- locked --> A3[return success:false / throw]
        A2 -- ok --> A4[assertWorkflowMutationNotAborted]
        A4 --> A5[performCreate/Update/Delete]
    end
    subgraph REST["REST API (api/workflows POST + api/folders POST)"]
        B1[checkHybridAuth / getSession] --> B2[getUserEntityPermissions]
        B2 -- no write --> B3[403 Forbidden]
        B2 -- write --> B4{assertFolderMutable}
        B4 -- FolderLockedError --> B5[423 Locked]
        B4 -- ok --> B6[performCreateWorkflow / performCreateFolder]
        B6 --> B7[200 OK]
    end
    subgraph Canvas["Realtime canvas (unchanged — already guarded)"]
        C1[operation handler] --> C2[assertWorkflowMutable]
        C2 --> C3[persist]
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    subgraph Agent["Copilot / Agent path (mutations.ts + edit-workflow)"]
        A1[ensureWorkspaceAccess / ensureWorkflowAccess] --> A2{assertWorkflowMutable\nor assertFolderMutable}
        A2 -- locked --> A3[return success:false / throw]
        A2 -- ok --> A4[assertWorkflowMutationNotAborted]
        A4 --> A5[performCreate/Update/Delete]
    end
    subgraph REST["REST API (api/workflows POST + api/folders POST)"]
        B1[checkHybridAuth / getSession] --> B2[getUserEntityPermissions]
        B2 -- no write --> B3[403 Forbidden]
        B2 -- write --> B4{assertFolderMutable}
        B4 -- FolderLockedError --> B5[423 Locked]
        B4 -- ok --> B6[performCreateWorkflow / performCreateFolder]
        B6 --> B7[200 OK]
    end
    subgraph Canvas["Realtime canvas (unchanged — already guarded)"]
        C1[operation handler] --> C2[assertWorkflowMutable]
        C2 --> C3[persist]
    end
Loading

Reviews (2): Last reviewed commit: "fix(locks): normalize optional folderId ..." | Re-trigger Greptile

Comment thread apps/sim/lib/copilot/tools/handlers/workflow/mutations.ts Outdated
Comment thread apps/sim/lib/copilot/tools/handlers/workflow/mutations.ts
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 84affcf. Configure here.

@waleedlatif1 waleedlatif1 merged commit 80735b4 into staging Jun 17, 2026
16 checks passed
@waleedlatif1 waleedlatif1 deleted the audit/mothership-respect-locks branch June 17, 2026 03:28
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

1 similar comment
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant