Repository navigation
Description
Is there an existing issue for this?
- I have checked for existing issues
- I have reviewed the documentation
- I am using the latest SDK release or the bug is reproducible on master
How do you use Sentry?
Sentry Saas (sentry.io)
Which SDK are you using?
@sentry/nuxt (the affected code is in @sentry/core, so all browser SDKs are affected)
SDK Version
10.34.0 — the affected code is unchanged on current master (packages/core/src/integrations/functiontostring.ts)
Framework Version
Nuxt 4 (not framework-specific)
Description
The functionToStringIntegration (enabled by default) patches Function.prototype.toString. The patched function calls getClient() → getCurrentScope() → getMainCarrier() → getSentryCarrier(GLOBAL_OBJ) on every .toString() invocation, which performs a property read of GLOBAL_OBJ.__SENTRY__.
GLOBAL_OBJ is globalThis, which in a Window realm is the WindowProxy of the realm's browsing context. If that browsing context is later navigated to a cross-origin document while code from the old realm can still be invoked (e.g. a still-alive parent window holding references into a same-origin child iframe whose src changed to a cross-origin URL), the __SENTRY__ read on the WindowProxy throws:
SecurityError: Failed to read a named property '__SENTRY__' from 'Window':
Blocked a frame with origin "https://app.example.com" from accessing a cross-origin frame.
(current Chrome wording: Blocked a restricted frame with origin "…" from accessing another frame.)
Because the patch is installed on Function.prototype, the throw is triggered by any third-party script calling .toString(). In our production case the chain was: Microsoft Clarity's MutationObserver serialization → Tawk.to's core-js Function.prototype.toString wrapper → Facebook Pixel's (fbevents.js) core-js inspectSource → Sentry's patched toString → SecurityError, surfacing as onunhandledrejection noise (24 events within ~1 second from a single page transition on a page embedding a third-party game iframe with sandbox="... allow-top-navigation").
Production stack (origins redacted, resolved via source maps):
/0.8.66/clarity.js (async/await machinery, MutationObserver serialization)
/_s/v4/app/.../twk-chunk-vendors.js (ShadowRoot.toString — core-js toString wrapper)
/en_US/fbevents.js (core-js inspectSource)
@sentry/core/build/esm/integrations/functiontostring.js:24 (Function.prototype.toString)
@sentry/core/build/esm/currentScopes.js:101 (getClient)
@sentry/core/build/esm/currentScopes.js:10 (getCurrentScope)
@sentry/core/build/esm/carrier.js:18 (getMainCarrier → getSentryCarrier(GLOBAL_OBJ) → __SENTRY__ read throws)
Note: the native Function.prototype.toString never throws for these calls — the throw is introduced solely by the integration's internal carrier access, so the SDK converts harmless third-party introspection into uncatchable (for the app) SecurityError noise, which Sentry itself then reports.
Steps to Reproduce
Self-contained repro (Chrome). Serve the two files below from http://127.0.0.1:8801 and also expose the same directory on http://127.0.0.1:8802 (different port = cross-origin but same-site, any static server works):
parent.html:
<!doctype html>
<iframe id="f" src="/child.html"></iframe>
<script>
const f = document.getElementById('f')
let done = false
f.onload = () => {
if (done) return
done = true
const childFn = f.contentWindow.exposedFn // keep a reference into the child realm
setTimeout(() => { f.src = 'http://127.0.0.1:8802/parent.html' }, 300) // navigate iframe cross-origin
setTimeout(() => {
try {
childFn.toString() // resolves to the child realm's patched Function.prototype.toString
console.log('ok')
} catch (e) {
console.log('THREW', e.name, e.message)
}
}, 1500)
}
</script>child.html (stands in for any same-origin frame that initialized a Sentry browser SDK — inlined replica of functiontostring.ts + carrier.ts so the repro needs no bundler; using the real SDK behaves identically):
<!doctype html>
<script>
const SDK_VERSION = '10.34.0'
const GLOBAL_OBJ = globalThis
function getSentryCarrier(carrier) {
const __SENTRY__ = (carrier.__SENTRY__ = carrier.__SENTRY__ || {})
__SENTRY__.version = __SENTRY__.version || SDK_VERSION
return (__SENTRY__[SDK_VERSION] = __SENTRY__[SDK_VERSION] || {})
}
function getMainCarrier() { getSentryCarrier(GLOBAL_OBJ); return GLOBAL_OBJ }
function getClient() { getMainCarrier(); return undefined }
const SETUP_CLIENTS = new WeakMap()
const originalFunctionToString = Function.prototype.toString
Function.prototype.toString = function (...args) {
const originalFunction = this && this.__sentry_original__
const context = SETUP_CLIENTS.has(getClient()) && originalFunction !== undefined ? originalFunction : this
return originalFunctionToString.apply(context, args)
}
window.exposedFn = function exposedFn() {}
</script>Observed output:
THREW SecurityError Failed to read a named property '__SENTRY__' from 'Window': Blocked a restricted frame with origin "http://127.0.0.1:8801" from accessing another frame.
The same read also throws in any other "realm outlives its document's origin" situation (Android Chrome same-process cross-origin navigations of the top frame show the same signature).
Expected Result
The patched Function.prototype.toString should never throw where the native implementation would not. Suggested fix — wrap the patch body, mirroring the defensive style already used around the patch installation:
Function.prototype.toString = function (this: WrappedFunction, ...args: any[]): string {
try {
const originalFunction = getOriginalFunction(this);
const context =
SETUP_CLIENTS.has(getClient() as Client) && originalFunction !== undefined ? originalFunction : this;
return originalFunctionToString.apply(context, args);
} catch {
// e.g. SecurityError reading __SENTRY__ off a WindowProxy whose browsing context
// was navigated cross-origin — fall back to native behavior
return originalFunctionToString.apply(this, args);
}
};We've verified (against @sentry/core 10.34.0) that this keeps the unwrap behavior intact for live realms and degrades to exact native semantics otherwise. Happy to open a PR if this direction is acceptable.
Actual Result
SecurityError: Failed to read a named property '__SENTRY__' from 'Window': Blocked a frame with origin "…" from accessing a cross-origin frame. thrown out of Function.prototype.toString, reported as unhandled-rejection error events.
Metadata
Metadata
Assignees
Projects
- StatusShow more project fieldsNo status
Activity
linear-code commentedon Jul 5, 2026
JS-2926
JPeer264 commentedon Jul 6, 2026
@JT-Well thanks for the issue. Your suggested fix is actually valid, if you want you can open up a PR with that fix.
fix(core): Prevent functionToStringIntegration from throwing on cross…
fix(core): Prevent functionToStringIntegration from throwing on cross…
github-actions commentedon Jul 20, 2026
A PR closing this issue has just been released 🚀
This issue was referenced by PR #22273, which was included in the 10.67.0 release.
fix(core): Avoid `functionToStringIntegration` causing infinite recur…