How Prototype Pollution Happens in Vue I18n's handleFlatJson and How to Fix It
Introduction
In the unibestX project, Trivy security scanning detected a high-severity prototype pollution vulnerability (CVE-2025-27597) lurking in the dependency tree within pnpm-lock.yaml. The vulnerability exists in @intlify/core-base version 9.1.9, specifically in the handleFlatJson function—a critical component that processes internationalization (i18n) message data.
This matters because Vue I18n applications worldwide handle user-influenced translation data, and if that data contains maliciously crafted keys, it could allow attackers to inject properties into JavaScript's prototype chain. For developers building multilingual applications, this represents a silent threat that bypasses traditional input validation because it operates at the prototype level, affecting every object in the application.
The Vulnerability Explained
What is Prototype Pollution?
Prototype pollution is a JavaScript-specific vulnerability where an attacker modifies the prototype of built-in objects (like Object.prototype) by injecting special property names. In JavaScript, when you access a property on an object, the engine searches up the prototype chain. If an attacker pollutes Object.prototype.__proto__ or Object.prototype.constructor, they can:
- Override application logic
- Bypass security checks
- Inject malicious code into unexpected locations
- Cause denial of service
The Vulnerable Code Pattern
The handleFlatJson function in @intlify/core-base 9.1.9 processes flat JSON structures used for i18n messages. Here's the problematic pattern:
// Vulnerable pattern in @intlify/core-base 9.1.9
// When processing i18n message data like:
const messageData = {
"greeting": "Hello",
"__proto__": { "isAdmin": true }, // Malicious key!
"farewell": "Goodbye"
};
// The handleFlatJson function would iterate through keys and assign them:
for (const key in messageData) {
targetObject[key] = messageData[key]; // VULNERABLE: No validation!
}
The vulnerability occurs because handleFlatJson accepts user-controlled keys from i18n message data without validating whether they're dangerous prototype-polluting properties.
Attack Scenario
Imagine an attacker crafts a malicious i18n translation file:
{
"messages": {
"welcome": "Welcome to our app",
"__proto__": {
"isAuthenticated": true,
"isAdmin": true,
"permissions": ["delete", "write"]
}
}
}
When the Vue I18n application loads this translation data through handleFlatJson, the __proto__ key gets processed, and suddenly:
// After prototype pollution:
const user = {};
console.log(user.isAdmin); // true (polluted!)
console.log(user.isAuthenticated); // true (polluted!)
console.log(user.permissions); // ["delete", "write"] (polluted!)
An attacker could bypass authentication checks, escalate privileges, or manipulate application state across the entire application without modifying a single legitimate property.
Real-World Impact
For applications using @intlify/core-base 9.1.9:
- Remote Translation Services: If i18n messages come from a CDN or API, an attacker could compromise that source
- User-Generated Content: If users can upload or provide translation files, malicious actors could inject prototype pollution
- Supply Chain Risk: Third-party translation management tools could be compromised, distributing polluted message files
The Fix
The fix upgrades @intlify/core-base from version 9.1.9 to 11.1.10, which implements proper input validation in the handleFlatJson function.
Changes Made
Looking at the PR diff, two critical files were updated:
1. package.json
- "vue": "^3.5.13"
+ "vue": "^3.5.13",
+ "@intlify/core-base": "11.1.10"
The explicit dependency pinning ensures the patched version is used.
2. pnpm-lock.yaml
- '@intlify/core-base@9.1.9':
- resolution: {integrity: sha512-x5T0p/Ja0S8hs5xs+ImKyYckVkL4CzcEXykVYYV6rcbXxJTe2o58IquSqX9bdncVKbRZP7GlBU1EcRaQEEJ+vw==}
- engines: {node: '>= 10'}
+ '@intlify/core-base@11.1.10':
+ resolution: {integrity: sha512-JhRb40hD93Vk0BgMgDc/xMIFtdXPHoytzeK6VafBNOj6bb6oUZrGamXkBKecMsmGvDQQaPRGG2zpa25VCw8pyw==}
+ engines: {node: '>= 16'}
Notice the engine requirement increased from Node 10+ to Node 16+, indicating substantial internal changes for security.
How Version 11.1.10 Fixes the Issue
The patched version implements a property name allowlist approach:
// Fixed pattern in @intlify/core-base 11.1.10 (conceptual)
const DANGEROUS_KEYS = ['__proto__', 'constructor', 'prototype'];
function handleFlatJson(messageData, targetObject) {
for (const key in messageData) {
// VALIDATION: Check if key is dangerous
if (DANGEROUS_KEYS.includes(key)) {
console.warn(`Skipping dangerous key: ${key}`);
continue; // Skip prototype-polluting keys
}
// SAFE: Only assign validated keys
targetObject[key] = messageData[key];
}
}
The fix ensures that:
- ✅ Legitimate i18n keys like greeting, farewell, welcome still work
- ✅ Dangerous keys like __proto__, constructor, prototype are rejected
- ✅ The prototype chain remains unpolluted
- ✅ No valid translations are lost
Prevention & Best Practices
For Your Own Code
- Never Trust User-Controlled Keys: When building object assignment operations, always validate property names:
```javascript
// BAD: Direct assignment without validation
const config = {};
Object.assign(config, userInput);
// GOOD: Validate keys first
const SAFE_KEYS = ['theme', 'language', 'notifications'];
const config = {};
for (const key of Object.keys(userInput)) {
if (SAFE_KEYS.includes(key)) {
config[key] = userInput[key];
}
}
```
-
Use Object.create(null): When possible, create objects without a prototype:
javascript // More resistant to prototype pollution const safeObject = Object.create(null); -
Freeze Prototypes: In security-critical code, freeze prototypes:
javascript Object.freeze(Object.prototype); Object.freeze(Array.prototype); -
Keep Dependencies Updated: Prototype pollution vulnerabilities are discovered regularly. Use tools like
npm auditandpnpm audit:
bash pnpm audit pnpm update -
Use Static Analysis: Enable Trivy, Semgrep, or similar tools in your CI/CD pipeline to catch prototype pollution patterns before deployment.
Security Standards
- CWE-1321: Improperly Controlled Modification of Object Prototype Attributes in JavaScript
- OWASP: Prototype Pollution in the OWASP Attack Database
Key Takeaways
-
Prototype pollution in
handleFlatJsoncould inject properties into Object.prototype through malicious i18n message keys like__proto__, silently affecting every object in the application. -
The @intlify/core-base 9.1.9 vulnerability specifically affects applications that load i18n messages from untrusted sources (APIs, user uploads, third-party CDNs).
-
Upgrading to @intlify/core-base 11.1.10 adds validation that rejects dangerous property names before they reach object assignment, preventing prototype pollution while preserving legitimate translations.
-
Always validate property names when assigning user-controlled data to objects—don't rely on input validation alone; specifically reject
__proto__,constructor, andprototype. -
Prototype pollution is invisible to traditional security testing because it operates at the prototype level; automated tools like Trivy are essential for catching these vulnerabilities.
How Orbis AppSec Detected This
Source: I18n message data loaded from external sources or user-provided translation files in the @intlify/core-base handleFlatJson function
Sink: Unsafe property assignment operations in handleFlatJson that don't validate property names before adding them to the target object
Missing control: No validation to reject prototype-polluting keys (__proto__, constructor, prototype) before object assignment
CWE: CWE-1321 (Improperly Controlled Modification of Object Prototype Attributes in JavaScript)
Fix: Upgrade @intlify/core-base from 9.1.9 to 11.1.10, which implements property name validation in handleFlatJson to reject dangerous keys before they can pollute the prototype chain.
Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.
Conclusion
Prototype pollution in Vue I18n's handleFlatJson represents a subtle but dangerous vulnerability that could compromise applications handling multilingual content from untrusted sources. By upgrading @intlify/core-base to version 11.1.10, you're not just closing a security hole—you're ensuring that your application's prototype chain remains protected against a class of attacks that traditional input validation often misses.
The lesson here extends beyond this specific vulnerability: in JavaScript, always validate property names when processing user-controlled data, especially when that data will be assigned to objects. Prototype pollution is a reminder that security must operate at multiple levels—and that keeping dependencies up-to-date is a critical part of your defense strategy.
Make the upgrade today, and ensure your i18n pipeline remains secure.