Skip to content

Commit dd42153

Browse files
authored
Merge pull request #153 from keycloakify/better_freeze_error_tracability
Better freeze error traceability
2 parents f93ab29 + ee960b3 commit dd42153

File tree

1 file changed

+162
-47
lines changed

1 file changed

+162
-47
lines changed

src/core/earlyInit_browserRuntimeFreeze.ts

Lines changed: 162 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,20 @@ function freezeBrowserRuntime(params: { excludedApiNames: ApiName[] }) {
5858
);
5959
};
6060

61+
const definePropertyInterceptors: ((o: unknown, p: PropertyKey) => void | never)[] = [];
62+
63+
{
64+
const defineProperty_original = Object.defineProperty;
65+
66+
Object.defineProperty = (o, p, attributes) => {
67+
definePropertyInterceptors.forEach(definePropertyInterceptor => {
68+
definePropertyInterceptor(o, p);
69+
});
70+
71+
return defineProperty_original(o, p, attributes);
72+
};
73+
}
74+
6175
for (const apiName of [
6276
"fetch",
6377
"XMLHttpRequest",
@@ -141,6 +155,12 @@ function freezeBrowserRuntime(params: { excludedApiNames: ApiName[] }) {
141155
})
142156
})
143157
});
158+
159+
definePropertyInterceptors.push((o, p) => {
160+
if (o === prototype && p === propertyName) {
161+
throw createWriteError({ target, apiName });
162+
}
163+
});
144164
}
145165

146166
{
@@ -150,14 +170,27 @@ function freezeBrowserRuntime(params: { excludedApiNames: ApiName[] }) {
150170
}
151171
}
152172

153-
Object.defineProperty(crypto, "subtle", {
154-
configurable: false,
155-
enumerable: true,
156-
get: () => subtle,
157-
set: () => {
158-
throw createWriteError({ target: "window.crypto.subtle", apiName });
159-
}
160-
});
173+
{
174+
const o = crypto;
175+
const p = "subtle";
176+
const createWriteError_local = () =>
177+
createWriteError({ target: "window.crypto.subtle", apiName });
178+
179+
Object.defineProperty(o, p, {
180+
configurable: false,
181+
enumerable: true,
182+
get: () => subtle,
183+
set: () => {
184+
throw createWriteError_local();
185+
}
186+
});
187+
188+
definePropertyInterceptors.push((o_, p_) => {
189+
if (o_ === o && p_ === p) {
190+
throw createWriteError_local();
191+
}
192+
});
193+
}
161194

162195
continue;
163196
}
@@ -167,16 +200,30 @@ function freezeBrowserRuntime(params: { excludedApiNames: ApiName[] }) {
167200
break service_worker;
168201
}
169202

170-
const name_ = "serviceWorker";
203+
const o = window.navigator;
204+
const p = "serviceWorker";
205+
206+
const original = o?.[p];
171207

172-
const original = navigator[name_];
208+
if (!original) {
209+
break service_worker;
210+
}
211+
212+
const createWriteError_local = () =>
213+
createWriteError({ target: `window.navigator.${p}`, apiName });
173214

174-
Object.defineProperty(navigator, name_, {
215+
Object.defineProperty(o, p, {
175216
configurable: false,
176217
enumerable: true,
177218
get: () => original,
178219
set: () => {
179-
throw createWriteError({ target: `window.navigator.${name_}`, apiName });
220+
throw createWriteError_local();
221+
}
222+
});
223+
224+
definePropertyInterceptors.push((o_, p_) => {
225+
if (o_ === o && p_ === p) {
226+
throw createWriteError_local();
180227
}
181228
});
182229

@@ -191,18 +238,14 @@ function freezeBrowserRuntime(params: { excludedApiNames: ApiName[] }) {
191238
const pName = "call";
192239

193240
{
194-
const defineProperty_original = Object.defineProperty;
195-
196-
Object.defineProperty = (o, p, attributes) => {
241+
definePropertyInterceptors.push((o, p) => {
197242
if (typeof o === "function" && o !== Function.prototype && p === pName) {
198243
throw createWriteError({
199244
target: `<some function> 's .${pName}() behavior`,
200245
apiName
201246
});
202247
}
203-
204-
return defineProperty_original(o, p, attributes);
205-
};
248+
});
206249
}
207250

208251
Object.defineProperties = (o, properties) => {
@@ -215,15 +258,27 @@ function freezeBrowserRuntime(params: { excludedApiNames: ApiName[] }) {
215258
{
216259
const original = Function.prototype[pName];
217260

218-
Object.defineProperty(Function.prototype, pName, {
261+
const o = Function.prototype;
262+
const p = pName;
263+
264+
const createWriteError_local = () =>
265+
createWriteError({
266+
target: `window.Function.prototype.${p}`,
267+
apiName
268+
});
269+
270+
Object.defineProperty(o, p, {
219271
configurable: false,
220272
enumerable: true,
221273
get: () => original,
222274
set: () => {
223-
throw createWriteError({
224-
target: `window.Function.prototype.${pName}`,
225-
apiName
226-
});
275+
throw createWriteError_local();
276+
}
277+
});
278+
279+
definePropertyInterceptors.push((o_, p_) => {
280+
if (o === o_ && p === p_) {
281+
throw createWriteError_local();
227282
}
228283
});
229284
}
@@ -263,27 +318,37 @@ function freezeBrowserRuntime(params: { excludedApiNames: ApiName[] }) {
263318
continue;
264319
}
265320

266-
const target = `window.${apiName}.prototype.${propertyName}`;
321+
const o = original.prototype;
322+
const p = propertyName;
267323

268-
Object.defineProperty(original.prototype, propertyName, {
324+
const createWriteError_local = () =>
325+
createWriteError({ target: `window.${apiName}.prototype.${p}`, apiName });
326+
327+
Object.defineProperty(o, p, {
269328
enumerable: pd.enumerable,
270329
configurable: false,
271330
...("value" in pd
272331
? {
273332
get: () => pd.value,
274333
set: () => {
275-
throw createWriteError({ target, apiName });
334+
throw createWriteError_local();
276335
}
277336
}
278337
: {
279338
get: pd.get,
280339
set:
281340
pd.set ??
282341
(() => {
283-
throw createWriteError({ target, apiName });
342+
throw createWriteError_local();
284343
})
285344
})
286345
});
346+
347+
definePropertyInterceptors.push((o_, p_) => {
348+
if (o_ === o && p_ === p) {
349+
throw createWriteError_local();
350+
}
351+
});
287352
}
288353

289354
for (const symbol of Object.getOwnPropertySymbols(original.prototype)) {
@@ -295,27 +360,40 @@ function freezeBrowserRuntime(params: { excludedApiNames: ApiName[] }) {
295360
continue;
296361
}
297362

298-
const target = `window.${apiName}.prototype[Symbol.${symbol.toString()}]`;
363+
const o = original.prototype;
364+
const p = symbol;
365+
366+
const createWriteError_local = () =>
367+
createWriteError({
368+
target: `window.${apiName}.prototype[Symbol.${p.toString()}]`,
369+
apiName
370+
});
299371

300-
Object.defineProperty(original.prototype, symbol, {
372+
Object.defineProperty(o, p, {
301373
enumerable: pd.enumerable,
302374
configurable: false,
303375
...("value" in pd
304376
? {
305377
get: () => pd.value,
306378
set: () => {
307-
throw createWriteError({ target, apiName });
379+
throw createWriteError_local();
308380
}
309381
}
310382
: {
311383
get: pd.get,
312384
set:
313385
pd.set ??
314386
(() => {
315-
throw createWriteError({ target, apiName });
387+
throw createWriteError_local();
316388
})
317389
})
318390
});
391+
392+
definePropertyInterceptors.push((o_, p_) => {
393+
if (o_ === o && p_ === p) {
394+
throw createWriteError_local();
395+
}
396+
});
319397
}
320398

321399
for (const propertyName of Object.getOwnPropertyNames(original)) {
@@ -327,27 +405,37 @@ function freezeBrowserRuntime(params: { excludedApiNames: ApiName[] }) {
327405
continue;
328406
}
329407

330-
const target = `window.${apiName}.${propertyName}`;
408+
const o = original;
409+
const p = propertyName;
331410

332-
Object.defineProperty(original, propertyName, {
411+
const createWriteError_local = () =>
412+
createWriteError({ target: `window.${apiName}.${propertyName}`, apiName });
413+
414+
Object.defineProperty(o, p, {
333415
enumerable: pd.enumerable,
334416
configurable: false,
335417
...("value" in pd
336418
? {
337419
get: () => pd.value,
338420
set: () => {
339-
throw createWriteError({ target, apiName });
421+
throw createWriteError_local();
340422
}
341423
}
342424
: {
343425
get: pd.get,
344426
set:
345427
pd.set ??
346428
(() => {
347-
throw createWriteError({ target, apiName });
429+
throw createWriteError_local();
348430
})
349431
})
350432
});
433+
434+
definePropertyInterceptors.push((o_, p_) => {
435+
if (o_ === o && p_ === p) {
436+
throw createWriteError_local();
437+
}
438+
});
351439
}
352440

353441
if (Symbol.iterator in original.prototype) {
@@ -363,38 +451,65 @@ function freezeBrowserRuntime(params: { excludedApiNames: ApiName[] }) {
363451
continue;
364452
}
365453

366-
const target = `new ${apiName}()[Symbol.iterator]().__proto__.${propertyName}`;
454+
const o = iterator_prototype;
455+
const p = propertyName;
367456

368-
Object.defineProperty(iterator_prototype, propertyName, {
457+
const createWriteError_local = () =>
458+
createWriteError({
459+
target: `new ${apiName}()[Symbol.iterator]().__proto__.${propertyName}`,
460+
apiName
461+
});
462+
463+
Object.defineProperty(o, p, {
369464
enumerable: pd.enumerable,
370465
configurable: false,
371466
...("value" in pd
372467
? {
373468
get: () => pd.value,
374469
set: () => {
375-
throw createWriteError({ target, apiName });
470+
throw createWriteError_local();
376471
}
377472
}
378473
: {
379474
get: pd.get,
380475
set:
381476
pd.set ??
382477
(() => {
383-
throw createWriteError({ target, apiName });
478+
throw createWriteError_local();
384479
})
385480
})
386481
});
482+
483+
definePropertyInterceptors.push((o_, p_) => {
484+
if (o_ === o && p_ === p) {
485+
throw createWriteError_local();
486+
}
487+
});
387488
}
388489
}
389490
}
390491

391-
Object.defineProperty(window, apiName, {
392-
configurable: false,
393-
enumerable: true,
394-
get: () => original,
395-
set: () => {
396-
throw createWriteError({ target: `window.${apiName}`, apiName });
397-
}
398-
});
492+
{
493+
const o = window;
494+
const p = apiName;
495+
496+
const createWriteError_local = () =>
497+
createWriteError({ target: `window.${apiName}`, apiName });
498+
499+
Object.defineProperty(o, p, {
500+
configurable: false,
501+
enumerable: true,
502+
get: () => original,
503+
set: () => {
504+
throw createWriteError_local();
505+
}
506+
});
507+
508+
definePropertyInterceptors.push((o_, p_) => {
509+
if (o_ === o && p_ === p) {
510+
throw createWriteError_local();
511+
}
512+
});
513+
}
399514
}
400515
}

0 commit comments

Comments
 (0)