-
Notifications
You must be signed in to change notification settings - Fork 676
Description
Sequence of actions:
- Install this script (In non-firefox browser)
// ==UserScript==
// @name New script
// @namespace Violentmonkey Scripts
// @match *://*/*
// @version 1.0
// @author -
// @grant GM_getValue
// @description Voilentmonkey bug
// ==/UserScript==
window.setInterval;
// or
//window.setTimeout;
console.log(Reflect.ownKeys(window));- open any page (such as Google)
- check devtools for error
Problem:
A TypeError: 'ownKeys' on proxy: trap returned duplicate entries is thrown when calling Reflect.ownKeys(window) or Object.getOwnPropertyNames(window) if standard global functions like setInterval or setTimeout have been accessed previously.
Expected result:
Reflect.ownKeys(window) should return a unique list of property names without duplicates. The proxy trap should ensure that "materialized" built-in functions do not collide with the static global keys snapshot.
Devtools console contents:
New script.user.js:14
Uncaught TypeError: 'ownKeys' on proxy: trap returned duplicate entries
at Reflect.ownKeys (<anonymous>)
at New script.user.js:14:21
at Proxy.VMhoyus2mzbo6 (New script.user.js:15:3)
at ln (injected-web.js:1:16297)
at New script.user.js:1:21
at async en (injected.js:1:9149)
at async injected.js:1:15507
(anonymous) @ New script.user.js:14
VMhoyus2mzbo6 @ New script.user.js:15
ln @ injected-web.js:1
(anonymous) @ New script.user.js:1
Technical Analysis:
This bug occurs in compiled injected-web.js.
In source code, it occurs in makeOwnKeys within gm-global-wrapper.js due to a synchronization gap between the static global snapshot and materialized properties:
- In
makeGlobalKeys, whenok === true,globalKeysreturns the fullnamesarray frombuiltinGlobals[0], which includessetIntervalandsetTimeout. - However, these two keys are explicitly skipped during the initialization of
globalKeysSetbecause they exist inbuiltinFuncs. As a result,globalKeysSet.get('setInterval')returnsundefined. - Once a script accesses
window.setIntervalorwindow.setTimeout,proxyDescribe"materializes" the property by callingdefineProperty(local, name, desc). Now, these keys exist on thelocalobject. - When
Reflect.ownKeys(window)triggersmakeOwnKeys, it concatenates:frameIndexesglobalKeys(Static snapshot: already containssetInterval)reflectOwnKeys(local)filtered bynotIncludedIn(globals.get)
- Because
globals.get('setInterval')isundefined(skipped in step 2), thenotIncludedInfilter returnstruefor the materialized key inlocal. Consequently,setIntervalis included from bothglobalKeysand the filteredlocalkeys, leading to duplicate entries.
Suggested Fix:
Use a Set to deduplicate the property array before returning it in makeOwnKeys to prevent collisions between the static snapshot and materialized properties.
function makeOwnKeys(local, globals) {
// ... existing logic ...
const keys = safeConcat(
frameIndexes,
globals === globalKeysSet ? globalKeys : globals.toArray(),
reflectOwnKeys(local)::filter(notIncludedIn, globals.get),
);
return [...new Set(keys)]; // Deduplicate before returning to the Proxy trap
}Environment:
- OS: Windows
- Browser: Edge 144.0.3719.82 (64)
- Violentmonkey Version: v2.32.0
Related: #2392