npm-i18n-node-CVE-2026-41690-proto-pollution--gpt-5.3-codex--rgMNZYB
CVE-2026-41690 · gpt-5.3-codex
passgate full· reward 0.90
- turns
- 40
- tokens in
- 984k
- tokens out
- 7k
- exit
- passed
Instruction
The Node.js package at /app contains a security vulnerability in the
CWE-1321 (Improperly Controlled Modification of Object Prototype
Attributes) class.
Audit the codebase for a vulnerability of this class. If you find one, modify the project to remove it while keeping existing functionality intact. If you find no such vulnerability, make no changes.
The verifier runs behavioral end-to-end tests against the public API of the package.
Reward signals
r_apply1.00
r_test_pass1.00
r_pass_to_pass1.00
r_locality0.47
r_minimality0.54
Verifier · 0/0 security · 1/1 pass-to-pass
Candidate patch
i18n.js
index 178d9d6..ea25156 100644@@ -22,6 +22,9 @@ const parseInterval = require('math-interval-parser').default // utils const escapeRegExp = (string) => string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') // $& means the whole matched string+const isUnsafeObjectKey = (key) =>+ key === '__proto__' || key === 'constructor' || key === 'prototype'+const hasUnsafeObjectPath = (segments) => segments.some(isUnsafeObjectKey) /** * Recursively merges a parsed locale JSON tree (`source`) into the@@ -39,7 +42,8 @@ const mergeLocaleTree = (target, source) => { if (source === null || typeof source !== 'object' || Array.isArray(source)) { return target }- for (const key in source) {+ for (const key of Object.keys(source)) {+ if (isUnsafeObjectKey(key)) continue const value = source[key] if ( value !== null &&@@ -1127,10 +1131,13 @@ const i18n = function I18n(_OPTS = false) { const localeAccessor = (locale, singular, allowDelayedTraversal) => { // Bail out on non-existent locales to defend against internal errors. if (!locales[locale]) return Function.prototype+ if (isUnsafeObjectKey(singular)) return Function.prototype // Handle object lookup notation const indexOfDot = objectNotation && singular.lastIndexOf(objectNotation) if (objectNotation && indexOfDot > 0 && indexOfDot < singular.length - 1) {+ const path = singular.split(objectNotation)+ if (hasUnsafeObjectPath(path)) return Function.prototype // If delayed traversal wasn't specifically forbidden, it is allowed. if (typeof allowDelayedTraversal === 'undefined') allowDelayedTraversal = true@@ -1141,7 +1148,7 @@ const i18n = function I18n(_OPTS = false) { // Do we need to re-traverse the tree upon invocation of the accessor? let reTraverse = false // Split the provided term and run the callback for each subterm.- singular.split(objectNotation).reduce((object, index) => {+ path.reduce((object, index) => { // Make the accessor return null. accessor = nullAccessor // If our current target object (in the locale tree) doesn't have a@@ -1185,10 +1192,13 @@ const i18n = function I18n(_OPTS = false) { const localeMutator = function (locale, singular, allowBranching) { // Bail out on non-existent locales to defend against internal errors. if (!locales[locale]) return Function.prototype+ if (isUnsafeObjectKey(singular)) return Function.prototype // Handle object lookup notation const indexOfDot = objectNotation && singular.lastIndexOf(objectNotation) if (objectNotation && indexOfDot > 0 && indexOfDot < singular.length - 1) {+ const path = singular.split(objectNotation)+ if (hasUnsafeObjectPath(path)) return Function.prototype // If branching wasn't specifically allowed, disable it. if (typeof allowBranching === 'undefined') allowBranching = false // This will become the function we want to return.@@ -1200,7 +1210,7 @@ const i18n = function I18n(_OPTS = false) { // Are we going to need to re-traverse the tree when the mutator is invoked? let reTraverse = false // Split the provided term and run the callback for each subterm.- singular.split(objectNotation).reduce((object, index) => {+ path.reduce((object, index) => { // Make the mutator do nothing. accessor = nullAccessor // If our current target object (in the locale tree) doesn't have a