Budibase: Anonymous NoSQL Operator Injection via Published App Queries
Unauthenticated attackers can inject NoSQL operators into non-SQL data sources through published queries with the PUBLIC role and read as well as modify every document in the backing collections.
Advisory ID: TP-2026-012
Product: Budibase (open-source low-code platform for building internal tools and web apps on top of your own data sources)
Vulnerability type: NoSQL operator injection (CWE-943, CWE-89)
CVE: pending
CVSS 3.1: 10.0 (Critical) · CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N
Affected versions: <= 3.39.0
Fixed in: 3.39.12
Vendor advisory: GHSA-8qv3-p479-cj62
Reported: 20 May 2026
Summary
Budibase is an open-source low-code platform for building internal tools and applications on top of your own data sources. Published apps can expose queries with the PUBLIC role, which are callable without authentication and without a CSRF token. For non-SQL data sources (MongoDB, CouchDB, Elasticsearch, DynamoDB-PartiQL, and REST with a JSON body), the enrichContext function substitutes caller-supplied parameters directly into the raw JSON text of the query without escaping JSON metacharacters. The result is then evaluated with JSON.parse, so an attacker can inject closing quotes and additional keys and replace the builder’s intended filter with their own operators. As a result, an unauthenticated attacker can read and modify every document in the backing collections. turingpoint verified the flow and reported it responsibly to the vendor.
Root cause
Parameter substitution in query templates is performed as plain text replacement inside the raw JSON body for non-SQL data sources. enrichContext writes the parameter values into the body unchanged and escapes no JSON metacharacters such as quotes or braces. The existing parameter validation only rejects Handlebars markers, not JSON syntax. After substitution the body is parsed with JSON.parse into an object and, for MongoDB, passed directly to collection.find() and collection.updateMany(), so injected operators such as $exists override the intended filter. Because published queries with the PUBLIC role bypass authentication and CSRF protection, a single unauthenticated HTTP request is sufficient.
Proof of Concept
Prerequisite is a published Budibase app with at least one non-SQL query in the PUBLIC role:
# Published non-SQL query (PUBLIC role), template body e.g.:
# { "name": "{{ name }}" }
# Attacker request, no session, no CSRF token:
POST /api/v2/queries/<queryId>
x-budibase-app-id: <published-app-id>
Content-Type: application/json
{"parameters":{"name":"x\",\"name\":{\"$exists\":true},\"$comment\":\"audit"}}
# After substitution the resulting filter is:
# { "name": "x", "name": { "$exists": true }, "$comment": "audit" }
# -> the intended name filter is replaced by { "$exists": true }
# and returns or modifies every document in the collection.
Because the parameter value is written into the JSON body unescaped and then parsed, the injected { "$exists": true } replaces the intended filter and returns all documents. Applied against an update query, the response reports matchedCount and modifiedCount for the entire collection.
Impact
- Unauthenticated read access to every document in the backing collections, including sensitive fields not intended for exposure.
- Unauthenticated write, delete, and aggregate operations through published
update,delete, oraggregatequeries with thePUBLICrole. - Complete loss of confidentiality and integrity of the data behind non-SQL data sources, with no session, CSRF token, or user interaction.
- SQL data sources (PostgreSQL, MySQL, MSSQL, Oracle, MariaDB) are not affected because they use parameterized queries.
References
Is Something Like This in Your Software?
Our team found this vulnerability in the course of its work. Have your applications tested by the same specialists, with a penetration test from turingpoint.
