{"id":20686649,"url":"https://github.com/ericcornelissen/pp-runtime-gadgets","last_synced_at":"2026-04-22T19:34:53.847Z","repository":{"id":241795824,"uuid":"802894204","full_name":"ericcornelissen/pp-runtime-gadgets","owner":"ericcornelissen","description":"Gadgets in the JavaScript runtime based on the ECMAScript specification","archived":false,"fork":false,"pushed_at":"2025-07-12T12:04:32.000Z","size":1004,"stargazers_count":1,"open_issues_count":5,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-15T01:29:21.845Z","etag":null,"topics":["javascript","prototype-pollution"],"latest_commit_sha":null,"homepage":"https://ericcornelissen.github.io/pp-runtime-gadgets/","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ericcornelissen.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-05-19T14:54:57.000Z","updated_at":"2025-06-13T06:19:44.000Z","dependencies_parsed_at":"2024-05-30T04:50:53.948Z","dependency_job_id":"bcdaaa56-02fd-4b0a-b10d-4fe62b92ba3a","html_url":"https://github.com/ericcornelissen/pp-runtime-gadgets","commit_stats":null,"previous_names":["ericcornelissen/pp-runtime-gadgets"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ericcornelissen/pp-runtime-gadgets","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericcornelissen%2Fpp-runtime-gadgets","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericcornelissen%2Fpp-runtime-gadgets/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericcornelissen%2Fpp-runtime-gadgets/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericcornelissen%2Fpp-runtime-gadgets/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ericcornelissen","download_url":"https://codeload.github.com/ericcornelissen/pp-runtime-gadgets/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericcornelissen%2Fpp-runtime-gadgets/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32152600,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-22T17:06:48.269Z","status":"ssl_error","status_checked_at":"2026-04-22T17:06:19.037Z","response_time":58,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript","prototype-pollution"],"created_at":"2024-11-16T22:36:17.732Z","updated_at":"2026-04-22T19:34:53.820Z","avatar_url":"https://github.com/ericcornelissen.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- SPDX-License-Identifier: CC-BY-4.0 --\u003e\n\n# Prototype Pollution affects the JavaScript Runtime\n\nThis repository aims to provide a list of JavaScript language functionality that\ncan be affected by prototype pollution.\n\n## TODO\n\n- [x] Manually check all 160 `Get(` in the [fixed spec reference].\n- [ ] Double check all 160 `Get(` in the [fixed spec reference].\n- [ ] Manually check other `Get(`-like functions in the [fixed spec reference].\n- [ ] Replicate `Reflect.ownKeys`' gadget with other uses of\n      `[[OwnPropertyKeys]]`.\n- [ ] Similar gadgets to those for `RegExp.prototype[@@match]` and\n      `RegExp.prototype[@@matchAll]` in other `RegExp.prototype` functions.\n\n## Reproduce\n\nTo reproduce the results:\n\n- for Node.js: `make test-node` (or `make test-node-docker`)\n- for Deno: `make test-deno` (or `make test-deno-docker`)\n- for Browsers: `make test-web` and open \u003chttp://localhost:8080\u003e in a browser.\n\n## Overview\n\nThe table below provides an overview of known functions affected by prototype\npollution in the JavaScript language, or _gadgets_. This list is not exhaustive\nboth in terms of affected APIs and usable properties.\n\nAll gadgets were tested on Node.js v24.0.1, Deno v1.46.1, Chromium (Desktop)\nv136, and Firefox (Desktop) v138.\n\nThe \"Score\" is an attempt at capturing how easy it is to exploit the gadget in\nan arbitrary program, lower is easier. The contributors to the score are defined\nin the gadget PoC and the scoring system is defined in [`score.js`].\n\n[`score.js`]: ./pocs/score.js\n\n| API                                 | Prop(s)                               | Score | Node.js | Deno           | Chromium      | Firefox       |\n| ----------------------------------- | ------------------------------------- | ----- | ------- | -------------- | ------------- | ------------- |\n| `[[ToPrimitive]]`                   | [`'toString'`][o0002]                 | `3`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'valueOf'`][o0003]                  | `3`   | Yes     | Yes            | Yes           | Yes           |\n| `new AggregateError`                | [`'cause'`][o0080]                    | `0`   | Yes     | Yes            | Yes           | Yes           |\n| `new ArrayBuffer`                   | [`'maxByteLength'`][o0004]            | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.from`                        | [`\u003cn\u003e`][o0044]                        | `2`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.at`                | [`\u003cn\u003e`][o0046]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.concat`            | [`@@isConcatSpreadable`][o0088]       | `5`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`\u003cn\u003e`][o0087]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.copyWithin`        | [`\u003cn\u003e`][o0077]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.every`             | [`\u003cn\u003e`][o0073]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.fill`              | [`\u003cn\u003e`][o0054]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.filter`            | [`\u003cn\u003e`][o0072]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.find`              | [`\u003cn\u003e`][o0051]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.findIndex`         | [`\u003cn\u003e`][o0048]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.findLast`          | [`\u003cn\u003e`][o0050]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.findLastIndex`     | [`\u003cn\u003e`][o0055]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.flat`              | [`\u003cn\u003e`][o0089]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.flatMap`           | [`\u003cn\u003e`][o0091]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`\u003cn\u003e`][o0092]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.forEach`           | [`\u003cn\u003e`][o0090]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.includes`          | [`\u003cn\u003e`][o0058]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.indexOf`           | [`\u003cn\u003e`][o0094]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.join`              | [`\u003cn\u003e`][o0052]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.lastIndexOf`       | [`\u003cn\u003e`][o0095]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.map`               | [`\u003cn\u003e`][o0096]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.pop`               | [`\u003cn\u003e`][o0049]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.reduce`            | [`\u003cn\u003e`][o0070]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`\u003cn\u003e`][o0097]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.reduceRight`       | [`\u003cn\u003e`][o0074]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.reverse`           | [`\u003cn\u003e`][o0098]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.shift`             | [`\u003cn\u003e`][o0099]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`\u003cn\u003e`][o0056]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.slice`             | [`\u003cn\u003e`][o0100]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.some`              | [`\u003cn\u003e`][o0071]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.sort`              | [`\u003cn\u003e`][o0057]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.splice`            | [`\u003cn\u003e`][o0076]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`\u003cn\u003e`][o0101]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`\u003cn\u003e`][o0103]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.toLocaleString`    | [`\u003cn\u003e`][o0102]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.toReversed`        | [`\u003cn\u003e`][o0047]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.toSorted`          | [`\u003cn\u003e`][o0059]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.toSpliced`         | [`\u003cn\u003e`][o0053]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`\u003cn\u003e`][o0104]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.toString`          | [`join`][o0093]                       | `5`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.unshift`           | [`\u003cn\u003e`][o0116]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Array.prototype.with`              | [`\u003cn\u003e`][o0045]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `new Error`                         | [`'cause'`][o0079]                    | `0`   | Yes     | Yes            | Yes           | Yes           |\n| `Function.prototype.apply`          | [`\u003cn\u003e`][o0005]                        | `2`   | Yes     | Yes            | Yes           | Yes           |\n| `Function.prototype.bind`           | [`'name'`][o0078]                     | `0`   | No      | No             | No            | No            |\n| `isNaN`                             | [`'valueOf'`][o0117]                  | `3`   | Yes     | Yes            | Yes           | Yes           |\n| `Iterator`                          | [`'done'`][o0032]                     | `2`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'next'`][o0006]                     | `5`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'return'`][o0064]                   | `5`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'value'`][o0033]                    | `2`   | Yes     | Yes            | Yes           | Yes           |\n| `JSON.stringify`                    | [`\u003cn\u003e`][o0108]                        | `2`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'toJSON'`][o0025]                   | `3`   | Yes     | Yes            | Yes           | Yes           |\n| `new Map`                           | [`0,1`][o0105]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Object.defineProperties`           | [`'configurable'`][o0109]             | `0`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'enumerable'`][o0110]               | `0`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'get'`][o0111]                      | `3`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'set'`][o0112]                      | `3`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'value'`][o0113]                    | `1`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'writable'`][o0114]                 | `0`   | Yes     | Yes            | Yes           | Yes           |\n| `Object.defineProperty`             | [`'configurable'`][o0007]             | `0`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'enumerable'`][o0008]               | `0`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'get'`][o0009]                      | `3`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'set'`][o0010]                      | `3`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'value'`][o0011]                    | `1`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'writable'`][o0012]                 | `0`   | Yes     | Yes            | Yes           | Yes           |\n| `Object.entries`                    | [`'enumerable'`][o0013]               | `2`   | Yes     | Yes            | Yes           | Yes           |\n| `Object.fromEntries`                | [`0,1`][o0014]                        | `1`   | Yes     | Yes            | Yes           | Yes           |\n| `Object.keys`                       | [`\u003ck\u003e,'enumerable'`][o0115]           | `5`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'enumerable'`][o0015]               | `3`   | Yes     | Yes            | Yes           | Yes           |\n| `Object.prototype.toString`         | [`@@toStringTag`][o0034]              | `3`   | Yes     | Yes            | Yes           | Yes           |\n| `Object.values`                     | [`'enumerable'`][o0016]               | `3`   | Yes     | Yes            | Yes           | Yes           |\n| `new Proxy`                         | [`'apply'`][o0040]                    | `3`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'construct'`][o0065]                | `3`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'defineProperty'`][o0067]           | `3`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'deleteProperty'`][o0041]           | `3`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'getOwnPropertyDescriptor'`][o0038] | `3`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'isExtensible'`][o0042]             | `3`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'ownKeys'`][o0037]                  | `3`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'preventExtensions'`][o0069]        | `3`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'set'`][o0036]                      | `3`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'setPrototypeOf'`][o0075]           | `3`   | Yes     | Yes            | Yes           | Yes           |\n| `Reflect.apply`                     | [`\u003cn\u003e`][o0017]                        | `2`   | Yes     | Yes            | Yes           | Yes           |\n| `Reflect.construct`                 | [`\u003cn\u003e`][o0018]                        | `2`   | Yes     | Yes            | Yes           | Yes           |\n| `Reflect.defineProperty`            | [`'configurable'`][o0026]             | `0`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'enumerable'`][o0027]               | `0`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'get'`][o0028]                      | `3`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'set'`][o0029]                      | `3`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'value'`][o0030]                    | `1`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'writable'`][o0031]                 | `0`   | Yes     | Yes            | Yes           | Yes           |\n| `Reflect.ownKeys`                   | [`\u003cn\u003e`][o0001]                        | `5`   | Yes     | Yes            | Yes           | Yes           |\n| `new RegExp`                        | [`'source'`][o0039]                   | `2`   | Yes     | Yes            | Yes           | Yes           |\n| `RegExp.prototype[@@match]`         | [`0`][o0084]                          | `2`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'exec'`][o0066]                     | `2`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'flags'`][o0082]                    | `2`   | Yes     | No             | Yes           | Yes           |\n|                                     | [`'global'`][o0083]                   | `2`   | No      | Yes            | No            | No            |\n| `RegExp.prototype[@@matchAll]`      | [`'flags'`][o0086]                    | `2`   | Yes     | Yes            | Yes           | Yes           |\n|                                     | [`'lastIndex'`][o0085]                | `2`   | Yes     | Yes            | Yes           | Yes           |\n| `new SharedArrayBuffer`             | [`'maxByteLength'`][o0019]            | `1`   | Yes     | Yes            | _Unsupported_ | _Unsupported_ |\n| `Set.prototype.difference`          | [`'has','size'`][o0063]               | `3`   | Yes     | Yes            | Yes           | Yes           |\n| `Set.prototype.intersection`        | [`'has','size'`][o0061]               | `3`   | Yes     | Yes            | Yes           | Yes           |\n| `Set.prototype.isDisjointFrom`      | [`'has','size'`][o0062]               | `3`   | Yes     | Yes            | Yes           | Yes           |\n| `Set.prototype.isSubsetOf`          | [`'has','size'`][o0106]               | `3`   | Yes     | Yes            | Yes           | Yes           |\n| `Set.prototype.isSupersetOf`        | [`'has','size'`][o0107]               | `3`   | Yes     | Yes            | Yes           | Yes           |\n| `Set.prototype.symmetricDifference` | [`'has','size'`][o0060]               | `3`   | Yes     | Yes            | Yes           | Yes           |\n| `Set.prototype.union`               | [`'has','size'`][o0035]               | `3`   | Yes     | Yes            | Yes           | Yes           |\n| `String.prototype.endsWith`         | [`@@match`][o0020]                    | `5`   | Yes     | Yes            | Yes           | Yes           |\n| `String.prototype.includes`         | [`@@match`][o0021]                    | `5`   | Yes     | Yes            | Yes           | Yes           |\n| `String.prototype.matchAll`         | [`@@match,@@matchAll,'flags'`][o0022] | `8`   | Yes     | Yes            | Yes           | Yes           |\n| `String.prototype.replaceAll`       | [`@@match,@@replace,'flags'`][o0023]  | `8`   | Yes     | Yes            | Yes           | Yes           |\n| `String.prototype.startsWith`       | [`@@match`][o0024]                    | `5`   | Yes     | Yes            | Yes           | Yes           |\n| `String.raw`                        | [`'raw'`][o0081]                      | `2`   | Yes     | Yes            | Yes           | Yes           |\n| `TypedArray.from`                   | [`\u003cn\u003e`][o0068]                        | `2`   | Yes     | Yes            | Yes           | Yes           |\n| `with`                              | [`@@unscopables`][o0043]              | `3`   | Yes     | _Unsupported_  | Yes           | Yes           |\n\nwhere:\n\n- `/[0-9]+/`: is a specific numeric property.\n- `/'.+?'/`: is a specific string property.\n- `/@@.+?/`: is a specific [well-known symbol].\n- `\u003cn\u003e`: is any numeric property.\n- `\u003ck\u003e`: is any property.\n\nnotes:\n\n- `Array.prototype[*]` gadgets generally should also work on their TypedArray\n  equivalent.\n- `Iterator[*]` gadgets generally should also work on their async equivalent.\n\n[o0001]: ./pocs/[[OwnPropertyKeys]]-\u003cn\u003e.PoC.js\n[o0002]: ./pocs/[[ToPrimitive]]-toString.PoC.js\n[o0003]: ./pocs/[[ToPrimitive]]-valueOf.PoC.js\n[o0004]: ./pocs/ArrayBuffer-maxByteLength.PoC.js\n[o0005]: ./pocs/FunctionPrototypeApply-\u003cn\u003e.PoC.js\n[o0006]: ./pocs/Iterator-next.PoC.js\n[o0007]: ./pocs/ObjectDefineProperty-configurable.PoC.js\n[o0008]: ./pocs/ObjectDefineProperty-enumerable.PoC.js\n[o0009]: ./pocs/ObjectDefineProperty-get.PoC.js\n[o0010]: ./pocs/ObjectDefineProperty-set.PoC.js\n[o0011]: ./pocs/ObjectDefineProperty-value.PoC.js\n[o0012]: ./pocs/ObjectDefineProperty-writable.PoC.js\n[o0013]: ./pocs/ObjectEntries-enumerable.PoC.js\n[o0014]: ./pocs/ObjectFromEntries-0,1.PoC.js\n[o0015]: ./pocs/ObjectKeys-enumerable.PoC.js\n[o0016]: ./pocs/ObjectValues-enumerable.PoC.js\n[o0017]: ./pocs/ReflectApply-\u003cn\u003e.PoC.js\n[o0018]: ./pocs/ReflectConstruct-\u003cn\u003e.PoC.js\n[o0019]: ./pocs/SharedArrayBuffer-maxByteLength.PoC.js\n[o0020]: ./pocs/StringPrototypeEndsWith-@@match.PoC.js\n[o0021]: ./pocs/StringPrototypeIncludes-@@match.PoC.js\n[o0022]: ./pocs/StringPrototypeMatchAll-@@match,@@matchAll,flag.PoC.js\n[o0023]: ./pocs/StringPrototypeReplaceAll-@@match,@@replace,flag.PoC.js\n[o0024]: ./pocs/StringPrototypeStartsWith-@@match.PoC.js\n[o0025]: ./pocs/JSONStringify-toJSON.PoC.js\n[o0026]: ./pocs/ReflectDefineProperty-configurable.PoC.js\n[o0027]: ./pocs/ReflectDefineProperty-enumerable.PoC.js\n[o0028]: ./pocs/ReflectDefineProperty-get.PoC.js\n[o0029]: ./pocs/ReflectDefineProperty-set.PoC.js\n[o0030]: ./pocs/ReflectDefineProperty-value.PoC.js\n[o0031]: ./pocs/ReflectDefineProperty-writable.PoC.js\n[o0032]: ./pocs/Iterator-done.PoC.js\n[o0033]: ./pocs/Iterator-value.PoC.js\n[o0034]: ./pocs/ObjectToString-@@toStringTag.PoC.js\n[o0035]: ./pocs/SetPrototypeUnion-has,size.PoC.js\n[o0036]: ./pocs/Proxy-set.PoC.js\n[o0037]: ./pocs/Proxy-ownKeys.PoC.js\n[o0038]: ./pocs/Proxy-getOwnPropertyDescriptor.PoC.js\n[o0039]: ./pocs/RegExp-source.PoC.js\n[o0040]: ./pocs/Proxy-apply.PoC.js\n[o0041]: ./pocs/Proxy-deleteProperty.PoC.js\n[o0042]: ./pocs/Proxy-isExtensible.PoC.js\n[o0043]: ./pocs/with-@@unscopables.PoC.cjs\n[o0044]: ./pocs/ArrayFrom-\u003cn\u003e.PoC.js\n[o0045]: ./pocs/ArrayPrototypeWith-\u003cn\u003e.PoC.js\n[o0046]: ./pocs/ArrayPrototypeAt-\u003cn\u003e.PoC.js\n[o0047]: ./pocs/ArrayPrototypeToReversed-\u003cn\u003e.PoC.js\n[o0048]: ./pocs/ArrayPrototypeFindIndex-\u003cn\u003e.PoC.js\n[o0049]: ./pocs/ArrayPrototypePop-\u003cn\u003e.PoC.js\n[o0050]: ./pocs/ArrayPrototypeFindLast-\u003cn\u003e.PoC.js\n[o0051]: ./pocs/ArrayPrototypeFind-\u003cn\u003e.PoC.js\n[o0052]: ./pocs/ArrayPrototypeJoin-\u003cn\u003e.PoC.js\n[o0053]: ./pocs/ArrayPrototypeToSpliced-\u003cn\u003e.PoC-1.js\n[o0054]: ./pocs/ArrayPrototypeFill-\u003cn\u003e.PoC.js\n[o0055]: ./pocs/ArrayPrototypeFindLastIndex-\u003cn\u003e.PoC.js\n[o0056]: ./pocs/ArrayPrototypeShift-\u003cn\u003e.PoC-1.js\n[o0057]: ./pocs/ArrayPrototypeSort-\u003cn\u003e.PoC.js\n[o0058]: ./pocs/ArrayPrototypeIncludes-\u003cn\u003e.PoC.js\n[o0059]: ./pocs/ArrayPrototypeToSorted-\u003cn\u003e.PoC.js\n[o0060]: ./pocs/SetPrototypeSymmetricDifference-has,size.PoC.js\n[o0061]: ./pocs/SetPrototypeIntersection-has,size.PoC.js\n[o0062]: ./pocs/SetPrototypeIsDisjointFrom-has,size.PoC.js\n[o0063]: ./pocs/SetPrototypeDifference-has,size.PoC.js\n[o0064]: ./pocs/Iterator-return.PoC.js\n[o0065]: ./pocs/Proxy-construct.PoC.js\n[o0066]: ./pocs/RegExpPrototype@@match-exec.PoC.js\n[o0067]: ./pocs/Proxy-defineProperty.PoC.js\n[o0068]: ./pocs/TypedArrayFrom-\u003cn\u003e.PoC.js\n[o0069]: ./pocs/Proxy-preventExtensions.PoC.js\n[o0070]: ./pocs/ArrayPrototypeReduce-\u003cn\u003e.PoC-1.js\n[o0071]: ./pocs/ArrayPrototypeSome-\u003cn\u003e.PoC.js\n[o0072]: ./pocs/ArrayPrototypeFilter-\u003cn\u003e.PoC.js\n[o0073]: ./pocs/ArrayPrototypeEvery-\u003cn\u003e.PoC.js\n[o0074]: ./pocs/ArrayPrototypeReduceRight-\u003cn\u003e.PoC.js\n[o0075]: ./pocs/Proxy-setPrototypeOf.PoC.js\n[o0076]: ./pocs/ArrayPrototypeSplice-\u003cn\u003e.PoC-1.js\n[o0077]: ./pocs/ArrayPrototypeCopyWithin-\u003cn\u003e.PoC.js\n[o0078]: ./pocs/FunctionPrototypeBind-name.PoC.js\n[o0079]: ./pocs/Error-cause.PoC.js\n[o0080]: ./pocs/AggregateError-cause.PoC.js\n[o0081]: ./pocs/StringRaw-raw.PoC.js\n[o0082]: ./pocs/RegExpPrototype@@match-flags.PoC.js\n[o0083]: ./pocs/RegExpPrototype@@match-global.PoC.js\n[o0084]: ./pocs/RegExpPrototype@@match-0.PoC.js\n[o0085]: ./pocs/RegExpPrototype@@matchAll-lastIndex.PoC.js\n[o0086]: ./pocs/RegExpPrototype@@matchAll-flags.PoC.js\n[o0087]: ./pocs/ArrayPrototypeConcat-\u003cn\u003e.PoC.js\n[o0088]: ./pocs/ArrayPrototypeConcat-@@isConcatSpreadable.PoC.js\n[o0089]: ./pocs/ArrayPrototypeFlat-\u003cn\u003e.PoC.js\n[o0090]: ./pocs/ArrayPrototypeForEach-\u003cn\u003e.PoC.js\n[o0091]: ./pocs/ArrayPrototypeFlatMap-\u003cn\u003e.PoC-1.js\n[o0092]: ./pocs/ArrayPrototypeFlatMap-\u003cn\u003e.PoC-2.js\n[o0093]: ./pocs/ArrayPrototypeToString-join.PoC.js\n[o0094]: ./pocs/ArrayPrototypeIndexOf-\u003cn\u003e.PoC.js\n[o0095]: ./pocs/ArrayPrototypeLastIndexOf-\u003cn\u003e.PoC.js\n[o0096]: ./pocs/ArrayPrototypeMap-\u003cn\u003e.PoC.js\n[o0097]: ./pocs/ArrayPrototypeReduce-\u003cn\u003e.PoC-2.js\n[o0098]: ./pocs/ArrayPrototypeReverse-\u003cn\u003e.PoC.js\n[o0099]: ./pocs/ArrayPrototypeShift-\u003cn\u003e.PoC-2.js\n[o0100]: ./pocs/ArrayPrototypeSlice-\u003cn\u003e.PoC.js\n[o0101]: ./pocs/ArrayPrototypeSplice-\u003cn\u003e.PoC-2.js\n[o0102]: ./pocs/ArrayPrototypeToLocaleString-\u003cn\u003e.PoC.js\n[o0103]: ./pocs/ArrayPrototypeSplice-\u003cn\u003e.PoC-3.js\n[o0104]: ./pocs/ArrayPrototypeToSpliced-\u003cn\u003e.PoC-2.js\n[o0105]: ./pocs/Map-0,1.PoC.js\n[o0106]: ./pocs/SetPrototypeIsSubsetof-has,size.PoC.js\n[o0107]: ./pocs/SetPrototypeIsSupersetOf-has,size.PoC.js\n[o0108]: ./pocs/JSONStringify-\u003cn\u003e.PoC.js\n[o0109]: ./pocs/ObjectDefineProperties-configurable.PoC.js\n[o0110]: ./pocs/ObjectDefineProperties-enumerable.PoC.js\n[o0111]: ./pocs/ObjectDefineProperties-get.PoC.js\n[o0112]: ./pocs/ObjectDefineProperties-set.PoC.js\n[o0113]: ./pocs/ObjectDefineProperties-value.PoC.js\n[o0114]: ./pocs/ObjectDefineProperties-writable.PoC.js\n[o0115]: ./pocs/ObjectKeys-\u003ck\u003e,enumerable.PoC.js\n[o0116]: ./pocs/ArrayPrototypeUnshift-\u003cn\u003e.PoC.js\n[o0117]: ./pocs/isNan-valueOf.PoC.js\n\n## Unaffected\n\nThe table below lists evaluated sections in the ECMAScript spec which were\ndeemed unaffected by prototype pollution.\n\n| API                                    | Property        | Reason                                                                                                                   |\n| -------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------ |\n| [`[[DefineOwnProperty]]`][i0008]       | `\u003ck\u003e`           | _Not evaluated_                                                                                                          |\n| [`[[Get]]`][i0009]                     | `\u003ck\u003e`           | _Not evaluated_                                                                                                          |\n| [`[[GetOwnProperty]]`][i0007]          | `\u003ck\u003e`           | _Not evaluated_                                                                                                          |\n| [`ArraySpeciesCreate`][i0006]          | `'constructor'` | Object on which lookup should happen must be an array, which means it must have a `constructor` property.                |\n|                                        | `@@species`     | Object on which lookup should happen must be an array constructor, which means it must have a `@@species` property.      |\n| [`Array.prototype.reduceRight`][i0019] | `\u003cn\u003e`           | Cannot affect initial value because the last index necessarily coincides with the array length.                          |\n| [`HasBinding`][i0003]                  | `@@unscopable`  | _Not evaluated_                                                                                                          |\n|                                        | `\u003cn\u003e`           | _Not evaluated_                                                                                                          |\n| [`CopyDataProperties`][i0001]          | `\u003ck\u003e`           | Implementation should `ToObject` the subject, hence all own keys are actually own keys.                                  |\n| [`CreateRegExpStringIterator`][i0018]  | `0`             | Only ever invoked on a properly constructed RegExp, meaning `0` is always defined.                                       |\n|                                        | `'lastIndex'`   | This property is explicitly set in all functions calling this abstract operation.                                        |\n| [`Error.prototype.toString`][i0014]    | `'name'`        | `this` will realistically only be an instance of Error, in which case `name` is defined on `Error.prototype`.            |\n|                                        | `'message'`     | `this` will realistically only be an instance of Error, in which case `message` is defined on `Error.prototype`.         |\n| [`Function.prototype.bind`][i0013]     | `'length'`      | It is checked that the property is an own property before it is accessed.                                                |\n| [`GetBindingValue`][i0004]             | `\u003cn\u003e`           | Checks `HasProperty` before `Get`.                                                                                       |\n| [`GetPrototypeFromConstructor`][i0005] | `'prototype'`   | Object on which lookup should happen must be a callable, which means it must have a `prototype` property.                |\n| [`GetSubstitution`][i0015]             | `\u003ck\u003e`           | Getting properties on the `namedCapture` object which always has a `null` prototype.                                     |\n| [`Object.assign`][i0010]               | `\u003ck\u003e`           | Will only access keys in the `[[OwnPropertyKeys]]` set.                                                                  |\n| `Object.freeze`                        | [`\u003ck\u003e`][i0022]  | -                                                                                                                        |\n| `Object.seal`                          | [`\u003ck\u003e`][i0021]  | -                                                                                                                        |\n| [`ObjectDefineProperties`][i0011]      | `\u003ck\u003e`           | Will only access keys in the `[[OwnPropertyKeys]]` set.                                                                  |\n| [`OrdinaryHasInstance`][i0002]         | `'prototype'`   | Object on which lookup should happen must be a callable, which means it must have a `prototype` property.                |\n| [`PromiseResolve`][i0020]              | `'constructor'` | It is checked that `x` is a `Promise`.                                                                                   |\n| [`RegExpBuiltinExec`][i0017]           | `'lastIndex'`   | `R` is only ever an initialized RegExp.                                                                                  |\n| [`RegExp.prototype.toString`][i0012]   | `'source'`      | `this` will realistically only be an instance of RegExp, in which case `source` is always defined.                       |\n|                                        | `'flags'`       | `this` will realistically only be an instance of RegExp, in which case `flags` is always defined.                        |\n| [`RegExp.prototype[@@match]`][i0016]   | `'unicode'`     | Only affects `lastIndex` incrementing, effect depends on user implementation (Note: may be accessed instead of \"flags\"). |\n|                                        | `'unicodeSets'` | Only affects `lastIndex` incrementing, effect depends on user implementation (Note: may be accessed instead of \"flags\"). |\n\n[i0001]: https://tc39.es/ecma262/#sec-copydataproperties\n[i0002]: https://tc39.es/ecma262/#sec-ordinaryhasinstance\n[i0003]: https://tc39.es/ecma262/#sec-object-environment-records-hasbinding-n\n[i0004]: https://tc39.es/ecma262/#sec-object-environment-records-getbindingvalue-n-s\n[i0005]: https://tc39.es/ecma262/#sec-getprototypefromconstructor\n[i0006]: https://tc39.es/ecma262/#sec-arrayspeciescreate\n[i0007]: https://tc39.es/ecma262/#sec-arguments-exotic-objects-getownproperty-p\n[i0008]: https://tc39.es/ecma262/#sec-arguments-exotic-objects-defineownproperty-p-desc\n[i0009]: https://tc39.es/ecma262/#sec-arguments-exotic-objects-get-p-receiver\n[i0010]: https://tc39.es/ecma262/#sec-object.assign\n[i0011]: https://tc39.es/ecma262/#sec-objectdefineproperties\n[i0012]: https://tc39.es/ecma262/#sec-regexp.prototype.tostring\n[i0013]: https://tc39.es/ecma262/#sec-function.prototype.bind\n[i0014]: https://tc39.es/ecma262/#sec-error.prototype.tostring\n[i0015]: https://tc39.es/ecma262/#sec-getsubstitution\n[i0016]: https://tc39.es/ecma262/#sec-regexp.prototype-%symbol.match%\n[i0017]: https://tc39.es/ecma262/#sec-regexpbuiltinexec\n[i0018]: https://tc39.es/ecma262/#sec-createregexpstringiterator\n[i0019]: https://tc39.es/ecma262/#sec-array.prototype.reduceright\n[i0020]: https://tc39.es/ecma262/#sec-array.prototype.reduceright\n[i0021]: ./pocs/ObjectSeal-\u003ck\u003e.PoC.js\n[i0022]: ./pocs/ObjectFreeze-\u003ck\u003e.PoC.js\n\n## Methodology\n\nTwo approaches have been used to compile the list of usable and unusable runtime\ngadgets.\n\n### Manual (in progress)\n\nA manual review of the ECMAScript [spec] has been conducted, specifically\nlooking for use of the `Get(O, P)` function. This function gets property `P`\nfrom object `O`, hence if `P` is missing from `O` the lookup could be affected\nby prototype pollution.\n\nDuring manual testing, a proxy object like the one shown below is used to find\nout what properties are being looked up exactly.\n\n```javascript\nconst proxy = new Proxy({}, {\n  get(target, property, _receiver) {\n   if (!Object.hasOwn(target, property)) {\n    console.log(\"looked up:\", property);\n   }\n\n   return target[property];\n  },\n});\n```\n\n### `tc39/test262`\n\nThe [tc39/test262] suite is an extensive conformance test suite for the\nECMAScript specification. Hence, it should provide extensive coverage of all\nJavaScript language features, and can thus be used to help automatically find\ngadgets.\n\nThe [tc39](./tc39/) directory contains the materials necessary for using the\ntest262 suite in a semi-automated pipeline for finding runtime gadgets. More\ndetail can be found in its `README.md`.\n\n## Related Work\n\n\u003c!-- 2025 --\u003e\n- [Follow My Flow: Unveiling Client-Side Prototype Pollution Gadgets from One Million Real-World Websites](https://www.computer.org/csdl/proceedings-article/sp/2025/223600a016/21B7Q7OZKms)\n\n  Toolchain the find prototype pollution gadgets in websites. It cannot find the\n  gadgets described in this repository because it cannot instrument the relevant\n  code.\n\n\u003c!-- 2024 --\u003e\n- [Undefined-oriented Programming: Detecting and Chaining Prototype Pollution Gadgets in Node. js Template Engines for Malicious Consequences](https://www.computer.org/csdl/proceedings-article/sp/2024/313000a121/1Ub23uAzNcI)\n\n  Toolchain to find prototype pollution in the Node.js template engines. It\n  cannot find the gadgets described in this repository because it cannot\n  statically analyze built-in functionality.\n\n- [GHunter: Universal Prototype Pollution Gadgets in JavaScript Runtimes](https://www.usenix.org/conference/usenixsecurity24/presentation/cornelissen)\n\n  Toolchain to find prototype pollution gadgets in the Node.js runtime. It\n  cannot find the gadgets described in this repository because they're not\n  covered by the binding code.\n\n\u003c!-- 2023 --\u003e\n- [Silent Spring: Prototype Pollution Leads to Remote Code Execution in Node.js](https://www.usenix.org/conference/usenixsecurity23/presentation/shcherbakov)\n\n  Toolchain to find prototype pollution and gadgets in libraries and the Node.js\n  runtime. It cannot find the gadgets described in this repository because it\n  cannot statically analyze built-in functionality.\n\n[fixed spec reference]: ./spec-reference.html\n[spec]: https://tc39.es/ecma262 \"ECMAScript Language Specification\"\n[tc39/test262]: https://github.com/tc39/test262\n[well-known symbol]: https://tc39.es/ecma262/#sec-well-known-symbols \"ECMAScript Well-Known Symbols\"\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fericcornelissen%2Fpp-runtime-gadgets","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fericcornelissen%2Fpp-runtime-gadgets","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fericcornelissen%2Fpp-runtime-gadgets/lists"}