Mar 6, 2026·5 min read·28 visits
The Vercel Workflow SDK allowed developers to set predictable tokens for public webhooks. Attackers can guess these tokens to manipulate running workflows without authentication. Fixed in version 4.2.0 by enforcing random token generation.
A high-severity vulnerability exists in the Vercel Workflow DevKit (`@workflow/core`) affecting the `createWebhook()` function. The vulnerability allows developers to manually specify predictable tokens for public webhook endpoints, leading to an Insecure Direct Object Reference (IDOR) flaw. Unauthenticated attackers can guess these tokens (e.g., `github_webhook:repo_name`) and send malicious POST requests to resume paused workflows, inject data, or trigger unauthorized state transitions. The vendor has patched this by removing the ability to specify custom tokens in favor of mandatory cryptographically secure identifiers.
The Vercel Workflow DevKit provides a framework for building durable, long-running workflows in TypeScript. A core feature of this library is the ability to pause a workflow and wait for an external event via a webhook. This is implemented using the createWebhook() function, which generates a unique URL that external services (like Stripe or GitHub) can call to resume execution.
Prior to version 4.2.0, createWebhook() accepted an optional token parameter. This allowed developers to manually define the identifier used in the webhook URL (e.g., https://api.app/.well-known/workflow/v1/webhook/{token}). Because developers often prioritize readability or determinism, they frequently used predictable identifiers derived from public data, such as Repository IDs, Tenant IDs, or Slack Channel IDs.
This design flaw creates a significant attack surface. By predicting these tokens, an unauthenticated attacker can construct valid webhook URLs and trigger workflow resumption. This effectively bypasses the implicit authentication of the 'secret' URL, allowing unauthorized actors to inject payloads into the workflow context.
The root cause is a design vulnerability classified as CWE-639: Authorization Bypass Through User-Controlled Key (specifically, an Insecure Direct Object Reference). The SDK relied on the secrecy of the webhook URL as the sole mechanism for authentication but simultaneously allowed users to make that URL predictable.
The vulnerability manifests in the WebhookOptions interface. By exposing the token property to the developer, the library delegated security responsibility to the user without enforcing entropy requirements. Common integration patterns encouraged the use of semantic identifiers (e.g., github_webhook:${repo.id}) rather than random strings. Since the URL format is standardized (/.well-known/workflow/v1/webhook/...), the token was the only variable component protecting the workflow endpoint.
Furthermore, the system did not implement secondary authentication (such as signature verification) by default on these endpoints, meaning knowledge of the URL was sufficient to successfully execute the webhook logic.
The vulnerability exists in the createWebhook function in @workflow/core. The patch resolves the issue by explicitly forbidding the token property in the options object and forcing the use of internal random generation.
Vulnerable Implementation (Conceptual):
// Pre-patch: Token is accepted and used directly
export function createWebhook(options) {
// If user provides a token, use it; otherwise generate one
const token = options.token || generateRandomId();
return {
url: `https://.../webhook/${token}`,
...
};
}Patched Implementation: In the fixed version, the library actively validates the input options to ensure no token is provided. If a developer attempts to pass a token, the SDK throws a runtime error, preventing the application from starting or the webhook from being created with a weak identifier.
// packages/core/src/workflow/create-hook.ts
export function createWebhook(
options?: WebhookOptions
): Webhook<Request> | Webhook<RequestWithResponse> {
// Destructure token to check for its existence
const { respondWith, token, ...rest } = (options ?? {}) as WebhookOptions & {
token?: string;
};
// CRITICAL FIX: Explicitly reject user-defined tokens
if (token !== undefined) {
throw new Error(
'`createWebhook()` does not accept a `token` option. Webhook tokens are always randomly generated. Use `createHook()` with `resumeHook()` for deterministic token patterns.'
);
}
// ... subsequent logic uses internal secure ID generation
}This change enforces a secure-by-default posture. By removing the token option from the public API surface, the library guarantees that all generated webhooks utilize high-entropy identifiers that are infeasible to guess.
An attacker can exploit this vulnerability by conducting reconnaissance on Vercel-hosted applications to identify workflow endpoints and then guessing the tokens based on the application's context.
1. Reconnaissance:
The attacker scans for the standard Vercel Workflow path: /.well-known/workflow/v1/webhook/. A 404 or 405 response indicates the path exists but the specific token is missing, confirming the usage of the SDK.
2. Token Prediction:
If the target application integrates with GitHub, an attacker might hypothesize the token format github_webhook:{repo_name}. For a repository named company/hiring-bot, the predicted token would be github_webhook:company:hiring-bot (or similar URL-safe variations).
3. Payload Injection: The attacker sends a POST request to the constructed URL:
curl -X POST https://target-app.vercel.app/.well-known/workflow/v1/webhook/github_webhook:company:hiring-bot \
-H "Content-Type: application/json" \
-d '{"action": "approve_candidate", "candidate_id": "attacker-id", "bypass_checks": true}'4. Execution: If a workflow instance is currently awaiting this webhook, it consumes the JSON payload. The workflow proceeds using the attacker's data, effectively allowing unauthenticated remote execution of subsequent workflow steps.
The impact of this vulnerability is high, specifically affecting the Integrity of the application logic. While it does not directly expose the underlying server file system (Confidentiality), it allows for significant manipulation of business logic.
CVSS v3.1 Estimate: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:L (Score: 8.2 - High). The attack vector is Network, requires no privileges, and has High Integrity impact.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:L| Product | Affected Versions | Fixed Version |
|---|---|---|
@workflow/core Vercel | < 4.2.0-beta.64 | 4.2.0-beta.64 |
| Attribute | Detail |
|---|---|
| Attack Vector | Network (Public Internet) |
| CVSS | 7.5 (High) |
| CWE | CWE-639 (IDOR) |
| Impact | Workflow Manipulation / Data Injection |
| Affected Component | createWebhook() API |
| Remediation | Upgrade to @workflow/core@4.2.0+ |
Authorization Bypass Through User-Controlled Key
A CSV Formula Injection vulnerability (CWE-1236) exists in the Spree headless eCommerce platform within the customer export functionality. An unauthenticated attacker can register a customer profile containing malicious formula sequences in fields like the first name or last name. When an administrator exports the customer data to a CSV file and opens it in a spreadsheet application, the spreadsheet engine can interpret and execute these formulas, potentially leading to remote command execution on the administrator's workstation or out-of-band data exfiltration.
A Stored Cross-Site Scripting (XSS) vulnerability exists in WWBN AVideo versions up to and including 29.0. Unsanitized category descriptions are stored in the database and subsequently rendered as raw HTML in the Gallery view plugin, allowing low-privileged authenticated users to execute arbitrary JavaScript in the browsers of visiting users.
A critical supply chain compromise was identified in the Node.js package @cap-js/openapi at version 1.4.1. An attacker gained unauthorized publishing access to the npm registry and distributed a backdoored release that harvests sensitive developer credentials, environment variables, and SSH keys. The malicious code then exfiltrates the collected data to external actor-controlled servers.
An authenticated wallet credit bypass vulnerability exists in WWBN AVideo version 29.0 and earlier. The AuthorizeNet plugin includes an unfinished mockup endpoint, processPayment.json.php, which lacks actual transaction verification and hardcodes success. This allows any authenticated user to credit their wallet with arbitrary balances without making any payments.
An unauthenticated stored DOM-based Cross-Site Scripting (DOM XSS) vulnerability in the YPTSocket plugin of WWBN AVideo (formerly YouPHPTube) allows remote attackers to execute arbitrary JavaScript within the session context of administrative users. Unsanitized metadata parameters supplied during the WebSocket handshake are persisted in an SQLite database and broadcast to connected users. The frontend application processes these parameters through an unsafe jQuery append sink, leading to silent, high-impact administrative context compromise.
A path parsing and normalization inconsistency vulnerability exists in the Hono web framework prior to version 4.12.21. When hosting sub-applications via the app.mount() routing interface, Hono calculates the routing path prefix length on a percent-decoded representation of the URI but executes the path-slicing offset on the raw, percent-encoded string. This discrepancy results in malformed request paths being dispatched to mounted sub-applications, potentially leading to route bypasses, route confusion, and application-level Denial of Service.