CVE-2026-50136: Budibase Remote Code Execution Vulnerability

HIGH
Advisory Identifier: GHSA-jj36-r9w3-3pfh
CVSS 7.4
CWE-306
Published
Advisory Summary

CVE-2026-50136 is a HIGH (CVSS 7.4) remote code execution vulnerability (CWE-306: Missing Authentication for Critical Function) affecting Budibase libraries in the npm ecosystem. The issue involves unauthenticated s3 signed upload url generation allows arbitrary writes with stored datasource credentials. Remediation is available by upgrading affected packages to version 3.39.2.

CVE-2026-50136 at a Glance

CVE Identifier CVE-2026-50136
GHSA Identifier GHSA-jj36-r9w3-3pfh
Severity HIGH
CVSS v3.1 Base Score 7.4
CWE Classification CWE-306: Missing Authentication for Critical Function
Affected Product Budibase
Package Ecosystem npm
Published Date
Last Updated

Security Impact

CVSS: 7.4

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:L).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required Low (PR:L)
User Interaction None (UI:N)
Scope Changed (S:C)
Confidentiality Low (C:L)
Integrity Low (I:L)
Availability Low (A:L)

How to Fix CVE-2026-50136

To remediate this vulnerability, upgrade all affected Budibase dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

@budibase/server fixed: 3.39.2
npm install @budibase/server@3.39.2
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

npm ls @budibase/server

How to Check if You Are Affected

To determine whether your Node.js application is affected by CVE-2026-50136, inspect your project dependencies using npm:

npm ls @budibase/server

Analyze the terminal output to locate any version of Budibase in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 3.39.2), your application is affected.

Note: The npm ls @budibase/server command inspects dependencies declared in your package.json and lockfile; it does not perform automated runtime dynamic scanning.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-306: Missing Authentication for Critical Function, where the product does not perform any authentication for functionality that requires a provable user identity or consumes a significant amount of resources. In Budibase, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

The application server exposes an unauthenticated endpoint that generates S3 `PutObject` presigned URLs using credentials stored in a workspace datasource. The route is protected only by the recaptcha middleware and does not require authentication, table permission, datasource permission, or builder access. A public caller who knows a workspace ID and S3 datasource ID can request a signed upload URL for attacker-controlled bucket and key values. ### Details The static route registers the signed upload URL endpoint with only `recaptcha` before the controller: - `packages/server/src/api/routes/static.ts:44-48` ```ts 44: .post( 45: "/api/attachments/:datasourceId/url", 46: recaptcha, 47: controller.getSignedUploadURL 48: ) ``` The controller loads the datasource by `datasourceId` with enriched secret values: - `packages/server/src/api/controllers/static/index.ts:590-598` ```ts 590:export const getSignedUploadURL = async function ( 591: ctx: Ctx<GetSignedUploadUrlRequest, GetSignedUploadUrlResponse> 592:) { 593: // Ensure datasource is valid 594: let datasource 595: try { 596: const { datasourceId } = ctx.params 597: datasource = await sdk.datasources.get(datasourceId, { enriched: true }) 598: if (!datasource) { ``` The request body controls `bucket` and `key`, and the server signs a PUT URL using the stored datasource credentials: - `packages/server/src/api/controllers/static/index.ts:609-629` ```ts 609: if (datasource?.source === "S3") { 610: const { bucket, key } = ctx.request.body || {} 611: if (!bucket || !key) { 612: ctx.throw(400, "bucket and key values are required") 613: } 614: try { 615: let endpoint = datasource?.config?.endpoint 616: if (endpoint && !utils.urlHasProtocol(endpoint)) { 617: endpoint = `https://${endpoint}` 618: } 619: const s3 = new S3({ 620: region: awsRegion, 621: endpoint: endpoint, 622: credentials: { 623: accessKeyId: datasource?.config?.accessKeyId as string, 624: secretAccessKey: datasource?.config?.secretAccessKey as string, 625: }, 626: }) 627: const params = { Bucket: bucket, Key: key } 628: signedUrl = await getSignedUrl(s3, new PutObjectCommand(params)) 629: if (endpoint) { ``` The endpoint returns the signed URL and public URL to the caller: - `packages/server/src/api/controllers/static/index.ts:630-639` ```ts 630: publicUrl = `${endpoint}/${bucket}/${key}` 631: } else { 632: publicUrl = `https://${bucket}.s3.${awsRegion}.amazonaws.com/${key}` 633: } 634: } catch (error: any) { 635: ctx.throw(400, error) 636: } 637: } 638: 639: ctx.body = { signedUrl, publicUrl } ``` Because no authorization middleware is applied, the API trusts public input to choose where the stored S3 credentials will write. ### PoC Non-destructive validation approach: 1. Create or identify a workspace with an S3 datasource. 2. Obtain the production workspace ID and S3 datasource ID. 3. Send an unauthenticated request with the workspace ID header and attacker-controlled bucket/key: ```http POST /api/attachments/<datasourceId>/url HTTP/1.1 x-budibase-app-id: app_<workspace-id> content-type: application/json {"bucket":"attacker-controlled-or-permitted-bucket","key":"poc/budibase.txt"} ``` 4. Observe that the response contains a signed PUT URL. 5. Upload harmless content to the returned `signedUrl` and confirm the object is created using the datasource's stored S3 credentials. ### Impact This allows unauthenticated arbitrary object writes wherever the stored S3 datasource credentials have `PutObject` access. Depending on the datasource permissions, this can corrupt application data, overwrite public assets, place attacker-controlled objects in trusted buckets, consume storage, or abuse an organization's cloud credentials.

