Listen to this Post
How the Vulnerability Works
A record user with table-level `SELECT` permissions can learn the value of a field that is protected by field-level `SELECT` permissions, provided that field is indexed. When `DEFINE FIELD … PERMISSIONS FOR select WHERE …` hides a field from a caller, and that field is indexed, the query planner uses an indexed‑COUNT shortcut — such as Index::Count, IndexCountScan, or the legacy `Iterate Index Count` / `Iterate Index Keys` paths — that counts matching index entries and skips the permission check that would normally hide the value.
For example, an attacker can run:
SELECT count() FROM t WHERE hidden_field = "guess" GROUP ALL;
This returns a count greater than zero whenever a record actually has that value, even though the caller is never allowed to read the field directly. The same query with `WITH NOINDEX` correctly returns [], confirming the gap. By repeating the count query with different guesses, an attacker can confirm or recover the contents of any restricted field they cannot read through a normal SELECT.
The legacy planner (surrealdb/core/src/idx/planner/tree.rs) and the streaming planner (surrealdb/core/src/exec/planner/select/mod.rs) now both refuse the indexed fast path when the `WHERE` / `ORDER` tree references a field governed by a non‑Full `SELECT` permission. A new `cond_touches_restricted_field` flag is propagated, and `eval_count` refuses a dedicated `Index::Count` when set. The streaming planner adds cond_touches_restricted_select_field, a `RestrictedIdiomChecker` visitor that matches each idiom against the table’s field‑permission prefixes (loaded via the plan‑time transaction), and gates `IndexCountScan` emission on it. The fast paths are preserved for root / owner sessions via should_check_perms_for_view. Versions 3.1.0 and later are not affected.
DailyCVE Form
Platform: SurrealDB
Version: <3.1.0
Vulnerability: Info Disclosure
Severity: Medium
date: 2026-07-01
Prediction: 2026-05-26
What Undercode Say
Bash / SurrealQL commands to test the vulnerability:
Check SurrealDB version surreal version Connect to the database and run the exploit query surreal sql --conn http://localhost:8000 --user root --pass root --ns test --db test
-- Create a table with a hidden indexed field
DEFINE TABLE t SCHEMAFULL;
DEFINE FIELD secret ON t TYPE string;
DEFINE FIELD secret ON t PERMISSIONS FOR select WHERE $auth.id = 'owner';
DEFINE INDEX idx_secret ON t COLUMNS secret;
-- Insert some data
INSERT INTO t (secret) VALUES ('guess1'), ('guess2'), ('guess3');
-- Attacker (non-owner) runs:
SELECT count() FROM t WHERE secret = 'guess1' GROUP ALL;
-- Returns > 0 if 'guess1' exists, leaking the value
-- Confirm the leak is due to the index:
SELECT count() FROM t WHERE secret = 'guess1' GROUP ALL WITH NOINDEX;
-- Returns [] correctly
Code snippet showing the fix (from commit 0c6dd021b):
// surrealdb/core/src/exec/planner/select/mod.rs
fn resolve_indexes(&mut self) {
// Skip any B-tree / unique index whose columns are governed
// by a non-Full SELECT permission.
if self.cond_touches_restricted_select_field {
return; // Do not use the indexed COUNT shortcut
}
// ... otherwise proceed with index selection
}
Exploit
An attacker with table‑level `SELECT` can:
- Run repeated `SELECT count() FROM t WHERE hidden_field = “guess” GROUP ALL` queries with different guesses.
- Observe whether the count is greater than zero, confirming the presence of that value.
- Recover the entire value of any indexed, field‑protected column by brute‑forcing or dictionary attacks.
What the attack cannot do:
- Read fields that are not indexed (the shortcut only fires when an index covers the predicate column).
- Cross table, database, or namespace isolation boundaries.
- Modify data, escalate privileges, or affect availability.
Protection
- Upgrade to SurrealDB 3.1.0 or later – versions 3.1.0 and later include the fix that gates the indexed‑
COUNTfast path behind the proper permission checks. - Avoid `DEFINE INDEX` on fields whose values are protected by field‑level `SELECT` permissions – without the index, the leak does not occur.
- Restrict the ability of record users to issue arbitrary `SELECT count() … GROUP ALL` queries against tables containing field‑protected columns.
- Use namespace / database isolation as the primary boundary where feasible.
Impact
What an attacker can do:
- Confirm or recover values of a field protected by field‑level `SELECT` permissions on any table they hold table‑level `SELECT` on, provided the field is indexed.
- Repeat the query with different guesses to read restricted field contents one value at a time.
What it cannot do:
- Read fields that are not indexed.
- Cross table, database, or namespace isolation boundaries.
- Modify data, escalate privileges, or affect availability.
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

