Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
📦
📦 npm
Not in CISA KEV
MEDIUM severity

CVE-2026-50029 — js-toml

MEDIUMFix: sunnyadn/js-toml@e0504fa

CVE-2026-50029 is a medium-severity (CVSS 5.3) CWE-697 vulnerability in js-toml. A fix is available for js-toml — see the affected versions and patch details below.

js-toml has silent type confusion via falsy-primitive duplicate-key bypass

Also known asGHSA-m34p-749j-x6m6
Published
Updated
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Oct 24, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

Proof-of-concept exploit code exists

  • CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
  • CISA assesses this as automatable — exploitation doesn’t require manual, per-target effort, which raises the odds of mass scanning and opportunistic attacks.

Exploitation and automatability from CISA’s SSVC triage for CVE-2026-50029.

EPSS Exploitation Probability

via FIRST.org ↗
0.4%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs33th percentile — riskier than 33% of all scored CVEsHighest risk
0.00%0.30%0.60%0.90%0.2%0.4%0.4%Sep 26Oct 26Oct 26

Probability of exploitation in the next 30 days, from FIRST.org EPSS.

How urgent is this, really

CVE-2026-50029 by exploitation likelihood (EPSS) against impact (CVSS). Outside the shaded patch-first corner.

Where this sits among everything scored

Of 395,249 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Counts from FIRST.org, log-scaled.

Real-World Exposure

1 pkg affected

How broadly this vulnerability is actually deployed: weekly install volume shows current usage, and reverse-dependency count shows how many other packages break if it stays unpatched.

34other npm packages depend on this — each one inherits the vulnerability until it's patched upstream
js-tomlnpm
24Kdownloads / week

Description

Summary

js-toml's interpreter checks whether a key already exists in a parser-built container with if (object[key]) instead of if (key in object). When the prior value is a falsy primitive — false, 0, 0n, 0.0, -0, or "" — the duplicate-key branch is skipped and the value is silently overwritten by a later sub-table, dotted-key sub-table, or array-of-tables sharing the same name. Per the TOML 1.0.0 spec ("Defining a key multiple times is invalid"; "You cannot define any key or table more than once"), this should be a parse error.

The result is structural type confusion of attacker-named keys in the value returned by load(). A boolean-typed false (or numeric 0) becomes a truthy object. Host applications that gate behavior on if (config.flag), if (!user.banned), if (config.allowDelete), or if (config.publicMode) will silently take the truthy branch.

This is distinct from GHSA-65fc-cr5f-v7r2 (the 1.0.2 prototype-pollution fix). Object.prototype is not polluted. The Object.create(null) mitigation from 1.0.2 is intact; the bug here is in the duplicate-key state machine, not in container construction.

Details

Two truthy checks are wrong:

src/load/interpreter.ts:214 — Interpreter.tryCreatingObject

if (object[key]) {            // falsy primitives slip through
    // duplicate-key logic
} else {
    object[key] = createSafeObject();   // silently overwrites the prior falsy value
    ...
}

src/load/interpreter.ts:278 — Interpreter.getOrCreateArray

if (object[first] && !Array.isArray(object[first])) {   // same flaw
    throw new DuplicateKeyError();
}
object[first] = object[first] || [];   // overwrites the prior falsy value

Both should use the in operator. Containers are created via Object.create(null), so in is unambiguous (no inherited keys to worry about).

The bug is reachable through every parent-walking interpreter path:

  • assignValue — dotted keys in key = value
  • createTable — [stdTable] headers
  • getOrCreateArray — [[arrayOfTables]] headers

PoC

isAdmin = false
[isAdmin]
forced = "yes"
import { load } from 'js-toml';

const config = load(`
isAdmin = false
[isAdmin]
forced = "yes"
`);

console.log(JSON.stringify(config));
// {"isAdmin":{"forced":"yes"}}

console.log(config.isAdmin ? 'BYPASS' : 'safe');
// BYPASS

if (config.isAdmin) {
  // attacker reaches admin-only code
}

Impact

Spec-violating input acceptance leading to structural type confusion. (CWE-697)

Suggested fix

in src/load/interpreter.ts

export class Interpreter extends BaseCstVisitor {
     ignoreImplicitDeclared,
     ignoreExplicitDeclared
   ) {
-    if (object[key]) {
+    if (key in object) {
       if (
         !isPlainObject(object[key]) ||
         (!ignoreExplicitDeclared &&
export class Interpreter extends BaseCstVisitor {
       return this.getOrCreateArray(keys, object[first], idx + 1);
     }

-    if (object[first] && !Array.isArray(object[first])) {
+    if (first in object && !Array.isArray(object[first])) {
       throw new DuplicateKeyError();
     }

     object[first] = object[first] || [];

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
📦npmjs-tomlall versions1.1.2npm install js-toml@1.1.2

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for js-toml, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update js-toml to 1.1.2 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-50029 is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

Frequently Asked Questions

### Summary `js-toml`'s interpreter checks whether a key already exists in a parser-built container with `if (object[key])` instead of `if (key in object)`. When the prior value is a falsy primitive — `false`, `0`, `0n`, `0.0`, `-0`, or `""` — the duplicate-key branch is skipped and the value is silently overwritten by a later sub-table, dotted-key sub-table, or array-of-tables sharing the same name. Per the TOML 1.0.0 spec ("Defining a key multiple times is invalid"; "You cannot define any key or table more than once"), this should be a parse error. The result is **structural type confusion
O3 Security · Impact-Aware SCA

Is CVE-2026-50029 in your dependencies?

Find it across npm, including transitive dependencies.

CVE-2026-50029: js-toml — Fixed in 1.1.2