> ## Documentation Index
> Fetch the complete documentation index at: https://formbricks.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Cube Tenant Isolation

> Threat model and controls for XM analytics tenant isolation in Cube

## Context

XM analytics reads Hub feedback records through Cube. Hub stores all tenants in a shared `feedback_records`
table and uses `tenant_id` to separate rows. Workspace access is the application authorization boundary. In the
current Hub schema, `tenant_id` stores the authorized FeedbackDirectory ID, so every Cube query must be
scoped to a directory that the authenticated workspace can access before data leaves Cube.

## Threat Model

The main risk is cross-tenant read access through an unscoped Cube query. The attacker could be a regression in
server code, an AI-generated query that includes malicious filters, a copied static token, or a direct request to
Cube from inside the deployment network.

The controls assume query bodies are attacker-influenced. Tenant identity is never trusted from the query JSON.

## Enforcement Flow

<Steps>
  <Step title="Authorize workspace access">
    Server actions and server components authorize workspace access in the Next.js app.
  </Step>

  <Step title="Validate the Cube query">
    The app validates the Cube query and rejects any `FeedbackRecords.tenantId` member supplied by users,
    saved charts, or AI output, including filters, dimensions, time dimensions, and order clauses.
  </Step>

  <Step title="Mint a short-lived JWT">
    The app mints a short-lived JWT per Cube request with `tenantId`, `feedbackDirectoryId`,
    `workspaceId`, `organizationId`, `userId`, `scope`, `iss`, `aud`, `jti`, and `exp` claims.
  </Step>

  <Step title="Verify the JWT in Cube">
    Cube verifies the JWT and exposes the claims through `securityContext`.
  </Step>

  <Step title="Enforce the tenant filter">
    Cube `queryRewrite` rejects missing tenant context, rejects caller-supplied tenant member usage, and
    appends `FeedbackRecords.tenantId = securityContext.tenantId` to every query.
  </Step>
</Steps>

## Audit Evidence

The app records a sanitized `cubeQuery` audit event for each Cube query attempt, keyed by the JWT `jti`. Cube also
emits a structured audit log line from `queryRewrite` with tenant, feedback directory, workspace,
organization, user, request ID, source, and queried member names. Raw filter values are intentionally omitted from
both logs.

## Operational Notes

`CUBEJS_API_SECRET` is a signing secret, not an access token. Do not pass it to clients or reuse it as a bearer
token. Set `CUBEJS_JWT_ISSUER` and `CUBEJS_JWT_AUDIENCE` consistently for the web app and Cube so Cube rejects
tokens minted for any other audience or issuer. Set `CUBEJS_DEFAULT_API_SCOPES=meta,data` for Cube deployments so
GraphQL, SQL, and orchestration APIs are not exposed unless explicitly needed. Network isolation for Cube remains
recommended, but JWT-backed `queryRewrite` is the mandatory data boundary.
