Skip to content

fix(cubejs): fifth review of the confinement (multi-hop row rules, query deadlines) - #107

Merged
acmeguy merged 1 commit into
mainfrom
fix/confinement-round5
Oct 31, 2026
Merged

acmeguy merged 1 commit into
mainfrom
fix/confinement-round5

Conversation

@acmeguy

@acmeguy acmeguy commented Oct 31, 2026

Copy link
Copy Markdown

Why

A fifth adversarial review of the merged confinement (#103–#106) confirmed three findings, all reachable with default config.

Finding Impact Fix
Critical: a ruled table joined in the middle of a multi-hop alias path (top.mid.lf.label, where mid → semantic_events) gets no row rule Cross-tenant rows feed the result (reproduced end to end) Every instance along a path is ruled. memberPathHops walks the compiled model exactly as Cube's resolveMemberPath does, and index.js passes the hops with the resolver
High: a never-ending DuckDB or SQLite query holds a libuv worker Four requests froze DNS, crypto and file I/O for every tenant until SIGKILL DuckDB query, stream and initSql, and SQLite queries, are interrupted after CUBEJS_DB_LOCAL_QUERY_TIMEOUT (default 300 s); UV_THREADPOOL_SIZE=64
Medium: DuckDB COPY … TO / EXPORT DATABASE The shared disk can be filled Refused

Verification

  • Image yarn test: 762/762, including new tests for the multi-hop rule, both engines' deadline interrupts, and COPY/EXPORT refusal.
  • The reviewers' exploits, rerun against this image:
    • multi-hop leak: returns only tenant A's row; the B-label filter oracle returns nothing.
    • wedge attack (SQLite via run-sql, and DuckDB via initSql on /test): tenant B still gets 200, and DNS, WebCrypto and file reads stay ok.

Residual

Tenant DuckDB and SQLite still run in the cube process. With 64 workers and the deadline, an attacker needs 64 concurrent long queries to stall the pod, and only for up to 300 s. Moving them out of process is the durable fix.

🤖 Generated with Claude Code

- Row rules: every cube instance a member path passes through is ruled, not
  only the last one. A ruled table joined in the middle of a multi-hop alias
  path (top.mid.lf.label, mid = semantic_events) was read across tenants.
  memberPathHops walks the compiled model exactly as Cube's
  resolveMemberPath does; index.js passes the hops with the resolver.
- In-process engines: DuckDB queries, streams and initSql, and SQLite
  queries, are interrupted past CUBEJS_DB_LOCAL_QUERY_TIMEOUT (default 300s).
  A never-ending query held a libuv worker; four froze DNS, crypto and file
  I/O for the whole pod. UV_THREADPOOL_SIZE=64.
- DuckDB: COPY ... TO and EXPORT DATABASE refused (no disk quota bounds
  them; the shared disk could be filled).

The review's exploits rerun against this image: closed (multi-hop leak
filtered; pod stays responsive after the wedge attack). 762/762.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@acmeguy
acmeguy merged commit 7c1d6f8 into main Oct 31, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant