Skip to content

Commit 21db867

Browse files
committed
fix: use fixed Map/WeakMap intrinsics via Reflect.apply to prevent recursion
Prevent infinite recursion in monkey-patched `Map`/`WeakMap` methods by using fixed native method references captured at module load time, ensuring safe access to internal caches without triggering patch handlers. Safer controller state access (Patches and WindowStates maps)
1 parent 4afcf41 commit 21db867

1 file changed

Lines changed: 30 additions & 10 deletions

File tree

libs/source/monkey-patches/index.ts

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,26 @@ const TinyShieldPatchIdSet: ReadonlySet<string> = new Set(TinyShieldPatchIds)
8787
const WindowStates = new WeakMap<TinyShieldWindow, TinyShieldState>()
8888
const ProtectedFunctionStrings: ReadonlySet<string> = new Set(['toString', 'get', 'set'])
8989
const FunctionNameProperty = 'name'
90+
const OriginalMapGet = Map.prototype.get as (Key: unknown) => unknown
91+
const OriginalMapSet = Map.prototype.set as (Key: unknown, Value: unknown) => Map<unknown, unknown>
92+
const OriginalWeakMapGet = WeakMap.prototype.get as (Key: object) => unknown
93+
const OriginalWeakMapSet = WeakMap.prototype.set as (Key: object, Value: unknown) => WeakMap<object, unknown>
94+
95+
function ApplyMapGet<K, V>(Target: Map<K, V>, Key: K): V | undefined {
96+
return Reflect.apply(OriginalMapGet, Target as unknown as Map<unknown, unknown>, [Key]) as V | undefined
97+
}
98+
99+
function ApplyMapSet<K, V>(Target: Map<K, V>, Key: K, Value: V): Map<K, V> {
100+
return Reflect.apply(OriginalMapSet, Target as unknown as Map<unknown, unknown>, [Key, Value]) as Map<K, V>
101+
}
102+
103+
function ApplyWeakMapGet<K extends object, V>(Target: WeakMap<K, V>, Key: K): V | undefined {
104+
return Reflect.apply(OriginalWeakMapGet, Target as unknown as WeakMap<object, unknown>, [Key]) as V | undefined
105+
}
106+
107+
function ApplyWeakMapSet<K extends object, V>(Target: WeakMap<K, V>, Key: K, Value: V): WeakMap<K, V> {
108+
return Reflect.apply(OriginalWeakMapSet, Target as unknown as WeakMap<object, unknown>, [Key, Value]) as WeakMap<K, V>
109+
}
90110

91111
const ASInitPositiveRegExps: RegExp[][] = [[
92112
/[a-zA-Z0-9]+ *=> *{ *const *[a-zA-Z0-9]+ *= *[a-zA-Z0-9]+ *; *if/,
@@ -146,7 +166,7 @@ class TinyShieldControllerImpl implements TinyShieldController {
146166
}
147167

148168
GetPatch(PatchId: TinyShieldPatchId): TinyShieldPatchController {
149-
const Patch = this.State.Patches.get(PatchId)
169+
const Patch = ApplyMapGet(this.State.Patches, PatchId)
150170

151171
if (typeof Patch === 'undefined') {
152172
throw new Error(`Unknown tinyShield patch id: ${PatchId}`)
@@ -181,7 +201,7 @@ function ResolveBrowserWindow(WindowOption?: TinyShieldWindow): TinyShieldWindow
181201

182202
function GetTinyShieldState(Options: TinyShieldControllerOptions): TinyShieldState {
183203
const BrowserWindow = ResolveBrowserWindow(Options.Window)
184-
const ExistingState = WindowStates.get(BrowserWindow)
204+
const ExistingState = ApplyWeakMapGet(WindowStates, BrowserWindow)
185205

186206
if (typeof ExistingState !== 'undefined') {
187207
if (typeof Options.UserscriptName !== 'undefined') {
@@ -202,14 +222,14 @@ function GetTinyShieldState(Options: TinyShieldControllerOptions): TinyShieldSta
202222
Patches: new Map()
203223
}
204224

205-
State.Patches.set('FunctionToString', CreatePatchState('FunctionToString', () => InstallFunctionToStringPatch(State)))
206-
State.Patches.set('MapGet', CreatePatchState('MapGet', () => InstallMapGetPatch(State)))
207-
State.Patches.set('MapSet', CreatePatchState('MapSet', () => InstallMapSetPatch(State)))
208-
State.Patches.set('WeakMapSet', CreatePatchState('WeakMapSet', () => InstallWeakMapSetPatch(State)))
209-
State.Patches.set('SetTimeout', CreatePatchState('SetTimeout', () => InstallSetTimeoutPatch(State)))
210-
State.Patches.set('SetInterval', CreatePatchState('SetInterval', () => InstallSetIntervalPatch(State)))
225+
ApplyMapSet(State.Patches, 'FunctionToString', CreatePatchState('FunctionToString', () => InstallFunctionToStringPatch(State)))
226+
ApplyMapSet(State.Patches, 'MapGet', CreatePatchState('MapGet', () => InstallMapGetPatch(State)))
227+
ApplyMapSet(State.Patches, 'MapSet', CreatePatchState('MapSet', () => InstallMapSetPatch(State)))
228+
ApplyMapSet(State.Patches, 'WeakMapSet', CreatePatchState('WeakMapSet', () => InstallWeakMapSetPatch(State)))
229+
ApplyMapSet(State.Patches, 'SetTimeout', CreatePatchState('SetTimeout', () => InstallSetTimeoutPatch(State)))
230+
ApplyMapSet(State.Patches, 'SetInterval', CreatePatchState('SetInterval', () => InstallSetIntervalPatch(State)))
211231

212-
WindowStates.set(BrowserWindow, State)
232+
ApplyWeakMapSet(WindowStates, BrowserWindow, State)
213233
return State
214234
}
215235

@@ -251,7 +271,7 @@ function NormalizePatchIds(PatchIds: Iterable<TinyShieldPatchId>): TinyShieldPat
251271
}
252272

253273
function IsPatchEnabled(State: TinyShieldState, PatchId: TinyShieldPatchId): boolean {
254-
return State.Patches.get(PatchId)?.IsEnabled() === true
274+
return ApplyMapGet(State.Patches, PatchId)?.IsEnabled() === true
255275
}
256276

257277
function InstallFunctionToStringPatch(State: TinyShieldState): void {

0 commit comments

Comments
 (0)