diff --git a/CHANGELOG.md b/CHANGELOG.md index fff7847dd506..3e38190139d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,10 @@ When Next.js serves a page from its cache, for example an ISR page or a `cacheComponents` shell, the pageload now has the `page.server_rendering.type` attribute. It is `static` if the whole page came from the cache, and `partially_static` if parts of it rendered for this request. Then the pageload stays connected to this request, as before. If the request that rendered the cached page was sampled, the pageload also links to it. So in Sentry, a cached page no longer looks like missing server instrumentation. +### Other Changes + +- fix(core): Read `__sentry_original__` as an own property in `getOriginalFunction` ([#25964](https://github.com/getsentry/sentry-javascript/pull/25964)) + Work in this release was contributed by @lazerg and @chiliec. Thank you for your contributions! ## 11.10.0 diff --git a/packages/core/src/utils/object.ts b/packages/core/src/utils/object.ts index 46f40c2cd3c7..415f4bedc1fb 100644 --- a/packages/core/src/utils/object.ts +++ b/packages/core/src/utils/object.ts @@ -120,7 +120,7 @@ export function wrapMethod( */ // eslint-disable-next-line @typescript-eslint/ban-types export function getOriginalFunction(func: WrappedFunction): T | undefined { - return func.__sentry_original__; + return Object.prototype.hasOwnProperty.call(func, '__sentry_original__') ? func.__sentry_original__ : undefined; } /** diff --git a/packages/core/test/lib/integrations/functiontostring.test.ts b/packages/core/test/lib/integrations/functiontostring.test.ts index e42c83a77611..c47ce362f692 100644 --- a/packages/core/test/lib/integrations/functiontostring.test.ts +++ b/packages/core/test/lib/integrations/functiontostring.test.ts @@ -120,6 +120,18 @@ describe('FunctionToString', () => { expect(Reflect.apply(Function.prototype.toString, inspectedFunction, new Array(100_000))).toBe(expected); }); + it('does not overflow the stack when the prototype chain loops through a Proxy', () => { + function inspectedFunction(): void {} + Object.setPrototypeOf(inspectedFunction, Object.create(new Proxy(inspectedFunction, {}))); + const callToString = Function.prototype.call.bind(Function.prototype.toString) as (thisArg: unknown) => string; + const expected = callToString(inspectedFunction); + + const fts = functionToStringIntegration(); + getClient()?.addIntegration(fts); + + expect(callToString(inspectedFunction)).toBe(expected); + }); + it('falls back to native toString and does not throw when the carrier read throws', () => { const foo = { bar(wat: boolean): boolean {