Feb 10, 2026·6 min read·44 visits
An authenticated attacker can escalate privileges by injecting a `securityContext` object into WebSocket messages or triggering a race condition in the API gateway. This allows bypassing Row-Level Security (RLS) and multi-tenant isolation, granting full admin access or data visibility across tenants.
Cube (formerly Cube.js) is the self-proclaimed 'semantic layer' for building data applications—a fancy way of saying it sits between your messy SQL databases and your pretty frontend charts. In versions prior to 1.5.13, Cube suffered from a catastrophic logic error in its WebSocket gateway and async handling. By simply asking nicely (read: injecting a JSON object), a low-privileged user could override their own security context. Even worse, a race condition in the promise chain allowed user contexts to 'bleed' into one another, meaning your request for 'My Sales' might accidentally return 'competitor_sales' if the server was busy enough. This is a classic case of trusting client input combined with the distinct misery of Node.js event loop management.
In modern data stacks, 'Semantic Layers' are the new hotness. They act as the single source of truth, translating abstract business logic (like 'Revenue') into concrete SQL queries. Cube is the leader in this space. It handles authentication, caching, and query orchestration. Ideally, it's the bouncer at the club, checking IDs and ensuring you only sit at the table you paid for.
But here's the thing about bouncers: if they are distracted or easily bribed, the whole security model collapses. Cube relies heavily on 'Security Contexts'—objects defining who you are and what you can see (e.g., tenant_id: 123). This context is usually derived from a cryptographically signed JWT. You trust the JWT, you trust the context.
CVE-2026-25958 reveals that Cube didn't just trust the JWT. It also trusted the WebSocket packet you just sent it. It’s the equivalent of the bouncer checking your ID, then asking, 'And what VIP level would you like to be today?' and actually writing it down on your wristband.
This vulnerability is a two-headed beast. The first head is WebSocket Parameter Pollution. When you connect to Cube via WebSockets, you send JSON messages to trigger data loads. The backend logic took the user's provided message and merged it into the internal state. The developers failed to sanitize the input for reserved keys like securityContext.
If you sent a payload containing securityContext: { isAdmin: true }, the application effectively said, 'Sure, seems legit,' and merged that flag into your active session. This allows for trivial privilege escalation. You aren't cracking a password; you're just politely informing the server that you are, in fact, the boss.
The second head is a Race Condition in the Async Boundary. In packages/cubejs-api-gateway/src/gateway.ts, specific core methods (like meta and sql) invoked response callbacks without properly awaiting the result. In the Node.js event loop, this created a 'broken promise chain'.
Because the request context wasn't strictly bound to the execution scope of the database query, high-concurrency traffic could cause Context Bleeding. The security context of User A (an Admin) could leak into the response processing of User B (a Viewer), accidentally serving privileged data to the wrong socket.
Let's look at the failure in the WebSocket handler. The code was iterating over the message parameters and blindly applying them. It lacked a 'strict' schema validation.
The Vulnerable Pattern (Simplified):
// Inside the WebSocket message handler
const processMessage = (message, connection) => {
// message.params comes directly from the user
const queryContext = {
...connection.userContext, // The real context from JWT
...message.params // The untrusted input overwriting it
};
// queryContext now has user-injected 'isAdmin: true'
return executeQuery(queryContext);
};The Fix (Commit 6271520):
The patch introduced zod schemas to strictly validate inputs. Instead of a spread operator that merges everything, the new code explicitly parses only allowed fields.
// The Fix: Strict Schema Validation
const LoadRequestSchema = z.object({
query: QuerySchema,
queryType: z.string().optional(),
// securityContext is notably ABSENT from the allowed schema
});
const safeParams = LoadRequestSchema.parse(message.params);
// Now safeParams only contains what the devs intended.Additionally, in gateway.ts (Commit 9e9f9d1), they had to fix the async bleeding.
Before:
this.loadImpl(options, (err, res) => {
// Callback hell leading to context loss
});After:
// Forcing the promise to resolve within the correct context scope
await this.loadImpl(options);By forcing the await, the AsyncLocalStorage context remains consistent throughout the lifecycle of the request.
To exploit this, you don't need fancy binary tools. You just need a WebSocket client and a valid low-privileged JWT. The goal is to bypass the Row-Level Security (RLS) that keeps tenants separate.
Step 1: Connect Establish a standard WebSocket connection to the Cube API. Authenticate with your low-privilege JWT.
Step 2: Craft the Payload
Construct a load message. The documentation says you should send a query. The exploit says you should send a query AND a securityContext.
{
"messageId": "1337",
"method": "load",
"params": {
"query": {
"measures": ["Orders.totalRevenue"],
"dimensions": ["Orders.createdAt"]
},
"securityContext": {
"u": {
"id": 1,
"roles": ["admin"],
"tenantId": "*"
}
}
}
}Step 3: Execution
Send the JSON. The server deserializes the message. The securityContext provided in params overrides the one extracted from your JWT. Cube generates the SQL. Instead of WHERE tenant_id = 'your_id', it generates WHERE 1=1 (or whatever the admin rule is).
Step 4: Profit The server streams back the financial data for every tenant on the platform. You have successfully democratized the data, whether they wanted you to or not.
This is a Critical Severity issue for any multi-tenant SaaS application using Cube. The impact is not just data leakage; it's total isolation failure.
order parameter (CWE-89), this could escalate further into blind SQL injection via the metadata generation layer.The business impact? If you are a B2B SaaS provider, your customers' competitors just got access to their sales numbers. Trust is the first casualty.
Remediation is straightforward but urgent. You must upgrade the Cube core packages.
Action Items:
1.5.13, 1.4.2, or 1.0.14 immediately. These versions introduce the Zod schema validation that strips the malicious payload.securityContext key in the parameters. This key should effectively never come from the client side.CUBEJS_WEB_SOCKETS=false. This reduces the attack surface significantly, as the REST API uses a different parsing path that was less susceptible to this specific pollution attack.Developer Lesson: Never use the spread operator (...) on user input when building security contexts. It's concise, it's pretty, and it's dangerous.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Cube Cube Dev | 0.27.19 - < 1.0.14 | 1.0.14 |
Cube Cube Dev | 1.1.0 - < 1.4.2 | 1.4.2 |
Cube Cube Dev | 1.5.0 - < 1.5.13 | 1.5.13 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-807 |
| Attack Vector | Network (WebSocket) |
| CVSS | 7.7 (High) |
| Impact | Privilege Escalation / Info Disclosure |
| Exploit Status | PoC Available |
| Fix Complexity | Low (Version Upgrade) |
The application relies on untrusted inputs to make security decisions, specifically allowing client-side overriding of the security context.
CVE-2026-115895 is a high-severity code injection vulnerability (CWE-94) in the happy-dom npm package prior to version 21.0.4. The vulnerability exists within the ECMAScriptModuleCompiler component, which transforms ECMAScript module syntax into executable Node.js functions when JavaScript evaluation is enabled. Due to an incomplete fix for CVE-2026-33943, the compiler failed to validate local named import aliases against legal JavaScript identifier rules. Attackers supplying crafted module imports—specifically utilizing destructuring default initializers alongside JSON module attributes—can inject arbitrary JavaScript expressions directly into the host Node.js runtime context.
An improper input validation and authorization bypass vulnerability exists in the LangGraph storage layer affecting langgraph, langgraph-checkpoint-postgres, and langgraph-checkpoint-sqlite. Because relational store implementations serialize namespace tuples into dot-delimited strings without validating labels on read, search, delete, and batch operations, an attacker who controls a namespace label can inject periods (.) to traverse namespace boundaries and access, modify, or delete data belonging to other namespaces.
CVE-2026-115886 (GHSA-2wqp-q3c6-ccj5) is a Server-Side Request Forgery (SSRF) and Unsafe Reflection vulnerability in MLflow versions 3.12.0rc0 prior to 3.17.0. Unauthenticated REST API endpoints accept serialized third-party scorer JSON payloads that are dynamically deserialized and reflectively instantiated without sanitizing constructor keyword arguments. When optional dependencies such as trulens and trulens-providers-litellm are installed, attackers can inject egress-steering arguments into the scorer payload, forcing the MLflow server to dispatch outbound HTTP requests to arbitrary internal destinations or cloud metadata services.
CVE-2026-113872 (GHSA-q7qp-rjg7-q26m) is a high-severity denial-of-service vulnerability affecting the multer multipart/form-data upload middleware for Node.js and Express in versions 2.0.0 through 2.4.0. When an endpoint is configured to accept multiple files using an asynchronous fileFilter callback, a crafted, truncated multipart HTTP request can trigger an unhandled error event on the underlying busboy stream parser after request teardown has already completed. Because all error listeners are stripped during teardown, Node.js treats the resulting error event as an uncaught exception and immediately terminates the server process.
CVE-2026-115897 (also tracked as GHSA-m5mm-p8h7-m29v and aliased as CVE-2026-115754) is a Cross-Site Scripting (XSS) vulnerability in the Yahoo serialize-javascript npm package prior to version 7.1.3. The flaw exists in the regular expression used by escapeFunctionBody() to neutralize HTML script closing tags within serialized JavaScript functions. Because function bodies are sanitized in isolation before structural formatting is applied, a function whose string representation terminates directly with the sequence '</script' bypasses the positive lookahead check. When combined with serialization formatting options that introduce whitespace delimiters and a subsequent property that supplies a closing bracket, an attacker can prematurely terminate an enclosing HTML <script> block and inject arbitrary HTML or JavaScript into the rendered document.
CVE-2026-109841 is a Subresource Integrity (SRI) verification bypass vulnerability in undici, the official HTTP/1.1 and HTTP/2 client library for Node.js that powers the global fetch() implementation. The flaw stems from improper handling of case sensitivity (CWE-178) when validating cryptographic hash algorithm tokens within SRI metadata. Because undici compared incoming algorithm identifiers against a strictly lowercase set without prior ASCII-lowercase normalization, valid uppercase or mixed-case tokens such as SHA256- or Sha384- were treated as unrecognized algorithms and discarded. Under SRI forward-compatibility rules, discarding all provided tokens results in an empty integrity list, causing undici to fail open and accept tampered HTTP response bodies without cryptographic verification.