The Authorizer Was Added to One Connector: WhoDB CVE-2026-112005 and Nineteen Advisories Without an Identifier
At 12:17:12.790Z on 18 October 2026 NVD received CVE-2026-112005, a path traversal (CWE-22) in WhoDB — clidey/whodb, 5,035 stars, Go, Apache-2.0, a database browser distributed as a Docker image and through the Mac App Store, the Microsoft Store and Snap. VulnCheck scores it CVSS 4.0 9.2; NVD also carries a CVSS 3.1 vector of 7.7. Affected: 0.104.0 before 0.135.0.
The underlying report is better than the CVE text, and it was already public. Repository advisory GHSA-x3h5-pr45-fx4r, published 15 October 2026 and credited to napahlm, rates it critical, CVSS 3.1 9.4 and summarises it as “unauthenticated arbitrary host file read and write through a DuckDB source session, disclosing every logged-in user’s database credential in plaintext.”
The two scores differ on one metric and it is worth recording rather than averaging: the vendor advisory says AC:L, NVD says AC:H. The reporter published a complete reproduction against the shipped image, in a browser, in about five minutes, with no credential. On the evidence available we side with the vendor.
A check on one path at one moment
WhoDB confines file-backed data sources to one server directory, /db in server mode. The DuckDB connector enforces that at core/src/plugins/duckdb/db.go by resolving symlinks, checking a prefix and stating the result:
if !strings.HasPrefix(fileNameDatabase, getDefaultDirectory()) {
The advisory’s own verdict on that line is the sentence the whole finding turns on: “That check is correct, and it is a check on one path at one moment: the database file, at open time. It says nothing about what the open session may then address afterwards.”
Afterwards is the query editor. RunSourceQuery takes caller-authored SQL — which is its entire purpose — and reaches RawExecute with ReadOnly unset, so DuckDB executes read_text, read_blob, COPY ... TO and ATTACH in-process as the WhoDB user. The directory check passed; the session then addressed the rest of the filesystem.
No credential is needed to reach it. LoginSource sits in the public mutation allow-list, a DuckDB login carries only a database name and no secret, Community Edition has no user accounts to bypass, and the server binds every interface by default. The login page even offers a picker listing every entry of /db, unfiltered — convenient, though the reporter notes a guessed filename works just as well.
What the escape reaches is the reason this is critical rather than merely bad. WhoDB writes its AES-256-GCM session key to session.key in the same directory as the whodb.db session store that key encrypts. One read_text returns the key, one read_blob returns the ciphertext, and offline decryption yields every logged-in user’s database connection details including passwords. The advisory notes that the code warns about this adjacency itself, in a comment, in the file that creates it.
The same defect, two weeks earlier, in the other connector
This is the part that makes it a class rather than an instance, and the reporter states it plainly: “This is the DuckDB counterpart of advisory GHSA-q79w-r6rg-4w2j. That advisory’s remediation added a per-connection authorizer to the SQLite connector only, in 0.132.0. The DuckDB connector has never had an equivalent.”
We read that earlier advisory first-hand. GHSA-q79w-r6rg-4w2j, published 30 September 2026, credited to 1984oran, is rated critical at CVSS 10.0 — unauthenticated session forgery, reached by logging into the sample SQLite database and using ATTACH in user-supplied SQL to open the session store by absolute path. Its root-cause statement reads: “SQLite database path validation runs only when the primary connection is opened. ATTACH DATABASE in later user-supplied SQL can open another SQLite file by absolute path and bypass that validation.”
That is the same sentence as the new finding with one noun changed. The remediation — commit 7d64b4b6, “fix for sqlite auth bypass” — registered a per-connection authorizer denying SQLITE_ATTACH and SQLITE_DETACH for the life of the session. It was applied to the connector where the bug was reported. DuckDB, which sits beside it, offers the same primitive through different syntax and received nothing.
And the project already knew DuckDB could write files. This comment was sitting in duckdb.go, in the vulnerable tree, before either advisory:
// executeReadOnly uses SQL because duckdb-go rejects sql.TxOptions.ReadOnly.
// A sql.Conn pins BEGIN, query and ROLLBACK to the same physical connection.
// The caller must apply ReadOnlyConfig: transaction mode alone permits COPY TO.
The knowledge was present, written down, and scoped to a configuration flag that the query-editor path never sets. A fix applied per-reported-instance rather than per-boundary leaves exactly this residue: a hazard the codebase can describe accurately and does not defend against.
The fix checks that the fix applied
Commit 80111c9e by modelorona, 12 October 2026, +279/−13 across four files (201 of those lines a new db_security_test.go), takes the reporter’s suggested approach and adds something better. The settings go on as DSN options:
const restrictedDSNOptions = "?enable_external_access=false&autoinstall_known_extensions=false&autoload_known_extensions=false&allow_community_extensions=false&lock_configuration=true"
Then, in server mode, the connection reads its own settings back and refuses to exist if they did not take:
err = sqlDB.QueryRowContext(config.OperationContext(), `SELECT
current_setting('enable_external_access'),
...
current_setting('lock_configuration')`).Scan(&externalAccess, &autoInstall, &autoLoad, &communityExtensions, &locked)
if err != nil || externalAccess || autoInstall || autoLoad || communityExtensions || !locked {
_ = sqlDB.Close()
...
return nil, errors.New("DuckDB security settings were not applied")
}
This is the line worth stealing. Applying a hardening setting and verifying it is in effect before handing the connection to a caller converts a silent misconfiguration into a refused connection. Most mitigations are write-only: you set the flag and assume. Here the control is read back from the engine that will enforce it.
The commit also fixes the fix. Because the driver parses ? as DSN options and # as a URL fragment, either character in a database path would discard every security option appended after it, so paths containing them are now rejected outright with a comment saying exactly that. A mitigation delivered through string concatenation inherits the parser’s ambiguities, and somebody checked.
The remediation shipped in 0.135.0 on 15 October 2026, whose release notes name the advisory directly — the disclosure hygiene here is good, and we note it because we criticise its absence often enough.
Twenty-one advisories, two CVEs
Reading the repository’s advisory list in full produces a number that reframes the single CVE: WhoDB has published twenty-one security advisories. Exactly two carry a CVE, CVE-2025-24786 and CVE-2025-24787, both published in February 2025. The other nineteen — every one from 2026, including the 10.0 session forgery and today’s critical — have no identifier from the project. VulnCheck has now backfilled precisely one of them, which is why this briefing exists and why the other eighteen do not have one.
The 2026 set is not marginal. It includes eight advisories published in a single burst on 30 September, and three that are squarely in this publication’s territory: “AI chat trusts model operation label when executing generated SQL” (high), “WhoDB MCP read-only and confirmation gates allow privileged database server file operations” (medium) and “Hosted MCP file-upload confirmation omits the local source path” (low). WhoDB is not merely a database client that happens to have a CVE; its README documents an MCP server and an AI chat that connects to OpenAI, Anthropic, Gemini, Ollama or LM Studio, and the advisory set shows that surface being audited. A tool whose job is to hold every database credential in an environment, and which now also executes model-generated SQL, is exactly the asset whose advisories should be easy to find.
They are not. Measured directly, one request each:
GET api.github.com/advisories/GHSA-x3h5-pr45-fx4r→ 404. The critical DuckDB advisory is repository-only; it was never published to the global database.GET api.github.com/advisories/GHSA-q79w-r6rg-4w2j→ 404. Same for the 10.0.- OSV version query for
github.com/clidey/whodb/coreat 0.134.0 — the last vulnerable release — returns two records,GHSA-5pf6-cq2v-23wwandGO-2024-3350. Both are from 2024. Nothing from the 2026 cluster. - OSV version query for
github.com/clidey/whodbat 0.134.0 returns zero — and that unsuffixed path is the coordinate the 10.0 advisory itself declares, while today’s namesgithub.com/clidey/whodb/core. Two advisories, two module paths, one project.
The OSV record for the CVE exists (200, aliased to the GHSA) and is the familiar auto-conversion: a GIT range plus an enumerated version list and no package coordinate. That list has its own defect — it omits 0.113.0, 0.130.0 and 0.131.0, all of which exist as tags and all of which sit inside the declared affected range. We checked each one against the repository’s git refs; all three return 200. An operator pinned at 0.131.0 is vulnerable and unlisted.
There is a fair defence for part of this, and we will make it: WhoDB is consumed as a container image, a Snap and an app-store binary. Almost nobody imports the Go module, so the module coordinate is a technically-correct answer to a question no deployment asks. But that defence does not cover the 404s. Publishing a repository advisory to the global database is a checkbox, it costs the maintainer nothing, and it is the difference between a critical finding that reaches tooling and one that reaches only the people already watching the repository.
What to do
Upgrade to 0.135.0 or later. No single earlier version covers the cluster — the SQLite authorizer landed in 0.132.0, the 30 September batch in 0.134.0, and the DuckDB confinement only in 0.135.0.
Rotate every database credential WhoDB has held, and do it regardless of whether you believe you were reached. The exposure is a decryptable store of connection details, the decryption happens offline, and nothing in WhoDB logs it. Evidence lives in your database servers’ authentication logs, not in WhoDB’s.
Until you have upgraded, treat network reachability as the only control that exists, because in Community Edition it is: there are no accounts, the public login mutation needs no secret for a file-backed source, and the listener binds every interface. Do not place a WhoDB instance where an unauthenticated HTTP request can arrive, and do not run it as a user whose readable files you have not enumerated.
Then audit for the shape rather than the version. Ask of your own codebase: is any confinement enforced once, at open time, on a session that afterwards accepts caller-authored input in a language with filesystem primitives? Was a fix for a reported instance applied at the call site rather than the boundary, leaving a sibling connector, driver or backend unprotected? Does any mitigation get applied without being read back? The first question is this CVE, the second is why it existed two weeks after the same bug was fixed next door, and the third is the thing WhoDB got right on the way out.
CVE-2026-112005 is not in the CISA KEV catalogue (checked 19 October 2026 against catalogue version 2026.10.16, 1,748 entries, which contains no WhoDB or Clidey records of any kind), and no exploitation has been reported.
Our verification was read-only. CVE-2026-112005’s identifier, publication timestamp (2026-10-18T12:17:12.790Z), vulnStatus (Received), source (VulnCheck), description, CVSS 4.0 and 3.1 vectors, CWE-22 assignment and references were read first-hand from the NVD 2.0 API. GHSA-x3h5-pr45-fx4r and GHSA-q79w-r6rg-4w2j — summaries, severities, CVSS vectors, CWEs, affected and patched ranges, ecosystem coordinates, credits and publication timestamps — and the complete twenty-one-advisory list with its CVE tally were read first-hand from the GitHub repository security-advisory API; every quoted sentence is verbatim from those advisories. Commit 80111c9e’s author, date, file list, line counts and complete diff were read first-hand from the GitHub commits API, and every line of Go and every code comment is quoted verbatim from that diff or from the advisory’s quotations of the vulnerable tree. The 0.135.0 release body and publication timestamp, repository metadata (5,035 stars, 241 forks, Go, Apache-2.0, created June 2024, not archived) and the existence of tags 0.113.0, 0.130.0 and 0.131.0 are from the GitHub API. The MCP and AI-provider descriptions are from the project README. All OSV lookups, OSV module-version queries against both coordinates, and the two global-advisory 404s were measured directly, one request each. KEV status was checked directly against the published catalogue. Brave Search was used for discovery only and returned no independent technical analysis — solely automated mirrors (threatint, Threat Radar, strix.ai, VulDB, CVEdetails). We installed nothing, started no WhoDB instance, created no data source, ran no query, decrypted nothing and executed no proof of concept; the reproduction described in the advisory is the reporter’s work and we did not repeat it.
Sources:
- NVD — CVE-2026-112005 (published 18 October 2026, 12:17:12.790Z; VulnCheck; CWE-22; CVSS 4.0 9.2 / CVSS 3.1 7.7)
- WhoDB — GHSA-x3h5-pr45-fx4r, unauthenticated arbitrary host file read and write through a DuckDB source session (critical, CVSS 9.4; 15 October 2026; reporter napahlm)
- WhoDB — GHSA-q79w-r6rg-4w2j, unauthenticated session forgery through sample SQLite filesystem access (critical, CVSS 10.0; 30 September 2026; reporter 1984oran)
- WhoDB commit 80111c9e — “duckdb patch fix” (12 October 2026; +279/−13; restricted DSN options plus settings read-back)
- WhoDB — release 0.135.0 (15 October 2026; names the advisory in its notes)
- VulnCheck — WhoDB 0.104.0 before 0.135.0 Arbitrary File Read/Write via DuckDB Query Editor
- CISA — Known Exploited Vulnerabilities catalogue (CVE-2026-112005 not listed; checked 19 October 2026)
- al-ice.ai — “The Default Only Fires When the Variable Is Missing” (19 October 2026; the batch sibling, and another OSV enumeration that omits most of its range)
- al-ice.ai — “Nine Stranded Advisories” (17 October 2026; repository advisories that never reached the global database)
- al-ice.ai — “Add Missing Checks to Several Handlers” (18 October 2026; a maintainer naming their own defect class)