TL;DR

Xerj, a Rust search engine and MCP server for agents (17.7k stars), accepted an API bearer token on the Console API route that mints new API tokens, a route meant to require a passkey-authenticated browser session, so anyone holding one leaked token could mint persistent successor tokens that revoking the original would not touch (CVE-2026-97406, CERT/CC VU#645818, 2026-10-20; GitHub Advisory Database 9.1 Critical, unreviewed). The vendor did not answer CERT/CC's 2026-08-13 notice and the note still says no patch, but PR #2003 (merged 2026-10-21) gates the minting, revocation, invitation, passkey-deletion and logout routes behind a passkey session by default, and the maintainer states that v1.0.0-rc.98 (2026-10-22) carries it. Upgrade to rc.98 or later and rotate every API token issued before the upgrade, because the fix does not revoke tokens minted through the gap.

What happened

  • 2026-08-13. CERT/CC notifies the vendor (the note's "vendor notified" date). No reply.
  • 2026-10-20. CERT/CC publishes VU#645818 and the CNA record CVE-2026-97406: "XERJ Console API bearer tokens can bypass the passkey-only authentication requirement and directly invoke the API-token creation endpoint, allowing unauthorized minting of persistent successor credentials." Credit: Shiran Guez. The note says the platform "lacks hierarchical revocation", so revoking a parent token does not invalidate its descendants, that a stolen owner token keeps full administrative authority, and that an attacker could issue an invitation, redeem it, and enrol their own passkey. The record's affected lists one version, v1.0.0-rc.11, with no range and no fix. The GitHub Advisory Database entry GHSA-vjpr-qxjc-7crv (published 2026-10-20, updated 10-29) prints CVSS 3.1 9.1 AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N, CWE-306, and "Unknown" for package and versions.
  • 2026-10-21. PR #2003, "fix(console-api): require a passkey session to mint API tokens (CVE-2026-97406)", merges as commit 050a12e. Per the PR and the repository's docs/SECURITY_MODEL.md, a bearer token now gets 403 before the handler runs on POST /auth/api-tokens, DELETE /auth/api-tokens/:id, POST /auth/magic/issue, DELETE /auth/passkeys/:id and POST /auth/logout; ordinary reads and writes keep working on a bearer token. The check sits in the session extractor with no flag, so it is on by default. The same day, issue #2020 asks the maintainer for a vendor statement to CERT/CC, a release carrying the commit, and a GHSA, and notes that "the fix does not revoke tokens or passkeys minted before the upgrade."
  • 2026-10-22. v1.0.0-rc.98 is released; the maintainer's 2026-10-23 comment on #2020 says it contains the fix commit.
  • 2026-10-23. The maintainer posts a statement on the issue, not to CERT/CC: "this is a low-risk issue. It did not downgrade security, and exploiting it required an already-leaked token", and closes the issue as resolved.
  • 2026-10-27. CERT/CC revises the note (revision 2); it still reads "no patch, vendor unreachable". v1.0.0-rc.100 is released the same day; its notes do not mention the fix. The vendor tab on the repository has no advisory; the GHSA id is the database's own.

Severity. Two readings disagree. The database prints 9.1 Critical (unauthenticated in the vector); the maintainer calls it low-risk because the path starts from a token the attacker already holds. This file says high: the precondition is real, but a Xerj token is exactly what an agent or MCP client stores in its config, and the bug turned a revocable leak into persistent access. status: patched rests on a default-on gate in a merged commit and the maintainer's statement that rc.98 carries it; the release notes of rc.98 and rc.100 do not name it, and this file did not diff the tag.

Why it matters. Xerj is built to sit behind agents ("Works with Claude Code, Cursor and any MCP client"), which means its tokens live in ~/.claude.json, .cursor/mcp.json and CI environments, the places every credential-stealing package this corpus tracks reads first. A stolen token that can mint its own successors survives the rotation the owner thinks they did.

Am I affected?

xerj --version 2>/dev/null                                   # anything before v1.0.0-rc.98
grep -rn 'xerj' ~/.claude.json .cursor/mcp.json .mcp.json 2>/dev/null   # where a token would sit
  • Any Xerj instance whose console was reachable on a release before rc.98, and any API token issued before the upgrade, is in scope. The Console API's token list (GET /auth/api-tokens, metadata only) shows tokens you did not mint.

If you are affected

  1. Upgrade to v1.0.0-rc.98 or later (rc.100 is newest as of 2026-10-30).
  2. From a passkey session, list and revoke every API token, then issue new ones; the fix does not revoke tokens minted before it. Remove passkeys you did not enrol.
  3. Treat anything the instance indexed as read by whoever held the token: if an MCP server was malicious covers the config-file and token rotation steps for the clients.

Prevention

  • Bind the console to loopback or an authenticated proxy; the README's default is loopback: MCP hygiene.
  • Scope and rotate the tokens agents hold: credential hygiene.

Sources