Skip to content

fix(scripts): guard validate-i18n against prototype pollution#966

Merged
jedrazb merged 1 commit into
mainfrom
fix/codeql-validate-i18n-proto-pollution
Jun 21, 2026
Merged

fix(scripts): guard validate-i18n against prototype pollution#966
jedrazb merged 1 commit into
mainfrom
fix/codeql-validate-i18n-proto-pollution

Conversation

@jedrazb

@jedrazb jedrazb commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Resolves the medium-severity CodeQL js/prototype-pollution-utility alert in scripts/validate-i18n.mjs.

setNestedValue (and its sibling deleteNestedValue) built object keys from a dotted path, so a segment like __proto__, constructor, or prototype could mutate the prototype chain instead of the target object. Both now reject those segments up front. deleteNestedValue matters most: its paths come from locale-file data (getLeafPaths), i.e. untrusted input, so it's guarded for symmetry per the "check sibling sinks" rule.

Verified bun run i18n:validate still passes for all locales and legit key paths are unaffected. Dev-tooling only, no package impact (no changeset).

🤖 Generated with Claude Code

@vercel

vercel Bot commented Jun 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docx-editor Ready Ready Preview, Comment Jun 20, 2026 9:37pm

Request Review

@eigenpal-release-pal

eigenpal-release-pal Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA ✍️ ✅

Posted by the CLA bot.

@greptile-apps

greptile-apps Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR guards setNestedValue in scripts/validate-i18n.mjs against prototype pollution by rejecting path segments that match __proto__, constructor, or prototype — resolving the CodeQL js/prototype-pollution-utility alert. It is dev-tooling only with no package-level impact.

  • Adds UNSAFE_KEYS set and a pre-flight check in setNestedValue that throws on any matching segment.
  • The sibling function deleteNestedValue was not given the same guard; its traversal loop reads stack[i][parts[i]] without filtering unsafe keys, leaving the same class of prototype-chain access open on the deletion path.

Confidence Score: 3/5

The fix correctly hardens setNestedValue but leaves the parallel deleteNestedValue function without the same guard, so the prototype-chain traversal risk the PR set out to eliminate is still present on the deletion path.

The change only partially addresses the vulnerability it targets. deleteNestedValue shares the same traversal pattern and is called with the same locale-derived paths, but the UNSAFE_KEYS pre-flight check was not added there. Until both functions are guarded, the script remains exploitable via a crafted locale key on the --fix deletion path.

scripts/validate-i18n.mjs — specifically the deleteNestedValue function starting at line 55.

Security Review

  • Prototype pollution via deleteNestedValue (scripts/validate-i18n.mjs, line 55): The UNSAFE_KEYS guard added to setNestedValue was not applied to deleteNestedValue. A path segment of __proto__ would cause the traversal loop to push Object.prototype onto the internal stack, and the subsequent delete call would then operate on the prototype rather than the locale object. The same UNSAFE_KEYS check should be added at the top of deleteNestedValue.

Important Files Changed

Filename Overview
scripts/validate-i18n.mjs Adds UNSAFE_KEYS guard to setNestedValue to fix prototype-pollution CodeQL alert, but the symmetric deleteNestedValue function was not hardened with the same guard, leaving the same class of traversal risk open.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[cmdValidate called with fix=true] --> B[Iterate missing paths from en.json]
    B --> C[setNestedValue locale, path, null]
    C --> D{Any segment in UNSAFE_KEYS?}
    D -- Yes --> E[throw Error — GUARDED ✓]
    D -- No --> F[Traverse & assign value]

    A --> G[Iterate extra paths from locale JSON]
    G --> H[deleteNestedValue locale, path]
    H --> I{Any segment in UNSAFE_KEYS?}
    I -- No check --> J[stack push via parts_i]
    J --> K{parts_i == __proto__ ?}
    K -- Yes --> L[Pushes prototype object onto stack]
    L --> M[delete from prototype chain ⚠️ NOT GUARDED]
    K -- No --> N[Normal traversal & delete]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[cmdValidate called with fix=true] --> B[Iterate missing paths from en.json]
    B --> C[setNestedValue locale, path, null]
    C --> D{Any segment in UNSAFE_KEYS?}
    D -- Yes --> E[throw Error — GUARDED ✓]
    D -- No --> F[Traverse & assign value]

    A --> G[Iterate extra paths from locale JSON]
    G --> H[deleteNestedValue locale, path]
    H --> I{Any segment in UNSAFE_KEYS?}
    I -- No check --> J[stack push via parts_i]
    J --> K{parts_i == __proto__ ?}
    K -- Yes --> L[Pushes prototype object onto stack]
    L --> M[delete from prototype chain ⚠️ NOT GUARDED]
    K -- No --> N[Normal traversal & delete]
Loading

Comments Outside Diff (1)

  1. scripts/validate-i18n.mjs, line 55-69 (link)

    P1 security deleteNestedValue missing the same UNSAFE_KEYS guard

    deleteNestedValue traverses the same dotted path with stack[i][parts[i]] but never checks against UNSAFE_KEYS. If a path segment is __proto__, stack[i]["__proto__"] resolves to the prototype object rather than an own property, pushing it onto stack. The subsequent delete stack[stack.length - 1][parts[parts.length - 1]] then operates on the prototype — the symmetric risk to what setNestedValue was just hardened against. The caller in cmdValidate feeds it extra paths derived from locale JSON files, so a crafted key name would reach this code path.

Reviews (1): Last reviewed commit: "fix(scripts): guard validate-i18n setNes..." | Re-trigger Greptile

@jedrazb jedrazb force-pushed the fix/codeql-validate-i18n-proto-pollution branch from 7d102bf to f8a5d2f Compare June 20, 2026 21:34
…lution

setNestedValue and deleteNestedValue built object keys from a dotted
path, so a segment like `__proto__`/`constructor`/`prototype` could
mutate the prototype chain instead of the target object. Both now reject
such segments up front. deleteNestedValue matters most: its paths come
from locale-file data (getLeafPaths), i.e. untrusted input.

Resolves CodeQL js/prototype-pollution-utility.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jedrazb

jedrazb commented Jun 21, 2026

Copy link
Copy Markdown
Contributor Author

Greptile triage — the deleteNestedValue prototype-pollution gap from your Security Review is already fixed in 914bd7da (this review saw 7d102bfe).

deleteNestedValue now has the same UNSAFE_KEYS pre-flight guard as setNestedValue — exactly your recommendation, and the more important one since its paths come from locale-file data (untrusted) via getLeafPaths. Verified: a __proto__-bearing path throws, and Object.prototype.toString stays intact. bun run i18n:validate still passes for all locales.

@jedrazb jedrazb merged commit 6fc02bd into main Jun 21, 2026
11 checks passed
@jedrazb jedrazb deleted the fix/codeql-validate-i18n-proto-pollution branch June 21, 2026 14:46
@eigenpal-release-pal

Copy link
Copy Markdown
Contributor

🚀 Released in v1.9.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant