Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
📦
📦 npm
Not in CISA KEV
MEDIUM severity

GHSA-3263-v5v9-xq8q

MEDIUM

GHSA-3263-v5v9-xq8q is a medium-severity (CVSS 5.4) CWE-863 vulnerability in budibase. O3 Security confirms whether GHSA-3263-v5v9-xq8q is actually reachable in your code before you act, and blocks exploitation at runtime until you patch.

Budibase: Row Action Trigger Bypasses View Row Filter Security Boundary Allowing Action on Out-of-Scope Rows

Also known asCVE-2026-45718
Published
May 18, 2026
Updated
Jun 9, 2026
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Aug 14, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

Proof-of-concept exploit code exists

  • CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.

Exploitation and automatability from CISA’s SSVC triage for GHSA-3263-v5v9-xq8q.

EPSS Exploitation Probability

via FIRST.org ↗
0.1%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs4th percentile — riskier than 4% of all scored CVEsHighest risk
0.00%0.22%0.43%0.65%0.0%0.1%0.1%0.1%Jun 26Aug 26Aug 26

EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.

How urgent is this, really

GHSA-3263-v5v9-xq8q plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.

Where this sits among everything scored

Of 360,142 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.

Real-World Exposure

1 pkg affected

How broadly this vulnerability is actually deployed: weekly install volume shows current usage, a proxy for how much of the ecosystem is exposed.

budibasenpm
27downloads / week

Description

Summary

The 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.

Details

View 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."

However, the row action trigger path bypasses this enforcement entirely:

  1. Route (packages/server/src/api/routes/rowAction.ts:55-59): Accepts a sourceId that can be a viewId.

  2. 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.

// triggerRowActionAuthorised.ts:24-26
const tableId = isTableIdOrExternalTableId(sourceId)
  ? sourceId
  : getTableIdFromViewId(sourceId)  // extracts underlying table

// Line 55: viewId context is lost
ctx.params.tableId = tableId
  1. Controller (packages/server/src/api/controllers/rowAction/run.ts:11): Reads only tableId from params — the view context is gone.
const { tableId, actionId } = ctx.params
const { rowId } = ctx.request.body
await sdk.rowActions.run(tableId, actionId, rowId, ctx.user)
  1. 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.
const row = await sdk.rows.find(tableId, rowId)  // No view filter check

The 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.

PoC

# Prerequisites:
# 1. Create a table with a "status" column containing rows: "active" and "archived"
# 2. Create a view filtering to status="active", assign it to BASIC role
# 3. Enable a row action for that view
# 4. Note the rowId of an "archived" row (not visible through the view)

# As a BASIC-role user with access only to the filtered view:
# Trigger the row action on a row OUTSIDE the view's filter scope

curl -X POST 'http://localhost:10000/api/tables/<viewId>/actions/<actionId>/trigger' \
  -H 'Cookie: budibase:auth=<basic_user_jwt>' \
  -H 'Content-Type: application/json' \
  -d '{"rowId": "<archived_row_id>"}'

# Expected: 403 or 404 (row not in view scope)
# Actual: 200 {"message": "Row action triggered."}
# The automation executes with the full archived row data,
# despite view filters excluding it from the user's access.

Impact

A 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:

  • Information disclosure: The automation receives the full row data as input, which may contain fields/values the user should not see.
  • Unauthorized data modification: If the automation modifies rows, the attacker can cause changes to rows outside their authorized scope.
  • Unauthorized actions: If the automation sends notifications, calls webhooks, or performs other side effects, the attacker can trigger these for out-of-scope rows.

This breaks the security model established by view filters, which are explicitly documented as preventing users from accessing rows they should not see.

Recommended Fix

The 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.

In packages/server/src/middleware/triggerRowActionAuthorised.ts, preserve the sourceId:

// Line 55: preserve the original sourceId for downstream filter validation
ctx.params.tableId = tableId
ctx.params.sourceId = viewId || tableId  // ADD THIS

In packages/server/src/api/controllers/rowAction/run.ts, pass the sourceId:

export async function run(
  ctx: Ctx<RowActionTriggerRequest, RowActionTriggerResponse>
) {
  const { tableId, actionId, sourceId } = ctx.params
  const { rowId } = ctx.request.body

  await sdk.rowActions.run(tableId, actionId, rowId, ctx.user, sourceId)
  ctx.body = { message: "Row action triggered." }
}

In packages/server/src/sdk/workspace/rowActions/crud.ts, validate the row against view filters:

export async function run(
  tableId: any,
  rowActionId: any,
  rowId: string,
  user: User,
  sourceId?: string
) {
  const table = await sdk.tables.getTable(tableId)
  if (!table) {
    throw new HTTPError("Table not found", 404)
  }

  // If triggered from a view, validate the row is within the view's scope
  if (sourceId && isViewId(sourceId)) {
    const result = await sdk.rows.search({
      viewId: sourceId,
      query: { equal: { _id: rowId } },
      limit: 1,
    })
    if (!result.rows.length) {
      throw new HTTPError("Row not found in view scope", 403)
    }
  }

  const { automationId } = await get(tableId, rowActionId)
  const automation = await sdk.automations.get(automationId)
  const row = await sdk.rows.find(tableId, rowId)
  // ... rest unchanged
}

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
📦npmbudibaseall versions3.38.1

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for budibase. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.

  2. Fix

    Update budibase to 3.38.1 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-3263-v5v9-xq8q is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

  4. How O3 protects you

    O3 pinpoints whether GHSA-3263-v5v9-xq8q is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.

Tailored to GHSA-3263-v5v9-xq8q. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.

Frequently Asked Questions

## Summary The 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. ## Details View 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
O3 Security · Impact-Aware SCA

Is GHSA-3263-v5v9-xq8q in your dependencies?

O3 detects GHSA-3263-v5v9-xq8q across npm dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.

GHSA-3263-v5v9-xq8q: budibase Information… | O3 Security