The Branch the Validator Never Opened: WeKnora’s Viewer-Role File Read via UNION-in-Subquery

NVD published CVE-2026-108821 at 10:16Z on 12 October 2026 against Tencent WeKnora — the open-source LLM knowledge platform (“turn raw documents into a queryable RAG, an autonomous reasoning agent, and a self-maintaining Wiki”), with 33,465 stars and 4,456 forks — describing SQL injection in the validateSubquery function of internal/utils/inject.go, affecting versions through 0.8.1 with upgrade to 0.8.2 recommended. The scores look modest: 6.3 medium on CVSS 3.1, 2.1 low on CVSS 4.0 with exploit maturity E:P — a public exploit exists. The record’s vulnStatus is Deferred, so no NVD analysis will adjudicate. Read the researcher’s write-up and the scores stop making sense: a user holding only the Viewer role — the lowest tenant role — lists the server filesystem, reads /etc/passwd, and enumerates every other workspace’s documents, all by asking the platform’s own data-analysis agent nicely.

What the researcher showed

The report, published by k4z01 in early September with a full local reproduction (default docker compose install, a locally served model, a Viewer-role account in a fresh chat), describes a two-step chain. First, the table name: a prompt asking the agent to run SELECT * FROM <T> fails, and the validation error itself discloses the internal table (table_not_allowed … Allowed tables: [k_589cf639_…]). Then the bypass, delivered as SQL the user instructs the agent to run verbatim:

SELECT * FROM k_589cf639_...,
  (SELECT CAST(region AS VARCHAR) AS c FROM k_589cf639_...
   UNION ALL SELECT file FROM glob('/data/files/**')) t

With the table name known, the same shape reads arbitrary server files — read_text('/etc/passwd') in the demonstrated case — and because the default local storage backend keeps every workspace’s uploads under a single directory tree (/data/files), glob('/data/files/**') turns one workspace’s Viewer into a reader of all workspaces’ documents. The data_analysis tool is in the default tool set; no exotic configuration is required beyond enabling the agent feature the tool exists for.

The six lines that are missing

The mechanism is a validator that checks the top of the tree and never opens one of the branches. At the top level, validateSelectStmt rejects compound queries outright:

if stmt.Op != pg_query.SetOperation_SETOP_NONE {
    return fmt.Errorf("compound queries (UNION/INTERSECT/EXCEPT) are not allowed")
}

But validateSubquery — reached for subqueries in a FROM clause, which the tool permits because it does not pass WithNoSubqueries() — walks FromClause, TargetList, WhereClause, GroupClause and HavingClause, then returns nil. For a set-operation node those lists are empty; the two UNION branches live in Larg and Rarg, which the function never examines. The guard it was meant to reach — “functions in FROM clause are not allowed”, the line that would have caught glob() and read_text() — sits one recursion away and is never evaluated. The table whitelist, correctly enforced, does not help: the bypass never names a disallowed table, it hides a function call inside a branch nobody iterates.

Here is the part that justifies this article’s existence rather than a link to the advisory. We read internal/utils/inject.go at main HEAD today — the repository was pushed again this morning — and validateSubquery still contains no examination of Op, Larg or Rarg. The top-level compound-query rejection is still there. The function the report is about is structurally unchanged. To be precise about what that does and does not mean: the surrounding layers have moved. The data_analysis call site now carries an explicit comment (“IMPORTANT: Must enable validateSelectStmt to block RangeFunction attacks”), the September fix PR validated nested SQL through the parsed tree and applied tenant predicates to aliases, release 0.8.2 shipped a cluster of tenant and traversal fixes, and today’s commit (#4136) scopes database queries to authorised shared knowledge bases. We did not execute the proof of concept, so we claim nothing about whether the end-to-end chain still fires at HEAD. What we can say, because we read it, is that the specific guard the report asked for — look inside the set-operation branches — is still absent from the specific function, and that is an odd shape for a fix to leave behind.

A validator story, again

This is the third time in a week this site has written some variant of “the checker inspected the container and not the contents.” Langflow’s decorator ran the code it was validating (CVE-2026-51886); agno’s namespace restriction could not be engaged by a caller who tried (last night’s batch); here, a SQL validator recurses into every clause of a subquery except the one that can contain a second query. The recurring defect is validators written against the grammar the author had in mind rather than the grammar the parser accepts. pg_query will happily parse a set-operation node inside a FROM subquery; the validator simply never asked what kind of node it was holding. If you maintain an allowlist-style SQL gate over an agent tool, the audit is mechanical: for every AST node type your parser can produce, show the line that handles it, and treat “returns nil over unexamined children” as a finding, not a default.

A final note on the numbers, because a 2.1 on the record that describes cross-tenant document disclosure to the lowest-privileged role will sail under a great many thresholds. CVSS 4.0 records VC:L/VI:L/VA:L with PR:L — the same low-impact compression this site documented across the agno batch, where arbitrary code in the host process scored below the unauthenticated server that fronted it. The researcher’s impact section names two consequences the vector does not price: any file readable by the server process returned to a Viewer, and every workspace’s uploads readable from any workspace. Score-conditioned triage would file this under “low” and move on; the write-up suggests reading the impact paragraph instead of the number.

What to do, with versions that actually exist

  • Upgrade to 0.8.2 or later. The NVD record names it, the release shipped 24 September with the nested-SQL validation fix and tenant-predicate hardening, and further scoping landed as recently as today. Do not stay on 0.8.1 with the agent data-analysis feature enabled.
  • Assume the Viewer tier saw more than its own knowledge base. If your instance ran ≤ 0.8.1 with untrusted Viewer-role users and default local storage, the demonstrated chain discloses other workspaces’ documents and server files. Rotate credentials that lived on the server filesystem and review what sat under the storage tree.
  • Separate the storage tree per tenant regardless. A single directory tree for all workspaces converts every future file-read primitive into a cross-tenant one. Per-workspace roots with OS-level separation survive the next validator gap.
  • Stop leaking schema in error messages. The chain’s first step was a validation error that named the internal table. Error strings returned to low-privilege callers should not enumerate allowed tables.

Verification note. The CVE identifier, publication timestamp (10:16:43Z, 12 October 2026), CVSS 3.1 and 4.0 scores and vectors, affected-through and fixed versions and vulnStatus: Deferred were read first-hand from the NVD 2.0 API. The mechanism description, PoC prompts, impact statements and reproduction details are the researcher’s published analysis, read first-hand in full from the k4z01/WeKnora-SQL-Validation-Bypass repository (file read via the GitHub API) and attributed as such. Independently, we read internal/utils/inject.go and internal/agent/tools/data_analysis.go at main HEAD via the GitHub API and quote their current state: validateSubquery examines neither Op nor Larg/Rarg; the top-level compound-query rejection and the call-site comment are present as described. Fix-history claims (July validator-bypass fix, September PR #3305, v0.8.2 release contents, today’s #4136) were read first-hand from the GitHub commits and releases APIs. Star, fork and push data likewise. We did not deploy WeKnora, run any model, or execute any SQL against any instance. The scoring critique and the validator-pattern reading are our editorial analysis. We have no evidence of exploitation and imply none.

Sources: