Krytyczna

Budibase has nonymous NoSQL operator injection via published-app query templates

  • Ujawniono: 23.06.2026
  • Aktualizacja: 11.10.2026 10:22

Summary

enrichContext at packages/server/src/sdk/workspace/queries/queries.ts:121-138 substitutes parameter values into the raw JSON body of a query, then JSON.parses the result. The validator validateQueryInputs at packages/server/src/api/controllers/query/index.ts:61-71 rejects only Handlebars markers ({{, }}) in user input and does not escape JSON metacharacters (", \, }). A parameter value containing a closing quote and additional keys lifts attacker-controlled fields into the parsed filter object.

For Mongo find, the parsed filter passes directly to collection.find() (packages/server/src/integrations/mongodb.ts:506-510). Duplicate-key JSON parsing overrides the builder's {name: "..."} with {name: {$exists: true}} and returns every document. The same primitive against an updateMany query (mongodb.ts:577-585) widens the filter scope to the full collection while the builder-controlled $set body runs against every matched document.

The authorized middleware at packages/server/src/middleware/authorized.ts:141-148 short-circuits when the query's role is PUBLIC. CSRF is not enforced on this path. POST /api/v2/queries/:queryId (packages/server/src/api/routes/query.ts:63) accepts the call with no session, only an x-budibase-app-id header that is public from the published-app URL.

Result: an unauthenticated visitor of any published Budibase app reads every document of the backing MongoDB, CouchDB, Elasticsearch, DynamoDB-PartiQL, or REST-with-JSON-body collection and, where the builder has published a PUBLIC write query, modifies every document of that collection with one HTTP request.

Affected

Budibase/budibase server, @budibase/server package, <= 3.39.0 (HEAD feab995, released 2026-05-20).

Reachable on any deployment where a workspace builder has set the role of a non-SQL query (MongoDB, CouchDB, Elasticsearch, DynamoDB-PartiQL, or REST with bodyType=json) to PUBLIC and published the app. This is the canonical low-code public-form use case.

SQL datasources (Postgres, MySQL, MSSQL, Oracle, MariaDB) route through interpolateSQL and are not affected.

Root cause

packages/server/src/sdk/workspace/queries/queries.ts:121-138: processStringSync(fields[key], parameters, {noEscaping: true, noHelpers: true}) writes the raw parameter value into the JSON-body string with no JSON-string escape; the followup JSON.parse(enrichedQuery.json || enrichedQuery.customData || enrichedQuery.requestBody) lifts the substituted text into the integration filter object.

packages/server/src/api/controllers/query/index.ts:61-71: validateQueryInputs only rejects values where findHBSBlocks(value).length !== 0 (Handlebars markers) and ignores JSON metacharacters.

packages/server/src/integrations/mongodb.ts:506-510: collection.find(json) receives the user-controlled filter object directly with no key prefix or operator allow-list.

packages/server/src/integrations/mongodb.ts:577-585: collection.updateMany(json.filter, json.update, json.options) accepts the templated filter without verifying that the substituted filter still matches the builder's intent.

packages/server/src/middleware/authorized.ts:141-148: if (resourceRoles.includes(roles.BUILTIN_ROLE_IDS.PUBLIC)) return next() skips both authentication and CSRF.

packages/server/src/integrations/queries/sql.ts:29-122: interpolateSQL rewrites every {{ binding }} to a positional bind placeholder ($N or ?). The SQL leg is bind-parameterised; the JSON leg is not.

Reproduction

budibase/budibase:latest (v3.39.0) Docker single-container, default config. Builder logs in once, creates a MongoDB datasource, creates a query GetUserByName with body { "name": "{{ name }}" }, sets the query role to PUBLIC, and publishes the app.

  1. Anonymous client sends the inject payload to the read query.
http
POST /api/v2/queries/<read-queryId> HTTP/1.1
Host: <budibase-host>
x-budibase-app-id: <published-appId>
Content-Type: application/json

{"parameters":{"name":"x\",\"name\":{\"$exists\":true},\"$comment\":\"audit"}}
js
{"data":[
  {"_id":"...","name":"alice","secret":"alice-secret-flag"},
  {"_id":"...","name":"bob","secret":"bob-secret-flag"},
  {"_id":"...","name":"admin","role":"admin","secret":"ADMIN-SUPER-SECRET-FLAG"}
]}
  1. Builder publishes a second query TouchUser (verb update, action updateMany, body { "filter": { "name": "{{ name }}" }, "update": { "$set": { "touched": true } } }, role PUBLIC). Anonymous client sends the same inject pattern.
http
POST /api/v2/queries/<updateMany-queryId> HTTP/1.1
Host: <budibase-host>
x-budibase-app-id: <published-appId>
Content-Type: application/json

{"parameters":{"name":"x\",\"name\":{\"$exists\":true},\"$comment\":\"esc"}}
js
{"data":[{"acknowledged":true,"matchedCount":3,"modifiedCount":3,"upsertedId":null,"upsertedCount":0}]}

Live-verified: against Budibase v3.39.0 on 2026-05-20, anonymous read returned every document including ADMIN-SUPER-SECRET-FLAG; anonymous updateMany reported matchedCount: 3, modifiedCount: 3 against a 3-document collection where the builder's filter intended name = "x".

Impact

  • Anonymous read of every document in any backing MongoDB, CouchDB, Elasticsearch, DynamoDB-PartiQL, or REST-with-JSON-body collection reachable through a PUBLIC query, including columns the published query was not designed to return (password_hash, secret, api_token, mfa_secret).
  • Anonymous modification of every document of that collection where the builder has published a PUBLIC update, delete, or aggregate query, beyond the builder's intended single-document scope.
  • One HTTP request, no session, no CSRF, no user interaction.

Credit

Jan Kahmen, turingpoint (jan@turingpoint.de).

Czego dotyczy

Kliknij technologię, aby zobaczyć wszystkie dotyczące jej podatności.

PlatformaPaczkaPodatne wersjePoprawka w wersji
Paczka npm @budibase/server < 3.39.12 3.39.12

Odnośniki

Ocena CVSS 10/10
Wektor CVSS
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N
Słabość (CWE)
CWE-89, CWE-943
Platforma
npm
Paczka
@budibase/server
Ujawniono
23.06.2026
Aktualizacja
11.10.2026 10:22

Źródła i licencje

OSV / GitHub Advisory Database - GHSA-8qv3-p479-cj62 - CC BY 4.0

Historia podatności: @budibase/server

Wszystkie opisane podatności tej paczki (Paczka npm), od najnowszej. Widać, czy to jednorazowa wpadka, czy paczka, której trzeba pilnować.

Wszystkie (42) w archiwum
Wszystkich wpisów
42
Pierwsza → ostatnia
03.2024 08.2026
Bez poprawki
19
Wykorzystywana w atakach
0

Rozkład istotności

  • Krytyczna 8
  • Wysoka 27
  • Średnia 7

Ujawnienia w latach

  1. 2024: 1
  2. 2025: 0
  3. 2026: 41
Istotność:
  1. Wysoka · 8.5 CVE-2026-73410 <= 3.38.1 brak poprawki
  2. Wysoka · 7.7 CVE-2026-72849 <= 3.38.1 brak poprawki
  3. Krytyczna · 9.6 CVE-2026-73300 <= 3.38.1 brak poprawki
  4. Budibase has nonymous NoSQL operator injection via published-app query templates ten wpis
    Krytyczna · 10.0 CVE-2026-54350 < 3.39.12 3.39.12
Pokaż więcej (15) Widać 20 z 35

Dowiedz się, które podatności dotyczą Twojej aplikacji

Po podłączeniu integracji DockRay i włączeniu raportowania oprogramowania DockRay porównuje zgłoszone wersje komponentów ze znanymi podatnościami i powiadamia o potwierdzonych dopasowaniach. Zakres zależy od obsługiwanej technologii i dostępnych biuletynów.

Podłącz swoją aplikację
Newsletter DockRay

Bezpieczeństwo aplikacji w Twojej skrzynce

Najważniejsze podatności, konkretne zalecenia i praktyczne wskazówki dla Twoich aplikacji. Wybierz technologie i śledź to, co ma znaczenie dla Twojego projektu.

  • Codzienne zestawienie nowych podatności, także krytycznych.
  • Powiadomienia o ważnych aktualizacjach.
  • Praktyczne porady o bezpieczeństwie i monitoringu aplikacji.

Pozostaw puste, aby otrzymywać informacje o wszystkich technologiach.

Zapis jest bezpłatny. Wypiszesz się jednym kliknięciem w wiadomości.