Repository navigation
fix(security): require a Node.js with the eMudhra CA fix (v2.9.2) - #216
Merged
Merged
Conversation
Eight eMudhra intermediate CAs were issued with 8-bit keys, so anyone can
sign a certificate for api.withone.ai. Node.js blocks them in the TLS
handshake from 22.23.4, 24.22.1 and 26.12.1. The CLI runs on the user's
Node, so on anything older it would hand a network attacker the API key.
bin/cli.js now runs src/node-check.ts (its own build entry) before loading
the CLI or opening any connection, and exits 1 on an unpatched Node, with
the usual {"error": ...} JSON in agent mode. engines.node becomes
^22.23.4 || ^24.22.1 || >=26.12.1, the CI matrix moves to 22/24/26, and the
tsup target to node22.
BREAKING CHANGE: Node.js 18, 20, 23, 25 and unpatched 22/24/26 are no
longer supported.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…de help - Version 2.9.2 instead of 3.0.0, so ^2 dependents get the check too. - The error prints the command that updates Node for how it was installed (nvm, nvm-windows, Volta, fnm, Homebrew, else nodejs.org), staying on a line that got the fix and moving an end-of-life line to 24. - Agent mode adds code NODE_UPGRADE_REQUIRED, currentNode, requiredNode and upgrade to the JSON error. - ONE_ALLOW_UNPATCHED_NODE=1 runs anyway with a warning on every run (silent in agent mode so stderr can't corrupt the JSON). - nodeHasTlsFix is exported for library consumers. - Versions read 22.23.4+, 24.22.1+ or 26.12.1+ everywhere. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…g output On Node 24+ the test runner reports through the same process.stdout the mem tests patch to capture agent JSON. find-by-key captured one of those binary chunks and failed to parse it; export-import swallowed them. Both now capture only string writes and forward everything else, as actions.preflight.test.ts already did. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Eight eMudhra intermediate CAs were issued with 8-bit private keys, so anyone can compute them and sign a valid-looking certificate for any domain, including
api.withone.ai. Node.js blocks them inside the TLS handshake from 22.23.4, 24.22.1 and 26.12.1 (nodejs/node#66871, shipped 2026-10-21). Node 18, 20, 23 and 25 are end-of-life and will never get the fix.The CLI doesn't ship a runtime. It runs on whatever
nodethe user has, so the Node release only protects users who upgrade. This PR makes that upgrade mandatory.Alternative to #215. That PR filters CA roots in userland. It fails CI on Node 18, and on older Node it matches and removes AAA Certificate Services (the legacy Comodo/Sectigo root) by name. It also can't act on the old Node lines that need it most; there it only warns. Node's fix blocks the eight intermediates by key, which userland can't do, so we require it instead.
What
src/node-check.tsis its own tsup entry, and its shared chunk has no other imports.bin/cli.jsruns it before loading the CLI or opening any connection. On an unpatched Node it exits 1.--agent) get JSON withcode: "NODE_UPGRADE_REQUIRED",currentNode,requiredNodeandupgrade.ONE_ALLOW_UNPATCHED_NODE=1runs anyway, with a security warning on every run. The warning is silent in agent mode, where stderr would corrupt the JSON. It's documented in the README only, not in the agent error or the skill.onebinary.nodeHasTlsFixis exported so they can check for themselves.engines.node:>=18→^22.23.4 || ^24.22.1 || >=26.12.1. CI matrix: 18/20/22 → 22/24/26. tsup target:node18→node22.skills/one/SKILL.md,guide-content.ts(show the user theupgradecommand instead of retrying), CLAUDE.md, and therun-tests.mjscomment.^2dependents get the check too. Lockfile version fields edited by hand per CLAUDE.md.Auto-update installs with
--force, which ignoresengines. So users on old Node still auto-update to 2.9.2, and then get the upgrade instructions instead of running exposed.Verified
To update: brew upgrade node, exit 1.--agent listreturns the JSON error withcodeandupgrade, exit 1.ONE_ALLOW_UNPATCHED_NODE=1, it prints the warning and then2.9.2, exit 0. In agent mode, stdout is clean JSON.2.9.2, exit 0.npm run buildandnpm run typecheckpass. The newnode-check.test.tscovers version gating, each version manager, Homebrew formulas and the fallback, and it passes.npm testlocally: 680 passed, 6 failed. All 6 are theresolveConfigtests inconfig.test.ts(macOS/private/varvs/vartmpdir path), a file this PR doesn't touch; fix(security): stop trusting the compromised eMudhra roots (v2.9.2) #215 reports the same 6 failing on untouchedmain.After merge
one syncjobs and installs with auto-update off.npm deprecate: the flaw is in Node, not in older CLI versions.🤖 Generated with Claude Code