CVE-2026-45718: Budibase Row Action Trigger Bypasses View Vulnerability
MODERATECVE-2026-45718 is a MODERATE (CVSS 5.4) row action trigger bypasses view vulnerability (CWE-863: Incorrect Authorization) affecting Budibase libraries in the npm ecosystem. The issue involves row action trigger bypasses view row filter security boundary allowing action on out-of-scope rows. Remediation is available by upgrading affected packages to version 3.38.1.
CVE-2026-45718 at a Glance
Security Impact
CVSS: 5.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:U/C:L/I:L/A:N).
How to Fix CVE-2026-45718
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.
npm install budibase@3.38.1
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
npm ls budibase
How to Check if You Are Affected
To determine whether your Node.js application is affected by CVE-2026-45718, inspect your project dependencies using npm:
npm ls budibase
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.38.1), your application is affected.
Note: The npm ls budibase 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-863: Incorrect Authorization, where the product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check. 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
CVE-2026-45718 Affected Budibase Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
budibase
|
npm |
< 3.38.1
|
3.38.1
|
References
Frequently Asked Questions
What is CVE-2026-45718?
Which Budibase versions are affected?
What version fixes CVE-2026-45718?
Is budibase affected?
What is the CVSS score of CVE-2026-45718?
What CWE is associated with CVE-2026-45718?
How can I check npm dependencies for CVE-2026-45718?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-3263-v5v9-xq8q",
"modified" : "2026-06-08T23:51:45Z",
"published" : "2026-05-18T17:44:22Z",
"aliases" : [ "CVE-2026-45718" ],
"summary" : "Budibase: Row Action Trigger Bypasses View Row Filter Security Boundary Allowing Action on Out-of-Scope Rows",
"details" : "## Summary\n\nThe row action trigger endpoint (`POST /api/tables/:sourceId/actions/:actionId/trigger`) fails to validate that the user-supplied `rowId` is within the scope of the view's row filters. A user with access to a filtered view can trigger row actions on any row in the underlying table, including rows explicitly excluded by the view's security filters.\n\n## Details\n\nView filters in Budibase are treated as a security boundary. The search path (`packages/server/src/sdk/workspace/rows/search.ts:93-94`) explicitly enforces view query filters with the comment: *\"that could let users find rows they should not be allowed to access.\"*\n\nHowever, the row action trigger path bypasses this enforcement entirely:\n\n1. **Route** (`packages/server/src/api/routes/rowAction.ts:55-59`): Accepts a `sourceId` that can be a viewId.\n\n2. **Middleware** (`packages/server/src/middleware/triggerRowActionAuthorised.ts:24-55`): Correctly validates that the user has READ permission on the view and that the row action is enabled for that view. However, at line 55 it sets `ctx.params.tableId = tableId` where `tableId` is the **underlying table** extracted from the viewId — the viewId is discarded.\n\n```typescript\n// triggerRowActionAuthorised.ts:24-26\nconst tableId = isTableIdOrExternalTableId(sourceId)\n ? sourceId\n : getTableIdFromViewId(sourceId) // extracts underlying table\n\n// Line 55: viewId context is lost\nctx.params.tableId = tableId\n```\n\n3. **Controller** (`packages/server/src/api/controllers/rowAction/run.ts:11`): Reads only `tableId` from params — the view context is gone.\n\n```typescript\nconst { tableId, actionId } = ctx.params\nconst { rowId } = ctx.request.body\nawait sdk.rowActions.run(tableId, actionId, rowId, ctx.user)\n```\n\n4. **SDK** (`packages/server/src/sdk/workspace/rowActions/crud.ts:254`): Fetches the row using `sdk.rows.find(tableId, rowId)` — directly from the table with no view filter enforcement.\n\n```typescript\nconst row = await sdk.rows.find(tableId, rowId) // No view filter check\n```\n\nThe `sdk.rows.find` function (`packages/server/src/sdk/workspace/rows/internal.ts:67-88`) fetches the row by ID directly from the database, only validating that `row.tableId === tableId`. It never checks whether the row matches the view's query filters.\n\n## PoC\n\n```bash\n# Prerequisites:\n# 1. Create a table with a \"status\" column containing rows: \"active\" and \"archived\"\n# 2. Create a view filtering to status=\"active\", assign it to BASIC role\n# 3. Enable a row action for that view\n# 4. Note the rowId of an \"archived\" row (not visible through the view)\n\n# As a BASIC-role user with access only to the filtered view:\n# Trigger the row action on a row OUTSIDE the view's filter scope\n\ncurl -X POST 'http://localhost:10000/api/tables/<viewId>/actions/<actionId>/trigger' \\\n -H 'Cookie: budibase:auth=<basic_user_jwt>' \\\n -H 'Content-Type: application/json' \\\n -d '{\"rowId\": \"<archived_row_id>\"}'\n\n# Expected: 403 or 404 (row not in view scope)\n# Actual: 200 {\"message\": \"Row action triggered.\"}\n# The automation executes with the full archived row data,\n# despite view filters excluding it from the user's access.\n```\n\n## Impact\n\nA user with BASIC role access to a filtered view can execute row actions (automations) on **any row** in the underlying table, including rows hidden by the view's security filters. The impact depends on what the triggered automation does:\n\n- **Information disclosure**: The automation receives the full row data as input, which may contain fields/values the user should not see.\n- **Unauthorized data modification**: If the automation modifies rows, the attacker can cause changes to rows outside their authorized scope.\n- **Unauthorized actions**: If the automation sends notifications, calls webhooks, or performs other side effects, the attacker can trigger these for out-of-scope rows.\n\nThis breaks the security model established by view filters, which are explicitly documented as preventing users from accessing rows they should not see.\n\n## Recommended Fix\n\nThe middleware should pass the `viewId` to the controller, and the SDK `run` function should validate the row against the view's filters before executing the automation.\n\nIn `packages/server/src/middleware/triggerRowActionAuthorised.ts`, preserve the sourceId:\n\n```typescript\n// Line 55: preserve the original sourceId for downstream filter validation\nctx.params.tableId = tableId\nctx.params.sourceId = viewId || tableId // ADD THIS\n```\n\nIn `packages/server/src/api/controllers/rowAction/run.ts`, pass the sourceId:\n\n```typescript\nexport async function run(\n ctx: Ctx<RowActionTriggerRequest, RowActionTriggerResponse>\n) {\n const { tableId, actionId, sourceId } = ctx.params\n const { rowId } = ctx.request.body\n\n await sdk.rowActions.run(tableId, actionId, rowId, ctx.user, sourceId)\n ctx.body = { message: \"Row action triggered.\" }\n}\n```\n\nIn `packages/server/src/sdk/workspace/rowActions/crud.ts`, validate the row against view filters:\n\n```typescript\nexport async function run(\n tableId: any,\n rowActionId: any,\n rowId: string,\n user: User,\n sourceId?: string\n) {\n const table = await sdk.tables.getTable(tableId)\n if (!table) {\n throw new HTTPError(\"Table not found\", 404)\n }\n\n // If triggered from a view, validate the row is within the view's scope\n if (sourceId && isViewId(sourceId)) {\n const result = await sdk.rows.search({\n viewId: sourceId,\n query: { equal: { _id: rowId } },\n limit: 1,\n })\n if (!result.rows.length) {\n throw new HTTPError(\"Row not found in view scope\", 403)\n }\n }\n\n const { automationId } = await get(tableId, rowActionId)\n const automation = await sdk.automations.get(automationId)\n const row = await sdk.rows.find(tableId, rowId)\n // ... rest unchanged\n}\n```",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "npm",
"name" : "budibase"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "3.38.1"
} ]
} ]
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/Budibase/budibase/security/advisories/GHSA-3263-v5v9-xq8q"
}, {
"type" : "ADVISORY",
"url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-45718"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/Budibase/budibase"
}, {
"type" : "WEB",
"url" : "https://github.com/Budibase/budibase/releases/tag/3.38.1"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-863" ],
"severity" : "MODERATE",
"github_reviewed" : true,
"github_reviewed_at" : "2026-05-18T17:44:22Z",
"nvd_published_at" : "2026-05-27T18:16:25Z"
}
}