CVE-2026-50136 Affected Budibase Versions

1 component(s)
Fixed Versions:
3.39.2
Product / Package Ecosystem Affected Versions Fixed Version
@budibase/server
npm
< 3.39.2
3.39.2

References

Frequently Asked Questions

What is CVE-2026-50136?
CVE-2026-50136 is a security vulnerability in Budibase involving Unauthenticated S3 signed upload URL generation allows arbitrary writes with stored datasource credentials under CWE-306: Missing Authentication for Critical Function.
Which Budibase versions are affected?
Affected versions include releases prior to 3.39.2 across affected packages: @budibase/server.
What version fixes CVE-2026-50136?
Upgrading to 3.39.2 (or the latest patched release for your respective branch) resolves the vulnerability.
Is @budibase/server affected?
Yes, @budibase/server is affected in releases prior to 3.39.2. Upgrade your dependency to 3.39.2 or later.
What is the CVSS score of CVE-2026-50136?
CVE-2026-50136 has a CVSS v3.1 base score of 7.4 (HIGH) with vector string CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:L.
What CWE is associated with CVE-2026-50136?
CVE-2026-50136 is classified under CWE-306: Missing Authentication for Critical Function (The product does not perform any authentication for functionality that requires a provable user identity or consumes a significant amount of resources.).
How can I check npm dependencies for CVE-2026-50136?
Run the dependency tree command: 'npm ls @budibase/server'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-jj36-r9w3-3pfh",
  "modified" : "2026-07-17T16:59:09Z",
  "published" : "2026-06-22T23:15:10Z",
  "aliases" : [ "CVE-2026-50136" ],
  "summary" : "Budibase: Unauthenticated S3 signed upload URL generation allows arbitrary writes with stored datasource credentials",
  "details" : "The application server exposes an unauthenticated endpoint that generates S3 `PutObject` presigned URLs using credentials stored in a workspace datasource. The route is protected only by the recaptcha middleware and does not require authentication, table permission, datasource permission, or builder access. A public caller who knows a workspace ID and S3 datasource ID can request a signed upload URL for attacker-controlled bucket and key values.\n\n### Details\n\nThe static route registers the signed upload URL endpoint with only `recaptcha` before the controller:\n\n- `packages/server/src/api/routes/static.ts:44-48`\n\n```ts\n44:  .post(\n45:    \"/api/attachments/:datasourceId/url\",\n46:    recaptcha,\n47:    controller.getSignedUploadURL\n48:  )\n```\n\nThe controller loads the datasource by `datasourceId` with enriched secret values:\n\n- `packages/server/src/api/controllers/static/index.ts:590-598`\n\n```ts\n590:export const getSignedUploadURL = async function (\n591:  ctx: Ctx<GetSignedUploadUrlRequest, GetSignedUploadUrlResponse>\n592:) {\n593:  // Ensure datasource is valid\n594:  let datasource\n595:  try {\n596:    const { datasourceId } = ctx.params\n597:    datasource = await sdk.datasources.get(datasourceId, { enriched: true })\n598:    if (!datasource) {\n```\n\nThe request body controls `bucket` and `key`, and the server signs a PUT URL using the stored datasource credentials:\n\n- `packages/server/src/api/controllers/static/index.ts:609-629`\n\n```ts\n609:  if (datasource?.source === \"S3\") {\n610:    const { bucket, key } = ctx.request.body || {}\n611:    if (!bucket || !key) {\n612:      ctx.throw(400, \"bucket and key values are required\")\n613:    }\n614:    try {\n615:      let endpoint = datasource?.config?.endpoint\n616:      if (endpoint && !utils.urlHasProtocol(endpoint)) {\n617:        endpoint = `https://${endpoint}`\n618:      }\n619:      const s3 = new S3({\n620:        region: awsRegion,\n621:        endpoint: endpoint,\n622:        credentials: {\n623:          accessKeyId: datasource?.config?.accessKeyId as string,\n624:          secretAccessKey: datasource?.config?.secretAccessKey as string,\n625:        },\n626:      })\n627:      const params = { Bucket: bucket, Key: key }\n628:      signedUrl = await getSignedUrl(s3, new PutObjectCommand(params))\n629:      if (endpoint) {\n```\n\nThe endpoint returns the signed URL and public URL to the caller:\n\n- `packages/server/src/api/controllers/static/index.ts:630-639`\n\n```ts\n630:        publicUrl = `${endpoint}/${bucket}/${key}`\n631:      } else {\n632:        publicUrl = `https://${bucket}.s3.${awsRegion}.amazonaws.com/${key}`\n633:      }\n634:    } catch (error: any) {\n635:      ctx.throw(400, error)\n636:    }\n637:  }\n638:\n639:  ctx.body = { signedUrl, publicUrl }\n```\n\nBecause no authorization middleware is applied, the API trusts public input to choose where the stored S3 credentials will write.\n\n### PoC\n\nNon-destructive validation approach:\n\n1. Create or identify a workspace with an S3 datasource.\n2. Obtain the production workspace ID and S3 datasource ID.\n3. Send an unauthenticated request with the workspace ID header and attacker-controlled bucket/key:\n\n```http\nPOST /api/attachments/<datasourceId>/url HTTP/1.1\nx-budibase-app-id: app_<workspace-id>\ncontent-type: application/json\n\n{\"bucket\":\"attacker-controlled-or-permitted-bucket\",\"key\":\"poc/budibase.txt\"}\n```\n\n4. Observe that the response contains a signed PUT URL.\n5. Upload harmless content to the returned `signedUrl` and confirm the object is created using the datasource's stored S3 credentials.\n\n### Impact\n\nThis allows unauthenticated arbitrary object writes wherever the stored S3 datasource credentials have `PutObject` access. Depending on the datasource permissions, this can corrupt application data, overwrite public assets, place attacker-controlled objects in trusted buckets, consume storage, or abuse an organization's cloud credentials.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:L"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "npm",
      "name" : "@budibase/server"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "3.39.2"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/Budibase/budibase/security/advisories/GHSA-jj36-r9w3-3pfh"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-50136"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/Budibase/budibase/pull/18774"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/Budibase/budibase/commit/d9dbb7f6105373cc88ecacdbcab70c776f7dd6a1"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/Budibase/budibase"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-306" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-06-22T23:15:10Z",
    "nvd_published_at" : "2026-06-26T21:16:34Z"
  }
}