Skip to content

fix(server-core): validate workflow ownership on control endpoints#1318

Merged
omeraplak merged 3 commits into
VoltAgent:mainfrom
ruanchaves:fix/workflow-control-ownership-1316
May 29, 2026
Merged

fix(server-core): validate workflow ownership on control endpoints#1318
omeraplak merged 3 commits into
VoltAgent:mainfrom
ruanchaves:fix/workflow-control-ownership-1316

Conversation

@ruanchaves

@ruanchaves ruanchaves commented May 27, 2026

Copy link
Copy Markdown
Contributor

PR Checklist

Bugs / Features

What is the current behavior?

POST /workflows/:id/executions/:executionId/suspend and POST /workflows/:id/executions/:executionId/cancel ignore the workflow ID path parameter and act on the active execution by executionId alone.

That allows a wrong-route request to suspend or cancel a live execution that belongs to another workflow.

What is the new behavior?

The shared control handlers now verify that the route workflow owns the execution before acting on it.

The adapters pass the route workflow ID into the shared handler body, and wrong-route suspend/cancel requests are rejected instead of mutating the real execution.

Fixes #1316

Notes for reviewers


Summary by cubic

Validate workflow ownership on suspend and cancel, and harden request validation so wrong-route or malformed requests can’t control executions. Adapters tag control requests with the route workflow ID, and @voltagent/server-core verifies ownership before acting. Fixes #1316.

  • Bug Fixes
    • @voltagent/server-core: enforce ownership in suspend/cancel; added createWorkflowControlRequestBody to validate JSON bodies and attach __workflowId.
    • @voltagent/server-elysia, @voltagent/server-hono, @voltagent/serverless-hono: use createWorkflowControlRequestBody to inject __workflowId; serverless-hono returns 400 for invalid JSON.
    • Added regression tests for wrong-route suspend/cancel, correct-route success, and request-body validation.

Written for commit dbd40e8. Summary will update on new commits. Review in cubic

Summary by CodeRabbit

  • Bug Fixes

    • Suspend and cancel workflow actions now verify the workflow ownership before operating; requests targeting a wrong or missing workflow will return an error, while correct-route requests proceed and respect provided reasons.
  • Tests

    • Added coverage validating failure for incorrect/missing workflow routing and success paths that invoke the control actions and clean up active executions.

Review Change Stack

@changeset-bot

changeset-bot Bot commented May 27, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: dbd40e8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@voltagent/server-core Patch
@voltagent/server-hono Patch
@voltagent/serverless-hono Patch
@voltagent/server-elysia Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4d7530dd-25cc-4bfc-9fa4-1ed27b5bc6fd

📥 Commits

Reviewing files that changed from the base of the PR and between 3c57190 and dbd40e8.

📒 Files selected for processing (6)
  • packages/server-core/src/handlers/workflow.handlers.spec.ts
  • packages/server-core/src/handlers/workflow.handlers.ts
  • packages/server-core/src/handlers/workflow.stream-attach.handlers.spec.ts
  • packages/server-elysia/src/routes/workflow.routes.ts
  • packages/server-hono/src/routes/index.ts
  • packages/serverless-hono/src/routes.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/serverless-hono/src/routes.ts
  • packages/server-core/src/handlers/workflow.stream-attach.handlers.spec.ts
  • packages/server-hono/src/routes/index.ts

📝 Walkthrough

Walkthrough

The PR enforces route-based ownership for suspend/cancel: route adapters attach the route workflow id to control request bodies, core handlers validate that the execution belongs to that workflow before acting, and tests plus a changeset document the change.

Changes

Workflow Ownership Validation

Layer / File(s) Summary
Ownership validation core logic
packages/server-core/src/handlers/workflow.handlers.ts
Introduces isWorkflowExecutionOwnedByRoute, WorkflowControlRequestBody, and createWorkflowControlRequestBody; handleSuspendWorkflow and handleCancelWorkflow now validate that the execution's stored workflowId matches the route __workflowId before proceeding.
Route adapter wiring for three server implementations
packages/server-elysia/src/routes/workflow.routes.ts, packages/server-hono/src/routes/index.ts, packages/serverless-hono/src/routes.ts
Suspend and cancel route handlers now construct a typed control body via createWorkflowControlRequestBody(parsedBody, params.id) and pass it into the core handlers instead of forwarding raw parsed JSON.
Ownership routing behavior tests
packages/server-core/src/handlers/workflow.stream-attach.handlers.spec.ts, packages/server-core/src/handlers/workflow.handlers.spec.ts
Adds startBlockingWorkflowExecution test helper and parameterized tests verifying wrong-route/missing-route requests are rejected and correct-route requests invoke controller methods and remove the active execution; also tests createWorkflowControlRequestBody behavior.
Release documentation
.changeset/workflow-control-ownership.md
Changeset updates documenting patch releases and the ownership validation change.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • VoltAgent/voltagent#1030: Modifies suspend/cancel workflow route handlers in server integrations; related to route-level control endpoints and path handling.

