Skip to content

Patch deepmerge for CVE-2026-93753 - #411

Merged
Vladimir Morozov (vmoroz) merged 1 commit into
microsoft:mainfrom
vmoroz:PR/deepmerge-prototype-poisoning
Nov 10, 2026
Merged

Vladimir Morozov (vmoroz) merged 1 commit into
microsoft:mainfrom
vmoroz:PR/deepmerge-prototype-poisoning

Conversation

@vmoroz

@vmoroz Vladimir Morozov (vmoroz) commented Nov 10, 2026 •

Copy link
Copy Markdown
Member

Summary

Dependabot reports CVE-2026-93753
in deepmerge@4.3.1. Rollup and Jest use it in three Yarn tool projects: the
Promise bundler (utils/promise), the hermes-parser tests, and the inspector
message generator tests. A __proto__ key in merged input can set the prototype
of the object that merge() returns. 4.3.1 is the latest release, and the
maintainer closed the fix
(TehShrike/deepmerge#274)
without merging it, so there is nothing to upgrade to.

  • Shared Yarn patch: apply the fix from Drop __proto__ keys regardless of the target's shape (CVE-2026-93753) TehShrike/deepmerge#274 through
    resolutions in all three projects. It drops only __proto__ in both object
    copy loops of the source, CommonJS, and UMD builds, so other keys and merge
    options behave as before. Each lockfile keeps the original 4.3.1 entry and adds
    one patched entry, without download URLs.
  • Regression test: .ado/scripts/deepmerge.test.js runs first in each
    project's test script. It checks the known attack shapes and normal merges
    against that project's installed copy. The inspector generator has no Jest
    tests, so its test script now passes --passWithNoTests; it always failed
    before.
  • Source lint: npm-source-lint accepts Yarn patch resolutions that carry
    Yarn's version, hash, and locator parameters when the patch file is
    inside the repository. Missing or outside patch files and encoded URLs are
    still rejected.

Dependabot still identifies the package as deepmerge 4.3.1, so its alerts stay
open. Remove the patch and the three resolutions once upstream releases a fix.

Test Plan

Local validation with the documented anonymous PackageFeedProxy environment:

  • .\dev npm-source-lint: passed, 13 projects, 0 violations.
  • node --test .\.ado\scripts\npm-source-lint.test.ts: passed, 58 tests.
  • node .\.yarn\releases\yarn-4.13.0.cjs --cwd <project> install --immutable:
    passed for all three projects.
  • node .\.yarn\releases\yarn-4.13.0.cjs --cwd .\utils\promise test: passed.
    The same regression test fails against unpatched deepmerge.
  • node .\.yarn\releases\yarn-4.13.0.cjs --cwd .\tools\hermes-parser\js test --runInBand tooling-glob-test:
    passed.
  • node .\.yarn\releases\yarn-4.13.0.cjs --cwd .\API\hermes\cdp\tools\hermes-inspector-msggen test:
    passed. The generator's Babel build and msggen --help also work.
  • Rebuilding Promise.js with rollup --config rollup.config.js index.js in
    utils/promise gives byte-identical output with patched and unpatched
    deepmerge.

CI runs the source lint and its tests, but not these three Yarn projects. The
native build was not run; no native code changed.

Microsoft Reviewers: Open in CodeFlow

deepmerge 4.3.1 is the latest release and has no fix for CVE-2026-93753
(GHSA-gcqx-w48m-w2h4): a __proto__ key can set the prototype of the
object that merge() returns. The maintainer closed the proposed fix,
TehShrike/deepmerge#274, without merging it.

Apply that fix as one shared Yarn patch in the three projects that use
deepmerge through Rollup or Jest: utils/promise, tools/hermes-parser/js
and API/hermes/cdp/tools/hermes-inspector-msggen. The patch drops
__proto__ in both object copy loops of the source, CommonJS and UMD
entry points. Each project's test script first runs a shared regression
test against its installed copy. The inspector message generator has no
Jest tests, so its test script now passes --passWithNoTests to Jest;
without it the script always failed.

Let npm-source-lint accept repository-local Yarn patch resolutions that
carry Yarn's version, hash and locator parameters. It still rejects
missing or out-of-repository patch files and encoded URLs.

Dependabot still identifies the package as deepmerge 4.3.1, so its
version-based alerts remain open.
Copilot AI balanced review requested due to automatic review settings November 10, 2026 01:16

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues are recorded for the deepmerge tooling-dependency hardening change.

No open issues 🎉

What changed in this PR

Mitigates CVE-2026-93753 in three Hermes JavaScript tooling projects by applying a shared patch to deepmerge 4.3.1. The changes affect tooling dependencies, not native VM code.

Changes:

  • Blocks __proto__ keys in deepmerge’s source, CommonJS, and UMD builds through shared Yarn patch resolutions.
  • Adds prototype-poisoning regression tests and normal-merge checks to each project’s test script.
  • Updates source lint to accept parameterized local Yarn patches while retaining path and remote-source checks.
File Description
utils/​promise/​yarn.lock Records the patched deepmerge dependency.
utils/​promise/​package.json Applies the patch and adds regression tests.
tools/​hermes-parser/​js/​yarn.lock Records the patched deepmerge dependency.
tools/​hermes-parser/​js/​package.json Applies the patch and runs regression tests before Jest.
API/​hermes/​cdp/​tools/​hermes-inspector-msggen/​yarn.lock Records the patched deepmerge dependency.
API/​hermes/​cdp/​tools/​hermes-inspector-msggen/​package.json Applies the patch, adds regression tests, and allows an empty Jest suite.
.yarn/​patches/​deepmerge-npm-4.3.1-prototype-poisoning.patch Filters __proto__ from both object-copy loops in all three builds.
.ado/​scripts/​npm-source-lint.ts Handles Yarn patch parameters while preserving source validation.
.ado/​scripts/​npm-source-lint.test.ts Covers valid patches, missing or outside paths, and encoded remote URLs.
.ado/​scripts/​deepmerge.test.js Checks attack cases and preserves expected merge behavior.

Review effort: Balanced

@github-actions

Copy link
Copy Markdown

Benchmark Results

Total benchmarks: 204

Inputs: baseline/baseline.json, bench_result.json

v8 (6176ms, 4977ms) -19.4%
v8 hermes (CI) hermes (CI)
v8-crypto 622.6ms 451.8ms
v8-deltablue 739.4ms 708.8ms
v8-raytrace 127.8ms 111.8ms
v8-regexp 593ms 348.2ms
v8-richards 937ms 762.6ms
v8-splay 225.2ms 211ms
v8-crypto (static) 452ms 375.2ms
v8-deltablue (static) 661.6ms 630.2ms
v8-raytrace (static) 91.8ms 96.2ms
v8-regexp (static) 749.2ms 354ms
v8-richards (static) 781.4ms 726.8ms
v8-splay (static) 195ms 200ms
test-suites (317886ms, 261461ms) -17.8%
test-suites hermes (CI) hermes (CI)
box2d 2949.8ms 2266ms
earley-boyer 2564.8ms 2262.2ms
navier-stokes 5743.8ms 3796.4ms
pdfjs 1038.2ms 757.8ms
gbemu 2296ms 1935.4ms
code-load 4472.8ms 4414.6ms
typescript 3381.8ms 2991.6ms
simpleSum 8855.4ms 5388ms
propAccess 2691.2ms 2055.8ms
allocObj 252ms 155.8ms
allocObjLit 6371.8ms 4519ms
allocNewObj 20053ms 15554.2ms
allocArray 249ms 155.6ms
allocNewArray 34956.8ms 26758.8ms
arrayRead 115.6ms 80.4ms
arrayReadByIndex 550ms 381.8ms
largeArrayRead 827.2ms 511.6ms
arrayWrite 261.8ms 199.2ms
largeArrayWrite 2218ms 1708.4ms
interp-dispatch 2880ms 2095.8ms
wb-perf 8327ms 5699.6ms
arrayReverse 40.6ms 32ms
arrayMap 1472.6ms 1127.2ms
arrayIndexOf 143.2ms 116.6ms
arrayLastIndexOf 148.6ms 133.6ms
arrayEvery 2308.8ms 1672.8ms
arraySome 2174.4ms 1646.6ms
arrayFill 2401.2ms 1903.2ms
arrayFilter 1866.4ms 1397.2ms
arrayFind 3207ms 2461.2ms
arrayFindIndex 3322ms 2490.4ms
arrayPop 1031.4ms 893.8ms
arrayReduce 1994.2ms 1516.6ms
arrayReduceRight 2075.8ms 1502.6ms
arrayShift 2065.8ms 1393.4ms
arrayUnshift 2109.4ms 1428.2ms
arrayIncludes 1216.6ms 113ms
arrayFrom 1102.8ms 976.4ms
arrayCopyWithin 1487ms 1025.2ms
stringFromCharCode 92.6ms 71ms
arraySlice 786.2ms 570ms
arraySplice 26.8ms 28.4ms
arrayOf 1048.8ms 737.2ms
stringCharAt 1346.6ms 930.6ms
stringMatch 3192.4ms 2353.2ms
stringSearch 3508.2ms 2312.2ms
stringStartsWith 701ms 417.4ms
stringEndsWith 650.4ms 386.6ms
stringIncludes 1520.6ms 1587ms
stringIndexOf 1555.6ms 1814.4ms
stringLastIndexOf 1821.4ms 1640.4ms
stringSplit 822ms 573.6ms
stringSlice 490ms 359ms
stringPadStart 2910.8ms 2393.6ms
stringPadEnd 2910ms 2426.8ms
regExpMatch 1506.2ms 546.6ms
regExpSearch 1227ms 988.4ms
regExpToString 1204ms 1201.4ms
stringReplace 1394.8ms 1431.4ms
regExpReplace 3309.2ms 318.6ms
regExpFlags 956.8ms 825.2ms
regExpSplit 1307.8ms 644ms
numberArrayReadWrite 2477.8ms 2096.6ms
protoCache 3594.6ms 2001.8ms
box2d (static) 1792.6ms 1744ms
earley-boyer (static) 1875.6ms 2000.4ms
navier-stokes (static) 3410.2ms 2855.2ms
pdfjs (static) 812.8ms 757.6ms
gbemu (static) 1797ms 1712.4ms
code-load (static) 4199.2ms 4375ms
typescript (static) 2685.6ms 2659.6ms
simpleSum (static) 936.2ms 1161ms
propAccess (static) 2298ms 2162.6ms
allocObj (static) 0.2ms 0.2ms
allocObjLit (static) 4154.4ms 3595.6ms
allocNewObj (static) 15749.2ms 16104.6ms
allocArray (static) 0.6ms 0.4ms
allocNewArray (static) 28494.2ms 28011.2ms
arrayRead (static) 74.2ms 62ms
arrayReadByIndex (static) 453.2ms 395.8ms
largeArrayRead (static) 552.2ms 483.8ms
arrayWrite (static) 178.4ms 143.2ms
largeArrayWrite (static) 1568.6ms 1289.4ms
interp-dispatch (static) 1918.8ms 1439ms
wb-perf (static) 8991.2ms 8106.4ms
arrayReverse (static) 37ms 32ms
arrayMap (static) 1064.8ms 980.2ms
arrayIndexOf (static) 128.2ms 114ms
arrayLastIndexOf (static) 138.8ms 131.2ms
arrayEvery (static) 1397.6ms 1433.2ms
arraySome (static) 1406.6ms 1440.2ms
arrayFill (static) 1969.2ms 1836.4ms
arrayFilter (static) 1061ms 1083ms
arrayFind (static) 2242.2ms 2061ms
arrayFindIndex (static) 2235.8ms 2081ms
arrayPop (static) 878ms 827.2ms
arrayReduce (static) 1311.8ms 1299.2ms
arrayReduceRight (static) 1322.6ms 1289.2ms
arrayShift (static) 1512ms 1409.4ms
arrayUnshift (static) 1568.2ms 1454.6ms
arrayIncludes (static) 963.4ms 111.2ms
arrayFrom (static) 1030.6ms 930.4ms
arrayCopyWithin (static) 1109.6ms 1036.6ms
stringFromCharCode (static) 77ms 66ms
arraySlice (static) 571.6ms 568ms
arraySplice (static) 26ms 28.6ms
arrayOf (static) 868.8ms 731.4ms
stringCharAt (static) 1100.6ms 901.2ms
stringMatch (static) 2227.6ms 2347.6ms
stringSearch (static) 2219.2ms 2311.2ms
stringStartsWith (static) 494.2ms 396ms
stringEndsWith (static) 457.8ms 409.4ms
stringIncludes (static) 1437.4ms 1332.6ms
stringIndexOf (static) 1440.8ms 1396.8ms
stringLastIndexOf (static) 1956.6ms 1572.2ms
stringSplit (static) 651.2ms 630.6ms
stringSlice (static) 445.6ms 348.8ms
stringPadStart (static) 2294.4ms 2406.2ms
stringPadEnd (static) 2499.2ms 2445.2ms
regExpMatch (static) 1483.2ms 545.6ms
regExpSearch (static) 1296.2ms 985ms
regExpToString (static) 1326.6ms 1198.2ms
stringReplace (static) 1455.6ms 1268.6ms
regExpReplace (static) 851.8ms 315.4ms
regExpFlags (static) 870.6ms 794.8ms
regExpSplit (static) 1200ms 649ms
numberArrayReadWrite (static) 2132.8ms 1730ms
protoCache (static) 3595.8ms 3299.4ms
micros (60876ms, 55888ms) -8.2%
micros hermes (CI) hermes (CI)
getNodeById.js 5273ms 4116.8ms
setInsert.js 2864.8ms 2073ms
stringify-number.js 1823.4ms 1369.2ms
typed-array-sort.js 22381ms 20721.8ms
getNodeById.js (static) 3941ms 3649.2ms
setInsert.js (static) 2517.6ms 2144.8ms
stringify-number.js (static) 1663.6ms 1375ms
typed-array-sort.js (static) 20411.4ms 20438.4ms
jit-benches (8563ms, 6199ms) -27.6%
jit-benches hermes (CI) hermes (CI)
idisp.js 2810.2ms 2095.8ms
idispn.js 3395.8ms 2203.2ms
idisp.js (static) 1906ms 1441.6ms
idispn.js (static) 450.8ms 458.4ms
many-subclasses (73437ms, 69748ms) -5.0%
many-subclasses hermes (CI) hermes (CI)
many.js 19104.8ms 17781ms
many-sh-1.js 7322.8ms 8638.2ms
many-sh-2.js 7329ms 8678.8ms
many-sh-3.js 7083.6ms 5411.6ms
many-sh-4.js 7235ms 5426.8ms
many.js (static) 15996ms 14690ms
many-sh-1.js (static) 2285.2ms 2288.6ms
many-sh-2.js (static) 2387ms 2371.6ms
many-sh-3.js (static) 2347.2ms 2226ms
many-sh-4.js (static) 2346ms 2235ms
map-objects (3627ms, 1744ms) -51.9%
map-objects hermes (CI) hermes (CI)
map-objects-untyped.js 1023.6ms 870.8ms
map-objects-typed.js 957.6ms -
map-objects-untyped.js (static) 917.2ms 873ms
map-objects-typed.js (static) 728.8ms -
map-strings (4335ms, 2199ms) -49.3%
map-strings hermes (CI) hermes (CI)
map-strings-untyped.js 1196.8ms 1087.6ms
map-strings-typed.js 1139.2ms -
map-strings-untyped.js (static) 1092.2ms 1111.8ms
map-strings-typed.js (static) 906.8ms -
nbody (3448ms, 2395ms) -30.5%
nbody hermes (CI) hermes (CI)
original/nbody.js 839.6ms 550ms
fully-typed/nbody.js 710ms 441ms
fully-typed/nbody.ts 865.2ms 558.6ms
original/nbody.js (static) 449.2ms 377.2ms
fully-typed/nbody.js (static) 135ms 91ms
fully-typed/nbody.ts (static) 449.2ms 377.6ms
string-switch (6503ms, 6438ms) -1.0%
string-switch (string-switch/plain) hermes (CI) hermes (CI)
bench.js 1316ms 1098.4ms
bench.js (static) 5187ms 5339.2ms
raytracer (5608ms, 5390ms) -3.9%
raytracer (raytracer/original) hermes (CI) hermes (CI)
bench-raytracer.js 1550.4ms 1429.8ms
raytracer.ts 1667.6ms 1405ms
bench-raytracer.js (static) 1198.4ms 1303.6ms
raytracer.ts (static) 1191.8ms 1251.4ms
MiniReact (30332ms, 21485ms) -29.2%
MiniReact hermes (CI) hermes (CI)
no-objects/out/simple-stripped.js 2239.2ms 790.4ms
no-objects/out/simple-lowered.js 2270.8ms 806.6ms
no-objects/out/music-stripped.js 42.6ms 39.6ms
no-objects/out/music-lowered.js 47.2ms 42.4ms
no-deps/stripped/MiniReact.js 5004ms 4240.8ms
no-deps/MiniReact.js 5256.2ms 4127.2ms
no-objects/out/simple.js 2260.4ms 759.2ms
no-objects/out/music.js 45.6ms 41ms
no-objects/out/simple-stripped.js (static) 1725.2ms 736.2ms
no-objects/out/simple-lowered.js (static) 1725.4ms 822.8ms
no-objects/out/music-stripped.js (static) 18.8ms 28.4ms
no-objects/out/music-lowered.js (static) 19.4ms 27.2ms
no-deps/stripped/MiniReact.js (static) 4024.8ms 4167.2ms
no-deps/MiniReact.js (static) 3938.2ms 4129.6ms
no-objects/out/simple.js (static) 1695.2ms 699.4ms
no-objects/out/music.js (static) 19ms 26.8ms
widgets (12934ms, 12323ms) -4.7%
widgets hermes (CI) hermes (CI)
simple-classes/widgets.js 1758.2ms 1907ms
original/es5/widgets.js 2798.8ms 2364.6ms
single-file/es5/widgets.js 2775.2ms 2357.8ms
simple-classes/widgets.js (static) 1012ms 1181ms
original/es5/widgets.js (static) 2283.2ms 2271ms
single-file/es5/widgets.js (static) 2306.2ms 2241.4ms

@vmoroz
Vladimir Morozov (vmoroz) merged commit 15d3824 into microsoft:main Nov 10, 2026
19 checks passed
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.

3 participants