The Password Was Public and the Check Never Looked Down: LobsterAI CVE-2026-112014 and Omnigent CVE-2026-112015

At 12:17:14 UTC on 18 October 2026, NVD published two CVEs 136 milliseconds apart — CVE-2026-112014 at .167Z and CVE-2026-112015 at .303Z. Both come from VulnCheck, both carry vulnStatus Received, and both are against AI agent software. One is a desktop AI agent that encrypted your exported provider API keys with a password sitting in plaintext in its public repository. The other is an agent orchestration framework whose upload validator checked the working directory of the bundle you submitted and never looked inside the sub-agents nested within it. Different products, different languages, different CWEs — and the same underlying failure: a trust assumption that was true of the part of the input somebody inspected, and false of the part nobody did.

Both fixes also shipped weeks before the identifiers did, which continues the patch-before-advisory pattern we have been tracking all month. The LobsterAI fix landed 13 October and released 16 October; the Omnigent fix merged 15 September and released the same day. If you scan by CVE feed alone, you were blind to both during exactly the window when the patches — and therefore the diffs describing the bugs — were public.

LobsterAI: AES-GCM locked with a key the whole internet holds

LobsterAI (netease-youdao/LobsterAI, ~6,100 stars) describes itself as an open-source, desktop-grade AI agent — data analysis, slides, docs, video and web research, built on OpenClaw, running tools on the user’s real desktop. Its “Export Providers” feature writes every configured third-party provider’s API key to a JSON backup file, encrypted with AES-GCM under a key derived by PBKDF2. That is a serious-looking construction, and it is exactly what makes the bug worth stating precisely: the cryptography was right and the password was 'lobsterai-APP'.

In src/renderer/constants/app.ts, read at the affected tag 2026.9.23:

export const EXPORT_PASSWORD = 'lobsterai-APP';

And in src/renderer/components/Settings.tsx, the export button’s handler took no password input from the user at all:

const DEFAULT_EXPORT_PASSWORD = EXPORT_PASSWORD;

const handleExportProviders = async () => {
  const payload = await buildProvidersExport(DEFAULT_EXPORT_PASSWORD);
};

The import path’s default decryption attempt used the identical constant. The salt is randomized per export, but as the reporter’s issue notes, the salt is embedded in the file — so anyone holding a default-flow export can re-derive the same key from the public password and recover every provider API key inside, with no other information. The NVD description says exactly this, and it scores the CVE 6.8 medium under CVSS 4.0 (CWE-321, use of hard-coded cryptographic key). Note the attack vector the scorer chose: AV:L. The ciphertext has to reach the attacker first — via a shared backup, a synced cloud drive, an email or support attachment, or accidental public exposure. This is not a remote-exploitation bug; it is a backup-file-is-the-secret bug, where the “encryption” contributed confidentiality only against attackers who cannot read GitHub.

The report itself is a model of the genre. Issue #2851, filed by carfeii on 13 October against the then-current default branch, names the files, quotes the constants, explains why the randomized salt does not save the construction, and withholds reproduction steps on request. The fix commit (1eb28c40, same day, “Fixes #2851”) is a genuine redesign rather than a secret rotation: provider exports default to credential-free output, and authenticated encrypted v3 backups now require a user-supplied password, validated for presence and minimum length in the new providerTransferCrypto.ts (PasswordRequired, WeakPassword), with legacy v1/v2 import compatibility and regression tests across four new files. The hardcoded constant is deleted from app.ts — the diff for that file is +0/−1. Release 2026.10.16 carries it under the modest line “Secure provider exports and preserve legacy imports.”

We covered the sibling of this bug five days ago: MCP Context Forge’s CVE-2026-53709, a hardcoded HS256 JWT key that the project’s own weak-secrets blocklist enumerated. LobsterAI is the same class with the irony removed and the blast radius widened — not a platform-admin bootstrap, but every third-party provider key the user had configured: OpenAI, Anthropic, and whatever else the agent could spend money and assume identity with. A stolen provider key is an identity, not only a billing event; inference bought with it is attributable to the victim. And note the outlook the issue implies but does not state: every default-flow export file ever produced is encrypted under a password that is now additionally a matter of public CVE record. Rotation of the keys, not just the upgrade, is the remediation.

Omnigent: the validator checked the root and trusted the recursion

Omnigent (omnigent-ai/omnigent, ~10,900 stars) is a Python AI agent framework and meta-harness — it orchestrates Claude Code, Codex, Cursor and custom agents, enforces policies and sandboxing, and runs agents on shared runner infrastructure. Servers in that deployment shape accept untrusted agent bundles uploaded by authenticated users. The upload validator in omnigent/server/bundles.py is the boundary, and it had already been hardened once: after GHSA-p8rw-8qj3-hf33 (CVE-2026-62677), version 0.3.0 shipped a check rejecting absolute or workspace-escaping os_env.cwd on the uploaded agent — because on a runner without OMNIGENT_RUNNER_WORKSPACE set, that value becomes the agent environment root and copytree source, exposing the host filesystem.

CVE-2026-112015 records what the 0.3.0 fix did not do: recurse. An authenticated user could upload a bundle whose nested sub-agent carried the absolute or parent-traversing working directory, bypassing the root-only validation, and the sub-agent’s unchecked cwd would become its environment root — read and write access to host files as the runner user, reachable with the agent’s own file and shell tools. CVSS 4.0 rates it 7.7 high (CWE-22 path traversal; network-reachable, low-privilege, no user interaction). The vendor advisory, GHSA-p5x3-3gqh-x44g, states the mechanism in one sentence worth quoting whole:

