🐛 Sanitize and validate name in startView/setViewName - #4969
🐛 Sanitize and validate name in startView/setViewName#4969carlosdelest wants to merge 2 commits into
Conversation
Every other name-bearing public RUM API sanitizes its name argument before it reaches an event, but startView and setViewName never did. A caller passing a non-string name (e.g. an object) landed unchanged in the view event, producing a malformed custom.view.name that broke downstream consumers. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
All contributors have signed the CLA ✍️ ✅ |
|
I have read the CLA Document and I hereby sign the CLA |
| strategy.startView({ | ||
| ...sanitizedOptions, | ||
| ...('name' in sanitizedOptions && { name: sanitizeStringOption(sanitizedOptions.name, 'view name') }), | ||
| ...(sanitizedOptions.service !== undefined && { | ||
| service: sanitizeStringOption(sanitizedOptions.service, 'view service'), | ||
| }), | ||
| ...(sanitizedOptions.version !== undefined && { | ||
| version: sanitizeStringOption(sanitizedOptions.version, 'view version'), | ||
| }), |
There was a problem hiding this comment.
suggestion: Can we keep things simple, like:
strategy.startView({
...sanitizedOptions,
name: sanitizeStringOption(sanitizedOptions.name, '...'),
service: sanitizeStringOption(sanitizedOptions.service, '...'),
version: sanitizeStringOption(sanitizedOptions.version, '...'),
handlingStack,
})There was a problem hiding this comment.
Also, this might be a minor breaking change. Before, this kind of worked:
DD_RUM.startView({ version: 42 })Maybe it's fine though.
There was a problem hiding this comment.
Good call — simplified to the direct form you suggested (pushed in 8376d49). It does mean service/version now always show up as explicit keys (possibly undefined) in the object passed to strategy.startView, same as name already did before this PR — consistent with how every other sanitized field in this file is handled.
this might be a minor breaking change
Yep. DD_RUM.startView({ version: 42 }) will now have version dropped (with a console warning) instead of silently flowing a non-string into the event.
Given the bug report was exactly this kind of type mismatch reaching the intake schema, I think it's the right tradeoff, but happy to change if you'd rather prefer for this stay lenient for service/version specifically (as opposed to name).
|
|
||
| let sessionIsActive = true | ||
| let name = viewOptions?.name | ||
| let name = sanitizeViewName(viewOptions?.name) |
There was a problem hiding this comment.
question: Why do we sanitize the name twice?
There was a problem hiding this comment.
Ouch — turns out we don't need to. Every call path (rumPublicApi.startView/setViewName, all the framework integrations, and the pre-start buffering in preStartRum.ts) already goes through rumPublicApi's sanitization before viewOptions/updatedName ever reaches trackViews.ts.
Removed the redundant guard there in 8376d49 — single sanitization point at the public API boundary.
Address review feedback on #4969: keep the startView option spread simple (matching the pattern used elsewhere in this file) instead of conditionally spreading each field, and remove the sanitizeViewName guard in trackViews.ts since every call path already goes through rumPublicApi's sanitization before reaching the strategy. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Bundles Sizes Evolution
|
🎉 All green!🧪 All tests passed 🎯 Code Coverage (details) 🔗 Commit SHA: 8376d49 | Docs | View more details | Give us feedback! |
Motivation
RUM view events were landing with
custom.view.nameas a nested object (e.g.{"name": "test"}) instead of a string, breaking downstream consumers that expectview.nameto be a string.Every other name-bearing public RUM API (
addAction,startAction,addTiming,addDurationVital,addFeatureFlagEvaluation, ...) sanitizes itsnameargument before it reaches an event.startViewandsetViewNamewere the exceptions — nothing validated or coerced the type ofname, so a caller passing a non-string value (e.g.startView({ name: { name: 'test' } })) flowed unchanged into the emitted event.Changes
packages/browser-rum-core/src/boot/rumPublicApi.ts: added asanitizeStringOption()helper. Non-string values (excludingundefined) are dropped with adisplay.warn(); valid strings are passed throughsanitize(). Wired intostartView(name,service,version) andsetViewName.packages/browser-rum-core/src/domain/view/trackViews.ts: added a matchingsanitizeViewName()guard, applied wherenameis assigned innewView()andsetViewName(), as defense-in-depth in case an internal caller bypasses the public API.nameinputs for bothstartViewandsetViewName, confirmingview.namebecomesundefined(never an object) and a warning is logged, and that existing valid-string behavior is unchanged.Note that
sanitize()alone does not fix this: it deep-clones objects into JSON-safe form but does not coerce them to strings, so an explicit runtime type guard was required.Test instructions
yarn test:unit --spec packages/browser-rum-core/src/boot/rumPublicApi.spec.tsyarn test:unit --spec packages/browser-rum-core/src/domain/view/trackViews.spec.tsDD_RUM.startView({ name: { name: 'test' } })and confirm the resulting view event has noview.nameand a console warning appears.Checklist