Skip to main content

Analytics security and limits

A saved query reads real data through a real database connection, so what happens automatically matters as much as what the query itself does. Every rule below is one of three kinds: enforced automatically, rejected outright, or left for you to guarantee yourself.

Scope every query​

  • A query belongs to exactly one app.
  • An internal query is confined to the calling site's schema:
    • the connection's search path — the list of schemas the database looks in for a table name — is pinned to that schema alone while the query runs;
    • a statement naming another schema is rejected when it is saved; and
    • a statement naming a table that is not a data table of the same app is rejected when it is saved.
  • Caller parameter values are sent to the database separately from the query text, so they are treated as data and cannot change what the query does. See Placeholders and bound values.

Who can run and manage queries​

Who can run a query depends on who the caller is. A site user must pass two checks, in order:

LayerChecksDefaultDenied response
1. App roleThe site user holds a role on the app that owns the query, beyond the default role every signed-in user hasAlways checked, before the query is read403 — No app roles assigned
2. Per-query policyA rule attached to that specific query, created when the query is created and removed when it is deletedOpen to any caller who already cleared layer 1, until you add a rule403 — You do not have permission to execute query '<slug>', once a rule naming other roles exists

There is no policy field in the create request — the query's policy is created automatically, with no rules, so every new query starts open to any site user who already holds a role on the app. To restrict who can run it:

  1. Decide which roles genuinely need to run it.
  2. After the query exists, open Policies in the app, edit that query's policy, and name those roles. Analytics doesn't do this for you.
  3. Test with one user who should be allowed and one who shouldn't, in the same site and app.
Organization members skip app-level checks

Members of the organization that owns the site — owners, admins, and other members — can run any saved query without an app role or a query policy check. Other users need a role on the app and must pass the query's policy.

If the policy service is unavailable when a query is saved, the query is still saved but may have no policy. A query with no policy can't be run by users who rely on a policy; they are denied.

Managing saved queries requires only sign-in

Creating, listing, viewing, editing, and deleting saved queries currently checks only that the caller is signed in to the site — not an app role or the query's policy. Running a query does check roles and policies. Keep secrets out of query text (use secret. references), and review your app's saved queries regularly.

Rely on read-only enforcement​

Analytics queries are read-only, enforced in two ways: a check when the query is saved, and a read-only database setting when it runs.

  • When a query is saved, its text must be exactly one read statement for every SQL connector, internal included. The check reads the whole statement, so a write placed inside a subquery or a WITH clause, or a SELECT ... INTO that creates a table, is refused. Statements such as SET, COPY, and VALUES are refused because they are not a query.
  • When a query runs, each SQL connector runs it with a read-only database setting.
  • Elasticsearch runs through the search template endpoint only, and a query body containing a script or runtime_mappings key at any depth is refused when it is saved.

For every external connection, also use a database account that can only read.

Write a query that will save​

RejectedApplies to
Empty query textAll connectors
More than one statementInternal, PostgreSQL, MySQL, Redshift, and ClickHouse
A statement that is not a read, or contains a write anywhere inside itInternal, PostgreSQL, MySQL, Redshift, and ClickHouse
Functions that pause the database, read server files, reach other systems, or change a database setting (set_config)Internal, PostgreSQL, MySQL, Redshift, and ClickHouse; the blocked list differs for ClickHouse
A stored secret. placeholder outside a WHERE, JOIN ... ON, or HAVING conditionInternal, PostgreSQL, MySQL, Redshift, and ClickHouse
A placeholder used as a table or column name, inside part of a quoted text, followed directly by ::, or containing a filter, expression, or control blockAll connectors that apply to it; see Placeholders and bound values
A query body that is not valid JSON, or a placeholder used as a JSON keyElasticsearch
A script or runtime_mappings key at any depthElasticsearch
Referring to a schema other than the calling site'sInternal queries
Naming a table that is not a data table of the same app, or a statement whose tables cannot be worked outInternal queries
Referring to a secret that does not exist for the site and appAll connectors
An external connection with no connection secretAll external connectors

These checks run on every create and every edit, including an edit that only renames the query or changes its tags.

Handle secrets deliberately​

Secret values referenced by a query are read from storage when the query runs. They are not accepted from callers, and they are not part of the stored query definition, which holds only the secret's key.

In SQL queries a stored secret can decide which rows are read but cannot be placed where its value would be returned or used to sort: its placeholder is allowed only in a WHERE, JOIN ... ON, or HAVING condition, and any other position is refused when the query is saved. The built-in user profile is exempt, because it describes the caller rather than stored data.

Treat permission to run a query as permission to see every resolved value its text can return.

Connection secrets and secret. values are cached for up to one hour. Saving or deleting a secret normally clears the cache right away, but occasionally a rotated credential can keep being used until its cached copy expires — up to one hour.

When execution fails, Analytics applies best-effort redaction to resolved query secret values and password-like connection URLs before storing and returning the message. This doesn't catch everything: the connection's own credentials aren't checked, and database error messages can include host names or query details.

Account for execution records​

Raw caller parameter values are stored in the current execution record, together with the external connection secret key, referenced secret key names, and final error text.

An error message returned by the database can quote a value it failed to accept.

Do not put credentials, tokens, personal data, or other sensitive values in caller parameters. Automatic redaction doesn't catch everything, so review error messages before you share them.

Bound every query​

Add a fixed row limit to the query text rather than relying on connector defaults.

LimitValueGuidance
Result rows10,000 by default. Fixed for internal queries; an external connection secret may override it with max_result_rows. A run over the maximum fails instead of returning a partial resultUse an explicit fixed LIMIT or Elasticsearch size and test the deployed connector
StatementsOne statement per query, checked on save for every SQL connectorSend one reviewed read statement
Description lengthMaximum of exactly 1,000 characters, checked on save1,000 is accepted; 1,001 is rejected

Protect a query before you delete it​

Deleting a query permanently removes its definition — the query text, its connection, and its name. There is no recovery path, no archive, and no disable state.

Before deleting:

  1. Remember that deletion checks only that the caller is signed in, not their app role.
  2. Keep a copy of the query text if you might want it back. Recreating a query with the same name produces the same slug as long as no other query in the site holds it, but you have to supply the query text again.
  3. Confirm nothing else calls the query by slug, because the slug stops resolving immediately.

See Delete a query for the complete Console confirmation procedure.

Share safe diagnostics​

For a support request, capture:

  • the execution key returned with the failed run;
  • the query slug and the app that owns it;
  • the connection mode, and the connection secret key for an external query;
  • the parameter names supplied, without their values if any are sensitive; and
  • the error category and a summary of the message with any sensitive details removed.

Do not include credentials, personal data, or infrastructure details in a report.

Understand suspension​

If your organization's Analytics access is suspended — for example, for an unpaid bill — listing, viewing, running, creating, editing, and deleting queries all return 402 until it is restored.

Direct HTTP​

Manage saved queries in TaruviBase Console and run them with the SDKs. The reference shows the HTTP request the SDKs send, for comparison.