The upload validator checked the root agent's os_env.cwd but did not
recurse through sub-agents. The same value rejected on the root agent
was therefore accepted when placed in a sub-agent.

Two details in that advisory deserve attention. First, scope honesty: it names the reported configuration as disabling the OS sandbox (sandbox.type: none) and affected runners as those without OMNIGENT_RUNNER_WORKSPACE set, and it explicitly places trusted single-user operation — where the operator already controls executed code — outside the threat model. Second, lineage honesty: it identifies itself as a follow-up to the original advisory, warns that “the original advisory’s 0.3.0 patch must not be interpreted as fixing this nested path,” and discloses that a second, later report of the same defect (GHSA-f2xx-j6gv-ffc8) was closed as a duplicate. The fix, PR #7457 (“validate nested bundle working directories,” merged 15 September), validates working directories on the root agent, every terminal, and recursively nested sub-agents and their terminals, in POSIX and Windows forms, with regression coverage in tests/server/test_uploaded_bundle_cwd_validation.py — shipped in 0.14.0 the same day.

The NVD affected range is therefore “0.3.0 before 0.14.0,” which reads oddly until you understand the history: 0.3.0 is when the root check shipped, and every version since carried the nested hole. Operators who upgraded to 0.3.0 for the original advisory and stopped have been exposed to the sibling for over a year. That is the operational sting of partial fixes to recursive structures, and it is why the advisory’s explicit non-interpretation clause matters — it is doing the work that version-range metadata cannot.

Two shapes of the same lesson

Set side by side, the pair is a compact illustration of where agent tooling keeps failing. LobsterAI’s defect is a secret that was never secret: a constant in a public repository, in a feature whose entire purpose is to cross a trust boundary (this machine to wherever the backup goes). The encryption was reviewed — AES-GCM, PBKDF2, random salt — and the password was not, because it looked like configuration. Omnigent’s defect is a check that stopped one level too high: the validator enforced containment on the object it was shown and assumed the objects inside were the same shape. Both are failures to follow the untrusted input all the way down — one down into the repository, one down into the bundle tree.

Both also extend October’s running theme of fixes that predate their identifiers. The Omnigent patch was public for 33 days before the CVE; the LobsterAI patch for five. Komodo’s CVE-2026-110005 followed the same arc (patched July, disclosed October), as did Coder’s eleven retroactive CVEs and the MCP Context Forge record above. The practical consequence is unchanged: during the gap, the fix diff is public and the identifier your scanner keys on does not exist. For Omnigent, that gap contained a working-directory escape on shared runner infrastructure. Inventory by deployed application and read the repository advisory list directly; the feed is the last place these arrive, not the first.

What to do

For LobsterAI, upgrade to 2026.10.16 — and then rotate every provider API key that was ever present in a default-flow export file, because those files were encrypted under a password that is now in a CVE description. Treat any lobsterai-providers-*.json backup that left your machine as a plaintext key disclosure. Going forward, the new default export is credential-free, which is the correct default for a file designed to be moved around; use the password-protected v3 path only with a real password.

For Omnigent, upgrade to 0.14.0 or later (e.g. uv tool install --upgrade --python 3.12 'omnigent==0.14.0'). If your runners accept bundles from users you do not fully trust, audit for exploitation before upgrading: the sub-agent cwd becomes the environment root, so look for sessions whose working directories resolve outside the workspace. Set OMNIGENT_RUNNER_WORKSPACE and do not run untrusted bundles with sandbox.type: none — the advisory names both as prerequisites, which makes them your cheapest mitigations. And if you upgraded to 0.3.0 for CVE-2026-62677 and considered the bundle-upload boundary closed, re-open that conclusion: the 0.3.0 patch never covered the nested path.

Neither CVE is in the CISA KEV catalogue (checked 18 October 2026; direct lookup, zero hits for both identifiers), and no exploitation has been reported for either. That is the expected state for CVEs less than a day old, not a safety signal — particularly for Omnigent, where the fix diff has been readable since September.

Our verification was read-only. Both CVE identifiers, publication timestamps (12:17:14.167Z and 12:17:14.303Z), vulnStatus (Received), source identifier (disclosure@vulncheck.com), descriptions, CVSS 4.0 and 3.1 vectors, CWE assignments and reference URLs were read first-hand from the NVD 2.0 API. The LobsterAI mechanism — EXPORT_PASSWORD constant, DEFAULT_EXPORT_PASSWORD wiring, no password prompt on the export path, PBKDF2/AES-GCM construction with embedded salt — was read first-hand in src/renderer/constants/app.ts and src/renderer/components/Settings.tsx at tag 2026.9.23, and in the reporter’s issue #2851 (filed by carfeii, 13 October 2026). The fix scope — credential-free default exports, user-required password for v3 backups, deletion of the constant, PR #2863 — was read first-hand in fix commit 1eb28c40 and the 2026.10.16 release notes. The Omnigent mechanism and remediation were read first-hand in repository advisory GHSA-p5x3-3gqh-x44g (severity high), fix PR #7457 (merged 15 September 2026) and the v0.14.0 release notes; the pre-existing root-only guard and its GHSA-p8rw-8qj3-hf33 reference were read first-hand in bundles.py at v0.13.0. Repository descriptions, star counts and languages were read from the GitHub API. KEV status was checked directly against CISA’s catalogue. Brave Search was used for discovery; no independent third-party technical analysis of either CVE existed at the time of writing. We installed neither product, created no account, uploaded no bundle, exported nothing, and executed no proof of concept.

Sources: