{
  "affected": [
    {
      "database_specific": {
        "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/07/GHSA-c8jx-96c9-8xrp/GHSA-c8jx-96c9-8xrp.json"
      },
      "package": {
        "ecosystem": "crates.io",
        "name": "surrealdb",
        "purl": "pkg:cargo/surrealdb"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "3.1.0"
            }
          ],
          "type": "SEMVER"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-63742"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-863"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-07-01T20:22:06Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "A record user could learn the value of a hidden field by counting how many records match a guess.\n\nWhen `DEFINE FIELD ... PERMISSIONS FOR select WHERE ...` hides a field's contents from a caller, and that field is indexed, running `SELECT count() FROM t WHERE hidden_field = \"guess\" GROUP ALL` returned a count greater than zero whenever a record actually had that value — even though the caller was never allowed to read the field directly. The query planner used an indexed-COUNT shortcut (`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. The same query with `WITH NOINDEX` correctly returned `[]`, confirming the gap.\n\nBy repeating the count query with different guesses, an attacker can confirm or recover the contents of any restricted field they could not read through a normal `SELECT`.\n\n### Impact\n\nWhat an attacker **can** do:\n\n- 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.\n- Repeat the query with different guesses to read restricted field contents one value at a time.\n\nWhat it **can't** do:\n\n- Read fields that are not indexed (the shortcut only fires when an index covers the predicate column).\n- Cross table, database or namespace isolation boundaries.\n- Modify data, escalate privileges, or affect availability.\n\n### Patches\n\nThe 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:\n\n- `resolve_indexes` skips any B-tree / unique index whose columns are governed by such a permission.\n- A new `cond_touches_restricted_field` flag is propagated; `eval_count` refuses a dedicated `Index::Count` when set.\n- 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 txn), and gates `IndexCountScan` emission on it.\n- The fast paths are preserved for root / owner sessions via `should_check_perms_for_view`.\n\nVersions 3.1.0 and later are not affected.\n\n### Workarounds\n\nUsers unable to patch are advised to consider the following workarounds:\n\n- Avoid `DEFINE INDEX` on fields whose values are protected by field-level SELECT permissions. The class of attack is specific to the indexed fast paths.\n- Restrict the ability of record users to issue arbitrary `SELECT count() … GROUP ALL` queries against tables containing field-protected columns.\n- Use namespace / database isolation as the primary boundary where feasible.",
  "id": "GHSA-c8jx-96c9-8xrp",
  "modified": "2026-11-04T02:56:13.128405465Z",
  "published": "2026-07-01T20:22:06Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/surrealdb/surrealdb/security/advisories/GHSA-c8jx-96c9-8xrp"
    },
    {
      "type": "WEB",
      "url": "https://github.com/surrealdb/surrealdb/pull/240"
    },
    {
      "type": "WEB",
      "url": "https://github.com/surrealdb/surrealdb/commit/0c6dd021bb55b32a78a553c72bb9c0cdd414825f"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/surrealdb/surrealdb"
    }
  ],
  "schema_version": "1.9.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "SurrealDB: Field-level SELECT permissions bypassed via indexed COUNT fast paths"
}