fix: enforce workflow validation before deploy and run#3473
fix: enforce workflow validation before deploy and run#3473MaxwellCalkin wants to merge 2 commits intosimstudioai:mainfrom
Conversation
The deploy endpoint only validated schedule data but never checked workflow state (block types, edges, tool references). The Run button had validation hardcoded to `false`. Redeployments (Update) skipped all pre-deploy checks entirely. Changes: - Backend: call validateWorkflowState() before deploying to reject workflows with unknown block types, dangling edges, or invalid tool references (returns 400 with details) - Frontend panel: replace hardcoded `hasValidationErrors = false` with a check that blocks are connected via edges - Frontend deploy hook: run pre-deploy checks for redeployments too, not just first deploys Fixes simstudioai#3444 This PR was authored by Claude Opus 4.6 (AI), operated by @MaxwellCalkin Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PR SummaryMedium Risk Overview UI now prevents invalid operations earlier. The deploy button runs Written by Cursor Bugbot for commit 2766a39. This will update automatically on new commits. Configure here. |
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx
Show resolved
Hide resolved
Greptile SummaryThis PR closes a meaningful security/UX gap by adding workflow state validation at three enforcement points: a backend
Confidence Score: 3/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant User
participant panel.tsx
participant use-deployment.ts
participant use-predeploy-checks.ts
participant deploy/route.ts
participant validateWorkflowState
User->>panel.tsx: Click Run button
panel.tsx->>panel.tsx: Check hasBlocks && !hasEdges
alt hasValidationErrors
panel.tsx-->>User: Button disabled (no run)
else no validation errors
panel.tsx->>panel.tsx: handleRunWorkflow()
end
User->>panel.tsx: Click Deploy / Update button
panel.tsx->>use-deployment.ts: handleDeployClick()
use-deployment.ts->>use-predeploy-checks.ts: runPreDeployChecks(blocks, edges, loops, parallels)
alt checks fail
use-predeploy-checks.ts-->>use-deployment.ts: { passed: false, error }
use-deployment.ts-->>User: Notification error
else checks pass & isDeployed
use-deployment.ts-->>panel.tsx: { success: true, shouldOpenModal: true }
panel.tsx-->>User: Open redeploy modal
else checks pass & first deploy
use-deployment.ts->>deploy/route.ts: POST /api/workflows/[id]/deploy
deploy/route.ts->>validateWorkflowState: validateWorkflowState(blocks, edges, loops, parallels)
alt validation fails
validateWorkflowState-->>deploy/route.ts: { valid: false, errors }
deploy/route.ts-->>use-deployment.ts: HTTP 400 error
use-deployment.ts-->>User: Notification error
else validation passes
deploy/route.ts->>deploy/route.ts: validateWorkflowSchedules()
deploy/route.ts->>deploy/route.ts: deployWorkflow()
deploy/route.ts-->>use-deployment.ts: HTTP 200 success
use-deployment.ts-->>panel.tsx: { success: true, shouldOpenModal: true }
panel.tsx-->>User: Open deploy modal
end
end
|
| const hasEdges = useWorkflowStore((state) => state.edges.length > 0) | ||
| const hasValidationErrors = hasBlocks && !hasEdges |
There was a problem hiding this comment.
Single-block workflows incorrectly blocked by edge check
hasValidationErrors = hasBlocks && !hasEdges disables the Run button for any workflow that has blocks but zero edges. A workflow consisting of exactly one standalone block (e.g. a single agent block) has no edges by definition and would always show the button as disabled — even though it may be a perfectly valid, runnable workflow.
The intent (catching disconnected/dangling blocks) is good, but using edges.length === 0 as the sole proxy is too coarse. Consider checking that the starter block has at least one outgoing edge, or that the graph is connected, rather than requiring any edge to exist across the whole workflow.
| const workflowValidation = validateWorkflowState({ | ||
| blocks: normalizedData.blocks, | ||
| edges: normalizedData.edges, | ||
| loops: normalizedData.loops, | ||
| parallels: normalizedData.parallels, | ||
| } as WorkflowState) |
There was a problem hiding this comment.
Type assertion papers over a missing variables field
normalizedData may not include a variables property, so the cast as WorkflowState silently satisfies TypeScript while potentially producing undefined at runtime for that field. validateWorkflowState only uses blocks and edges, but if the function signature ever evolves, this cast could hide a real gap.
Consider spreading a safe default instead:
| const workflowValidation = validateWorkflowState({ | |
| blocks: normalizedData.blocks, | |
| edges: normalizedData.edges, | |
| loops: normalizedData.loops, | |
| parallels: normalizedData.parallels, | |
| } as WorkflowState) | |
| const workflowValidation = validateWorkflowState({ | |
| blocks: normalizedData.blocks, | |
| edges: normalizedData.edges, | |
| loops: normalizedData.loops, | |
| parallels: normalizedData.parallels, | |
| variables: {}, | |
| } as WorkflowState) |
- Allow single-block workflows to run (blockCount > 1 check instead of
hasBlocks && !hasEdges, which incorrectly blocked standalone blocks)
- Guard Mod+Enter keyboard shortcut with isButtonDisabled to match Run
button behavior and prevent bypassing validation
- Add explicit variables: {} default in deploy route to avoid relying on
type assertion to paper over a missing field
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
Fixes #3444
The deploy endpoint (
/api/workflows/[id]/deploy) only validated schedule data but never checked workflow state integrity. Additionally:hasValidationErrorshardcoded tofalse, so it was never blocked by validation errorsChanges
deploy/route.ts): CallvalidateWorkflowState()before deploying to reject workflows with unknown block types, dangling edge references, or invalid tool references. Returns HTTP 400 with a detailed error summary.panel.tsx): ReplacehasValidationErrors = falsewith a live check that blocks are connected via edges. The Run button is now disabled when blocks exist but have no connections.use-deployment.ts): RunrunPreDeployChecks()for redeployments too (not just first deploys), so the "Update" button also validates required fields, schedule config, and serialization before opening the deploy modal.How it works
This PR was authored by Claude Opus 4.6 (AI), operated by @MaxwellCalkin