You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Console breadcrumb serialization invokes getters & DOM traversal on logged objects (regression in 10.55.0: safeJoin now uses stringifyValue instead of String) #21353
I have searched the existing issues (no match for safeJoin / stringifyValue / console-breadcrumb getter side effects)
I have reviewed the documentation
I am using the latest SDK release (reproduces on 10.56.0)
How do you use Sentry?
Sentry SaaS (sentry.io)
Which SDK are you using?
@sentry/browser (root cause is in @sentry/core's safeJoin, surfaced via the Breadcrumbs integration; observed through @sentry/nuxt)
SDK Version
10.56.0 (regression introduced in 10.55.0; last unaffected version: 10.54.0)
Framework Version
Nuxt 4 / Vue 3.5
Reproduction Example/SDK Setup
import*asSentryfrom'@sentry/browser'// Default integrations include Breadcrumbs with console capturing enabledSentry.init({dsn: '__YOUR_DSN__'})constel=document.createElement('div')letgetterInvoked=falseObject.defineProperty(el,'id',{get(){getterInvoked=true// any side effect here now runs during console breadcrumb capturereturn''},})console.log(el)// Sentry's console breadcrumb handler serializes the args herequeueMicrotask(()=>console.warn('getter invoked by Sentry serialization?',getterInvoked))// @sentry/core <= 10.54.0 -> false// @sentry/core >= 10.55.0 -> true (incl. 10.56.0)
Steps to Reproduce
Initialize any browser SDK with default integrations (Breadcrumbs console: true).
console.log() an object that has a property getter (e.g. a DOM element, or any object with Object.defineProperty(..., { get })).
Observe that the getter is executed synchronously as part of breadcrumb creation — even when DevTools is closed and no event is sent.
Expected Result
Capturing a console breadcrumb should be side-effect free: serializing the logged arguments for the breadcrumb message must not invoke user-defined property getters, nor traverse the DOM. (<= 10.54.0 behaved this way — non-primitive args were serialized with String(value).)
Actual Result
Getters are invoked and DOM elements are walked during breadcrumb serialization.
Root cause — @sentry/coresafeJoin (packages/core/src/utils/string.ts), which the Breadcrumbs integration uses for the console breadcrumb message (message: safeJoin(handlerData.args, ' ') in _getConsoleBreadcrumbHandler):
For DOM elements, stringifyValue resolves to htmlTreeAsString → _htmlElementAsString (packages/core/src/utils/browser.ts), which reads elem.id / elem.className / elem.getAttribute(...) and walks the ancestor chain — executing getters and adding DOM-traversal cost to every console.log(element).
Additional Context
Real-world impact (how we found it): an anti-DevTools library uses the classic trap Object.defineProperty(div, 'id', { get() { /* assume DevTools open */ } }) followed by console.log(div), expecting the getter to fire only when DevTools renders the object. After upgrading @sentry/nuxt10.53.1 → 10.55.0, Sentry's breadcrumb serialization began invoking that id getter on every page, causing a false "DevTools open" detection (and a forced redirect) with DevTools closed. We confirmed via a pristine (un-instrumented) iframe console.log that the getter is read only through the main-window Sentry-instrumented console.
Precedent that this class of bug is known/dangerous:safeJoin already special-cases isVueViewModel to avoid serialization side effects (the infinite console-warning loop fixed in fix(utils): Prevent iterating over VueViewModel #8981). Invoking arbitrary getters / DOM traversal is the same category of risk (side effects, performance, potential exceptions).
Affected range:10.55.0 … 10.56.0 (latest). 10.54.0 and earlier are unaffected.
Suggestion: keep breadcrumb-message serialization side-effect-free for non-primitive args (e.g. retain String(value) for the human-readable message, since the structured data.arguments already carries the raw args for later normalization), or guard stringifyValue/htmlTreeAsString against invoking getters during breadcrumb capture.
Is there an existing issue for this?
safeJoin/stringifyValue/ console-breadcrumb getter side effects)10.56.0)How do you use Sentry?
Sentry SaaS (sentry.io)
Which SDK are you using?
@sentry/browser(root cause is in@sentry/core'ssafeJoin, surfaced via the Breadcrumbs integration; observed through@sentry/nuxt)SDK Version
10.56.0 (regression introduced in 10.55.0; last unaffected version: 10.54.0)
Framework Version
Nuxt 4 / Vue 3.5
Reproduction Example/SDK Setup
Steps to Reproduce
console: true).console.log()an object that has a property getter (e.g. a DOM element, or any object withObject.defineProperty(..., { get })).Expected Result
Capturing a console breadcrumb should be side-effect free: serializing the logged arguments for the breadcrumb
messagemust not invoke user-defined property getters, nor traverse the DOM. (<= 10.54.0behaved this way — non-primitive args were serialized withString(value).)Actual Result
Getters are invoked and DOM elements are walked during breadcrumb serialization.
Root cause —
@sentry/coresafeJoin(packages/core/src/utils/string.ts), which the Breadcrumbs integration uses for the console breadcrumbmessage(message: safeJoin(handlerData.args, ' ')in_getConsoleBreadcrumbHandler):For DOM elements,
stringifyValueresolves tohtmlTreeAsString→_htmlElementAsString(packages/core/src/utils/browser.ts), which readselem.id/elem.className/elem.getAttribute(...)and walks the ancestor chain — executing getters and adding DOM-traversal cost to everyconsole.log(element).Additional Context
Object.defineProperty(div, 'id', { get() { /* assume DevTools open */ } })followed byconsole.log(div), expecting the getter to fire only when DevTools renders the object. After upgrading@sentry/nuxt10.53.1 → 10.55.0, Sentry's breadcrumb serialization began invoking thatidgetter on every page, causing a false "DevTools open" detection (and a forced redirect) with DevTools closed. We confirmed via a pristine (un-instrumented) iframeconsole.logthat the getter is read only through the main-window Sentry-instrumented console.safeJoinalready special-casesisVueViewModelto avoid serialization side effects (the infinite console-warning loop fixed in fix(utils): Prevent iterating over VueViewModel #8981). Invoking arbitrary getters / DOM traversal is the same category of risk (side effects, performance, potential exceptions).10.55.0…10.56.0(latest).10.54.0and earlier are unaffected.String(value)for the human-readablemessage, since the structureddata.argumentsalready carries the raw args for later normalization), or guardstringifyValue/htmlTreeAsStringagainst invoking getters during breadcrumb capture.Priority
React with 👍 to help prioritize this issue.