Suggested reviewers

  • omeraplak

Poem

🐰 I hop through code with careful cheer,
I stamp the workflow id quite clear,
No stray suspend or rogue cancel slip,
Routes and handlers now share the quip,
Tests chase the bugs — the gates hold dear.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and concisely describes the main change: validating workflow ownership on control endpoints in the server-core package.
Description check ✅ Passed The PR description is well-structured and covers all key template sections: it includes a completed checklist, clearly explains current and new behavior, references the fixed issue, and provides helpful reviewer notes.
Linked Issues check ✅ Passed The PR fully addresses issue #1316 by implementing workflow ownership validation in suspend/cancel handlers, preventing wrong-route requests from mutating executions belonging to other workflows.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing #1316: validating workflow ownership on control endpoints. The PR intentionally excludes an unrelated server-elysia test issue to maintain focus.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/server-core/src/handlers/workflow.handlers.ts (1)

639-641: ⚡ Quick win

Fail closed when route workflow id is missing.

typeof workflowId !== "string" currently returns true, which bypasses ownership validation. Prefer rejecting when __workflowId is absent/invalid so control handlers stay safe even if adapter wiring regresses.

Suggested hardening diff
 async function isWorkflowExecutionOwnedByRoute(
   body: any,
   executionId: string,
   deps: ServerProviderDeps,
 ) {
   const workflowId = body?.__workflowId;
-  return (
-    typeof workflowId !== "string" ||
-    (
-      await deps.workflowRegistry
-        .getWorkflow(workflowId)
-        ?.workflow.memory.getWorkflowState(executionId)
-    )?.workflowId === workflowId
-  );
+  if (typeof workflowId !== "string" || workflowId.length === 0) {
+    return false;
+  }
+
+  return (
+    (
+      await deps.workflowRegistry
+        .getWorkflow(workflowId)
+        ?.workflow.memory.getWorkflowState(executionId)
+    )?.workflowId === workflowId
+  );
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/server-core/src/handlers/workflow.handlers.ts` around lines 639 -
641, The current guard uses "typeof workflowId !== 'string'" which can be true
in ways that bypass ownership checks; change the condition around
workflowId/__workflowId to fail closed by treating missing or empty values as
invalid (e.g., check for typeof workflowId !== 'string' || !workflowId ||
workflowId.trim() === '') so the function (the conditional that returns based on
workflowId) will reject when __workflowId is absent/invalid and therefore will
not bypass the ownership validation; update the boolean expression in the return
that references workflowId to include the emptiness/falsy checks so ownership
validation always runs for missing/invalid IDs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/server-core/src/handlers/workflow.handlers.ts`:
- Around line 639-641: The current guard uses "typeof workflowId !== 'string'"
which can be true in ways that bypass ownership checks; change the condition
around workflowId/__workflowId to fail closed by treating missing or empty
values as invalid (e.g., check for typeof workflowId !== 'string' || !workflowId
|| workflowId.trim() === '') so the function (the conditional that returns based
on workflowId) will reject when __workflowId is absent/invalid and therefore
will not bypass the ownership validation; update the boolean expression in the
return that references workflowId to include the emptiness/falsy checks so
ownership validation always runs for missing/invalid IDs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cb33848a-1878-4003-8f17-24d4ad1a4a5c

📥 Commits

Reviewing files that changed from the base of the PR and between 5be7626 and 3c57190.

📒 Files selected for processing (6)
  • .changeset/workflow-control-ownership.md
  • packages/server-core/src/handlers/workflow.handlers.ts
  • packages/server-core/src/handlers/workflow.stream-attach.handlers.spec.ts
  • packages/server-elysia/src/routes/workflow.routes.ts
  • packages/server-hono/src/routes/index.ts
  • packages/serverless-hono/src/routes.ts

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

2 issues found across 6 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/server-elysia/src/routes/workflow.routes.ts Outdated
Comment thread packages/serverless-hono/src/routes.ts Outdated
@ruanchaves

Copy link
Copy Markdown
Contributor Author

Follow-up commit dbd40e84 addresses the latest automated review feedback:

  • CodeRabbit hardening note: workflow control ownership validation now fails closed when the route workflow id is missing or empty.
  • Issues identified by cubic: the adapter-to-server-core workflow control contract now goes through a typed helper, and the adapters no longer mutate primitive JSON bodies directly.

Validated locally with Biome, focused server-core tests, and targeted builds for server-core, server-hono, serverless-hono, and server-elysia.

@omeraplak

Copy link
Copy Markdown
Member

Hey @ruanchaves ,
Thank yo so much 🔥

@omeraplak omeraplak merged commit 91abbb4 into VoltAgent:main May 29, 2026
41 of 43 checks passed
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.

[BUG] Workflow suspend/cancel endpoints ignore the workflow ID path parameter

2 participants