Socket
Socket
Sign inDemoInstall

@datadog/browser-rum-core

Package Overview
Dependencies
Maintainers
1
Versions
177
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@datadog/browser-rum-core - npm Package Compare versions

Comparing version 4.41.0 to 4.42.0

52

cjs/domain/assembly.js

@@ -9,22 +9,32 @@ "use strict";

var limitModification_1 = require("./limitModification");
var VIEW_EVENTS_MODIFIABLE_FIELD_PATHS = [
// Fields with sensitive data
'view.url',
'view.referrer',
'action.target.name',
'error.message',
'error.stack',
'error.resource.url',
'resource.url',
];
var OTHER_EVENTS_MODIFIABLE_FIELD_PATHS = VIEW_EVENTS_MODIFIABLE_FIELD_PATHS.concat([
// User-customizable field
'context',
]);
var VIEW_MODIFIABLE_FIELD_PATHS = {
'view.url': 'string',
'view.referrer': 'string',
};
var USER_CUSTOMIZABLE_FIELD_PATHS = {
context: 'object',
};
var modifiableFieldPathsByEvent;
function startRumAssembly(configuration, lifeCycle, sessionManager, viewContexts, urlContexts, actionContexts, buildCommonContext, reportError) {
var _a;
var eventRateLimiters = (_a = {},
_a["error" /* RumEventType.ERROR */] = (0, browser_core_1.createEventRateLimiter)("error" /* RumEventType.ERROR */, configuration.eventRateLimiterThreshold, reportError),
_a["action" /* RumEventType.ACTION */] = (0, browser_core_1.createEventRateLimiter)("action" /* RumEventType.ACTION */, configuration.eventRateLimiterThreshold, reportError),
var _a, _b;
modifiableFieldPathsByEvent = (_a = {},
_a["view" /* RumEventType.VIEW */] = VIEW_MODIFIABLE_FIELD_PATHS,
_a["error" /* RumEventType.ERROR */] = (0, browser_core_1.assign)({
'error.message': 'string',
'error.stack': 'string',
'error.resource.url': 'string',
'error.fingerprint': 'string',
}, USER_CUSTOMIZABLE_FIELD_PATHS, VIEW_MODIFIABLE_FIELD_PATHS),
_a["resource" /* RumEventType.RESOURCE */] = (0, browser_core_1.assign)({
'resource.url': 'string',
}, USER_CUSTOMIZABLE_FIELD_PATHS, VIEW_MODIFIABLE_FIELD_PATHS),
_a["action" /* RumEventType.ACTION */] = (0, browser_core_1.assign)({
'action.target.name': 'string',
}, USER_CUSTOMIZABLE_FIELD_PATHS, VIEW_MODIFIABLE_FIELD_PATHS),
_a["long_task" /* RumEventType.LONG_TASK */] = (0, browser_core_1.assign)({}, USER_CUSTOMIZABLE_FIELD_PATHS, VIEW_MODIFIABLE_FIELD_PATHS),
_a);
var eventRateLimiters = (_b = {},
_b["error" /* RumEventType.ERROR */] = (0, browser_core_1.createEventRateLimiter)("error" /* RumEventType.ERROR */, configuration.eventRateLimiterThreshold, reportError),
_b["action" /* RumEventType.ACTION */] = (0, browser_core_1.createEventRateLimiter)("action" /* RumEventType.ACTION */, configuration.eventRateLimiterThreshold, reportError),
_b);
var syntheticsContext = (0, syntheticsContext_1.getSyntheticsContext)();

@@ -47,3 +57,3 @@ var ciTestContext = (0, ciTestContext_1.getCiTestContext)();

},
browser_sdk_version: (0, browser_core_1.canUseEventBridge)() ? "4.41.0" : undefined,
browser_sdk_version: (0, browser_core_1.canUseEventBridge)() ? "4.42.0" : undefined,
},

@@ -95,3 +105,5 @@ application: {

if (beforeSend) {
var result = (0, limitModification_1.limitModification)(event, event.type === "view" /* RumEventType.VIEW */ ? VIEW_EVENTS_MODIFIABLE_FIELD_PATHS : OTHER_EVENTS_MODIFIABLE_FIELD_PATHS, function (event) { return beforeSend(event, domainContext); });
var result = (0, limitModification_1.limitModification)(event, modifiableFieldPathsByEvent[event.type], function (event) {
return beforeSend(event, domainContext);
});
if (result === false && event.type !== "view" /* RumEventType.VIEW */) {

@@ -98,0 +110,0 @@ return false;

import type { Context } from '@datadog/browser-core';
export type ModifiableFieldPaths = Record<string, 'string' | 'object'>;
/**

@@ -6,2 +7,2 @@ * Current limitation:

*/
export declare function limitModification<T extends Context, Result>(object: T, modifiableFieldPaths: string[], modifier: (object: T) => Result): Result | undefined;
export declare function limitModification<T extends Context, Result>(object: T, modifiableFieldPaths: ModifiableFieldPaths, modifier: (object: T) => Result): Result | undefined;

@@ -12,12 +12,11 @@ "use strict";

var result = modifier(clone);
modifiableFieldPaths.forEach(function (path) {
var originalValue = get(object, path);
var newValue = get(clone, path);
var originalType = (0, browser_core_1.getType)(originalValue);
(0, browser_core_1.objectEntries)(modifiableFieldPaths).forEach(function (_a) {
var fieldPath = _a[0], fieldType = _a[1];
var newValue = get(clone, fieldPath);
var newType = (0, browser_core_1.getType)(newValue);
if (newType === originalType) {
set(object, path, (0, browser_core_1.sanitize)(newValue));
if (newType === fieldType) {
set(object, fieldPath, (0, browser_core_1.sanitize)(newValue));
}
else if (originalType === 'object' && (newType === 'undefined' || newType === 'null')) {
set(object, path, {});
else if (fieldType === 'object' && (newType === 'undefined' || newType === 'null')) {
set(object, fieldPath, {});
}

@@ -44,3 +43,3 @@ });

var field = fields[i];
if (!isValidObjectContaining(current, field)) {
if (!isValidObject(current)) {
return;

@@ -56,5 +55,8 @@ }

}
function isValidObject(object) {
return (0, browser_core_1.getType)(object) === 'object';
}
function isValidObjectContaining(object, field) {
return typeof object === 'object' && object !== null && Object.prototype.hasOwnProperty.call(object, field);
return isValidObject(object) && Object.prototype.hasOwnProperty.call(object, field);
}
//# sourceMappingURL=limitModification.js.map

@@ -59,2 +59,3 @@ "use strict";

source_type: 'browser',
fingerprint: error.fingerprint,
},

@@ -61,0 +62,0 @@ type: "error" /* RumEventType.ERROR */,

@@ -11,2 +11,3 @@ "use strict";

stack: consoleError.stack,
fingerprint: consoleError.fingerprint,
source: browser_core_1.ErrorSource.CONSOLE,

@@ -13,0 +14,0 @@ handling: "handled" /* ErrorHandling.HANDLED */,

@@ -49,2 +49,3 @@ import type { Context, Duration, ErrorSource, ErrorHandling, ResourceType, ServerDuration, TimeStamp, RawErrorCause } from '@datadog/browser-core';

handling_stack?: string;
fingerprint?: string;
source: ErrorSource;

@@ -51,0 +52,0 @@ message: string;

@@ -205,2 +205,6 @@ /**

/**
* Fingerprint used for Error Tracking custom grouping
*/
fingerprint?: string;
/**
* The type of the error

@@ -207,0 +211,0 @@ */

@@ -1,2 +0,2 @@

import { combine, isEmptyObject, timeStampNow, currentDrift, display, createEventRateLimiter, canUseEventBridge, } from '@datadog/browser-core';
import { combine, isEmptyObject, timeStampNow, currentDrift, display, createEventRateLimiter, canUseEventBridge, assign, } from '@datadog/browser-core';
import { getSyntheticsContext } from './contexts/syntheticsContext';

@@ -6,22 +6,32 @@ import { getCiTestContext } from './contexts/ciTestContext';

import { limitModification } from './limitModification';
var VIEW_EVENTS_MODIFIABLE_FIELD_PATHS = [
// Fields with sensitive data
'view.url',
'view.referrer',
'action.target.name',
'error.message',
'error.stack',
'error.resource.url',
'resource.url',
];
var OTHER_EVENTS_MODIFIABLE_FIELD_PATHS = VIEW_EVENTS_MODIFIABLE_FIELD_PATHS.concat([
// User-customizable field
'context',
]);
var VIEW_MODIFIABLE_FIELD_PATHS = {
'view.url': 'string',
'view.referrer': 'string',
};
var USER_CUSTOMIZABLE_FIELD_PATHS = {
context: 'object',
};
var modifiableFieldPathsByEvent;
export function startRumAssembly(configuration, lifeCycle, sessionManager, viewContexts, urlContexts, actionContexts, buildCommonContext, reportError) {
var _a;
var eventRateLimiters = (_a = {},
_a["error" /* RumEventType.ERROR */] = createEventRateLimiter("error" /* RumEventType.ERROR */, configuration.eventRateLimiterThreshold, reportError),
_a["action" /* RumEventType.ACTION */] = createEventRateLimiter("action" /* RumEventType.ACTION */, configuration.eventRateLimiterThreshold, reportError),
var _a, _b;
modifiableFieldPathsByEvent = (_a = {},
_a["view" /* RumEventType.VIEW */] = VIEW_MODIFIABLE_FIELD_PATHS,
_a["error" /* RumEventType.ERROR */] = assign({
'error.message': 'string',
'error.stack': 'string',
'error.resource.url': 'string',
'error.fingerprint': 'string',
}, USER_CUSTOMIZABLE_FIELD_PATHS, VIEW_MODIFIABLE_FIELD_PATHS),
_a["resource" /* RumEventType.RESOURCE */] = assign({
'resource.url': 'string',
}, USER_CUSTOMIZABLE_FIELD_PATHS, VIEW_MODIFIABLE_FIELD_PATHS),
_a["action" /* RumEventType.ACTION */] = assign({
'action.target.name': 'string',
}, USER_CUSTOMIZABLE_FIELD_PATHS, VIEW_MODIFIABLE_FIELD_PATHS),
_a["long_task" /* RumEventType.LONG_TASK */] = assign({}, USER_CUSTOMIZABLE_FIELD_PATHS, VIEW_MODIFIABLE_FIELD_PATHS),
_a);
var eventRateLimiters = (_b = {},
_b["error" /* RumEventType.ERROR */] = createEventRateLimiter("error" /* RumEventType.ERROR */, configuration.eventRateLimiterThreshold, reportError),
_b["action" /* RumEventType.ACTION */] = createEventRateLimiter("action" /* RumEventType.ACTION */, configuration.eventRateLimiterThreshold, reportError),
_b);
var syntheticsContext = getSyntheticsContext();

@@ -44,3 +54,3 @@ var ciTestContext = getCiTestContext();

},
browser_sdk_version: canUseEventBridge() ? "4.41.0" : undefined,
browser_sdk_version: canUseEventBridge() ? "4.42.0" : undefined,
},

@@ -91,3 +101,5 @@ application: {

if (beforeSend) {
var result = limitModification(event, event.type === "view" /* RumEventType.VIEW */ ? VIEW_EVENTS_MODIFIABLE_FIELD_PATHS : OTHER_EVENTS_MODIFIABLE_FIELD_PATHS, function (event) { return beforeSend(event, domainContext); });
var result = limitModification(event, modifiableFieldPathsByEvent[event.type], function (event) {
return beforeSend(event, domainContext);
});
if (result === false && event.type !== "view" /* RumEventType.VIEW */) {

@@ -94,0 +106,0 @@ return false;

import type { Context } from '@datadog/browser-core';
export type ModifiableFieldPaths = Record<string, 'string' | 'object'>;
/**

@@ -6,2 +7,2 @@ * Current limitation:

*/
export declare function limitModification<T extends Context, Result>(object: T, modifiableFieldPaths: string[], modifier: (object: T) => Result): Result | undefined;
export declare function limitModification<T extends Context, Result>(object: T, modifiableFieldPaths: ModifiableFieldPaths, modifier: (object: T) => Result): Result | undefined;

@@ -1,2 +0,2 @@

import { sanitize, deepClone, getType } from '@datadog/browser-core';
import { sanitize, deepClone, getType, objectEntries } from '@datadog/browser-core';
/**

@@ -9,12 +9,11 @@ * Current limitation:

var result = modifier(clone);
modifiableFieldPaths.forEach(function (path) {
var originalValue = get(object, path);
var newValue = get(clone, path);
var originalType = getType(originalValue);
objectEntries(modifiableFieldPaths).forEach(function (_a) {
var fieldPath = _a[0], fieldType = _a[1];
var newValue = get(clone, fieldPath);
var newType = getType(newValue);
if (newType === originalType) {
set(object, path, sanitize(newValue));
if (newType === fieldType) {
set(object, fieldPath, sanitize(newValue));
}
else if (originalType === 'object' && (newType === 'undefined' || newType === 'null')) {
set(object, path, {});
else if (fieldType === 'object' && (newType === 'undefined' || newType === 'null')) {
set(object, fieldPath, {});
}

@@ -40,3 +39,3 @@ });

var field = fields[i];
if (!isValidObjectContaining(current, field)) {
if (!isValidObject(current)) {
return;

@@ -52,5 +51,8 @@ }

}
function isValidObject(object) {
return getType(object) === 'object';
}
function isValidObjectContaining(object, field) {
return typeof object === 'object' && object !== null && Object.prototype.hasOwnProperty.call(object, field);
return isValidObject(object) && Object.prototype.hasOwnProperty.call(object, field);
}
//# sourceMappingURL=limitModification.js.map

@@ -54,2 +54,3 @@ import { isEmptyObject, assign, ErrorSource, generateUUID, computeRawError, computeStackTrace, Observable, trackRuntimeError, } from '@datadog/browser-core';

source_type: 'browser',
fingerprint: error.fingerprint,
},

@@ -56,0 +57,0 @@ type: "error" /* RumEventType.ERROR */,

@@ -8,2 +8,3 @@ import { clocksNow, initConsoleObservable, ErrorSource, ConsoleApiName } from '@datadog/browser-core';

stack: consoleError.stack,
fingerprint: consoleError.fingerprint,
source: ErrorSource.CONSOLE,

@@ -10,0 +11,0 @@ handling: "handled" /* ErrorHandling.HANDLED */,

@@ -49,2 +49,3 @@ import type { Context, Duration, ErrorSource, ErrorHandling, ResourceType, ServerDuration, TimeStamp, RawErrorCause } from '@datadog/browser-core';

handling_stack?: string;
fingerprint?: string;
source: ErrorSource;

@@ -51,0 +52,0 @@ message: string;

@@ -205,2 +205,6 @@ /**

/**
* Fingerprint used for Error Tracking custom grouping
*/
fingerprint?: string;
/**
* The type of the error

@@ -207,0 +211,0 @@ */

{
"name": "@datadog/browser-rum-core",
"version": "4.41.0",
"version": "4.42.0",
"license": "Apache-2.0",

@@ -15,3 +15,3 @@ "main": "cjs/index.js",

"dependencies": {
"@datadog/browser-core": "4.41.0"
"@datadog/browser-core": "4.42.0"
},

@@ -29,3 +29,3 @@ "devDependencies": {

},
"gitHead": "1fcc1cabdf989599914551d0ef82e6cb8d569892"
"gitHead": "ce3a34d112bb660116de64da3c57590581d62f77"
}

@@ -230,3 +230,19 @@ import type { ClocksState, RelativeTime } from '@datadog/browser-core'

it('should reject modification on non sensitive and non context field', () => {
describe('allowed customer provided field', () => {
it('should allow modification of the error fingerprint', () => {
const { lifeCycle } = setupBuilder
.withConfiguration({
beforeSend: (event) => (event.error.fingerprint = 'my_fingerprint'),
})
.build()
notifyRawRumEvent(lifeCycle, {
rawRumEvent: createRawRumEvent(RumEventType.ERROR),
})
expect((serverRumEvents[0] as RumErrorEvent).error.fingerprint).toBe('my_fingerprint')
})
})
it('should reject modification of field not sensitive, context or customer provided', () => {
const { lifeCycle } = setupBuilder

@@ -246,2 +262,18 @@ .withConfiguration({

})
it('should not allow to add a sensitive field on the wrong event type', () => {
const { lifeCycle } = setupBuilder
.withConfiguration({
beforeSend: (event) => {
event.error = { message: 'added' }
},
})
.build()
notifyRawRumEvent(lifeCycle, {
rawRumEvent: createRawRumEvent(RumEventType.VIEW),
})
expect((serverRumEvents[0] as any).error?.message).toBeUndefined()
})
})

@@ -248,0 +280,0 @@

@@ -10,2 +10,3 @@ import type { Context, RawError, EventRateLimiter, User } from '@datadog/browser-core'

canUseEventBridge,
assign,
} from '@datadog/browser-core'

@@ -33,2 +34,3 @@ import type { RumEventDomainContext } from '../domainContext.types'

import type { CommonContext } from './contexts/commonContext'
import type { ModifiableFieldPaths } from './limitModification'
import { limitModification } from './limitModification'

@@ -45,18 +47,13 @@

const VIEW_EVENTS_MODIFIABLE_FIELD_PATHS = [
// Fields with sensitive data
'view.url',
'view.referrer',
'action.target.name',
'error.message',
'error.stack',
'error.resource.url',
'resource.url',
]
const VIEW_MODIFIABLE_FIELD_PATHS: ModifiableFieldPaths = {
'view.url': 'string',
'view.referrer': 'string',
}
const OTHER_EVENTS_MODIFIABLE_FIELD_PATHS = VIEW_EVENTS_MODIFIABLE_FIELD_PATHS.concat([
// User-customizable field
'context',
])
const USER_CUSTOMIZABLE_FIELD_PATHS: ModifiableFieldPaths = {
context: 'object',
}
let modifiableFieldPathsByEvent: { [key in RumEventType]: ModifiableFieldPaths }
type Mutable<T> = { -readonly [P in keyof T]: T[P] }

@@ -74,2 +71,30 @@

) {
modifiableFieldPathsByEvent = {
[RumEventType.VIEW]: VIEW_MODIFIABLE_FIELD_PATHS,
[RumEventType.ERROR]: assign(
{
'error.message': 'string',
'error.stack': 'string',
'error.resource.url': 'string',
'error.fingerprint': 'string',
},
USER_CUSTOMIZABLE_FIELD_PATHS,
VIEW_MODIFIABLE_FIELD_PATHS
),
[RumEventType.RESOURCE]: assign(
{
'resource.url': 'string',
},
USER_CUSTOMIZABLE_FIELD_PATHS,
VIEW_MODIFIABLE_FIELD_PATHS
),
[RumEventType.ACTION]: assign(
{
'action.target.name': 'string',
},
USER_CUSTOMIZABLE_FIELD_PATHS,
VIEW_MODIFIABLE_FIELD_PATHS
),
[RumEventType.LONG_TASK]: assign({}, USER_CUSTOMIZABLE_FIELD_PATHS, VIEW_MODIFIABLE_FIELD_PATHS),
}
const eventRateLimiters = {

@@ -162,6 +187,4 @@ [RumEventType.ERROR]: createEventRateLimiter(

if (beforeSend) {
const result = limitModification(
event,
event.type === RumEventType.VIEW ? VIEW_EVENTS_MODIFIABLE_FIELD_PATHS : OTHER_EVENTS_MODIFIABLE_FIELD_PATHS,
(event) => beforeSend(event, domainContext)
const result = limitModification(event, modifiableFieldPathsByEvent[event.type], (event) =>
beforeSend(event, domainContext)
)

@@ -168,0 +191,0 @@ if (result === false && event.type !== RumEventType.VIEW) {

import type { Context } from '@datadog/browser-core'
import { objectEntries } from '@datadog/browser-core'
import type { ModifiableFieldPaths } from './limitModification'
import { limitModification } from './limitModification'

@@ -12,3 +14,3 @@

limitModification(object, ['foo.bar', 'qux'], modifier)
limitModification(object, { 'foo.bar': 'string', qux: 'string' }, modifier)

@@ -28,3 +30,3 @@ expect(object).toEqual({

limitModification(object, ['foo.bar'], modifier)
limitModification(object, { 'foo.bar': 'string' }, modifier)

@@ -37,3 +39,3 @@ expect(object).toEqual({

it('should not allow to add a modifiable fields not present on the original object', () => {
it('should allow to add a modifiable fields not present on the original object', () => {
const object = { foo: { bar: 'bar' }, qux: 'qux' }

@@ -46,4 +48,21 @@ const modifier = (candidate: any) => {

limitModification(object, ['foo.bar', 'qux', 'qix'], modifier)
limitModification(object, { 'foo.bar': 'string', qux: 'string', qix: 'string' }, modifier)
expect(object as any).toEqual({
foo: { bar: 'modified1' },
qux: 'modified2',
qix: 'modified3',
})
})
it('should not allow to add a non modifiable fields not present on the original object', () => {
const object = { foo: { bar: 'bar' }, qux: 'qux' }
const modifier = (candidate: any) => {
candidate.foo.bar = 'modified1'
candidate.qux = 'modified2'
candidate.qix = 'modified3'
}
limitModification(object, { 'foo.bar': 'string', qux: 'string' }, modifier)
expect(object).toEqual({

@@ -60,9 +79,4 @@ foo: { bar: 'modified1' },

null_to_object: null,
object_to_null: {},
undefined_to_object: undefined,
object_to_undefined: {},
array_to_object: [],
object_to_array: {},

@@ -73,11 +87,9 @@ }

candidate.string_to_number = 1234
candidate.null_to_object = {}
candidate.object_to_null = null
candidate.undefined_to_object = {}
candidate.object_to_undefined = undefined
candidate.array_to_object = {}
candidate.object_to_array = []
}
limitModification(object, Object.keys(object), modifier)
limitModification(object, generateModifiableFieldPathsFrom(object), modifier)

@@ -88,9 +100,4 @@ expect(object).toEqual({

null_to_object: null,
object_to_null: {},
undefined_to_object: undefined,
object_to_undefined: {},
array_to_object: [],
object_to_array: {},

@@ -112,3 +119,3 @@ })

limitModification(object, Object.keys(object), modifier)
limitModification(object, generateModifiableFieldPathsFrom(object), modifier)

@@ -130,3 +137,3 @@ expect(object).toEqual({

limitModification(object, ['foo.bar', 'qux'], modifier)
limitModification(object, { 'foo.bar': 'string', qux: 'string' }, modifier)

@@ -146,3 +153,3 @@ expect(object).toEqual({

limitModification(object, ['foo'], modifier)
limitModification(object, { foo: 'object' }, modifier)

@@ -161,3 +168,3 @@ expect(object).toEqual({

const result = limitModification(object, ['foo.bar', 'qux'], modifier)
const result = limitModification(object, { 'foo.bar': 'string', qux: 'string' }, modifier)

@@ -177,5 +184,13 @@ expect(result).toBe(false)

limitModification(object, ['bar'], modifier)
limitModification(object, { bar: 'object' }, modifier)
expect(() => JSON.stringify(object)).not.toThrowError()
})
})
function generateModifiableFieldPathsFrom(object: Record<string, string | object>) {
const modifiableFieldPaths: ModifiableFieldPaths = {}
objectEntries(object).forEach(([key, value]) => {
modifiableFieldPaths[key] = typeof value as 'object' | 'string'
})
return modifiableFieldPaths
}

@@ -1,4 +0,6 @@

import { sanitize, deepClone, getType } from '@datadog/browser-core'
import { sanitize, deepClone, getType, objectEntries } from '@datadog/browser-core'
import type { Context } from '@datadog/browser-core'
export type ModifiableFieldPaths = Record<string, 'string' | 'object'>
/**

@@ -10,3 +12,3 @@ * Current limitation:

object: T,
modifiableFieldPaths: string[],
modifiableFieldPaths: ModifiableFieldPaths,
modifier: (object: T) => Result

@@ -16,11 +18,9 @@ ): Result | undefined {

const result = modifier(clone)
modifiableFieldPaths.forEach((path) => {
const originalValue = get(object, path)
const newValue = get(clone, path)
const originalType = getType(originalValue)
objectEntries(modifiableFieldPaths).forEach(([fieldPath, fieldType]) => {
const newValue = get(clone, fieldPath)
const newType = getType(newValue)
if (newType === originalType) {
set(object, path, sanitize(newValue))
} else if (originalType === 'object' && (newType === 'undefined' || newType === 'null')) {
set(object, path, {})
if (newType === fieldType) {
set(object, fieldPath, sanitize(newValue))
} else if (fieldType === 'object' && (newType === 'undefined' || newType === 'null')) {
set(object, fieldPath, {})
}

@@ -47,3 +47,3 @@ })

const field = fields[i]
if (!isValidObjectContaining(current, field)) {
if (!isValidObject(current)) {
return

@@ -59,4 +59,8 @@ }

function isValidObjectContaining(object: unknown, field: string): object is { [key: string]: unknown } {
return typeof object === 'object' && object !== null && Object.prototype.hasOwnProperty.call(object, field)
function isValidObject(object: unknown): object is Record<string, unknown> {
return getType(object) === 'object'
}
function isValidObjectContaining(object: unknown, field: string): object is Record<string, unknown> {
return isValidObject(object) && Object.prototype.hasOwnProperty.call(object, field)
}
import type { RelativeTime, TimeStamp, ErrorWithCause } from '@datadog/browser-core'
import { ErrorHandling, ErrorSource } from '@datadog/browser-core'
import { ErrorHandling, ErrorSource, NO_ERROR_STACK_PRESENT_MESSAGE } from '@datadog/browser-core'
import type { TestSetupBuilder } from '../../../../test'

@@ -35,37 +35,61 @@ import { setup } from '../../../../test'

describe('provided', () => {
it('notifies a raw rum error event', () => {
const { rawRumEvents } = setupBuilder.build()
const error = new Error('foo')
describe('addError', () => {
;[
{
testCase: 'an error instance',
error: new Error('foo'),
message: 'foo',
type: 'Error',
stack: jasmine.stringMatching('Error: foo'),
},
{
testCase: 'a string',
error: 'foo',
message: 'Provided "foo"',
type: undefined,
stack: NO_ERROR_STACK_PRESENT_MESSAGE,
},
{
testCase: 'an object',
error: { a: 'foo' },
message: 'Provided {"a":"foo"}',
type: undefined,
stack: NO_ERROR_STACK_PRESENT_MESSAGE,
},
].forEach(({ testCase, error, message, type, stack }) => {
it(`notifies a raw rum error event from ${testCase}`, () => {
const { rawRumEvents } = setupBuilder.build()
addError({
error,
handlingStack: 'Error: handling foo',
startClocks: { relative: 1234 as RelativeTime, timeStamp: 123456789 as TimeStamp },
})
addError({
error,
handlingStack: 'Error: handling foo',
startClocks: { relative: 1234 as RelativeTime, timeStamp: 123456789 as TimeStamp },
})
expect(rawRumEvents.length).toBe(1)
expect(rawRumEvents[0]).toEqual({
customerContext: undefined,
rawRumEvent: {
date: jasmine.any(Number),
error: {
id: jasmine.any(String),
message: 'foo',
source: ErrorSource.CUSTOM,
stack: jasmine.stringMatching('Error: foo'),
handling_stack: 'Error: handling foo',
type: 'Error',
handling: ErrorHandling.HANDLED,
source_type: 'browser',
causes: undefined,
expect(rawRumEvents.length).toBe(1)
expect(rawRumEvents[0]).toEqual({
customerContext: undefined,
rawRumEvent: {
date: jasmine.any(Number),
error: {
id: jasmine.any(String),
message,
source: ErrorSource.CUSTOM,
stack,
handling_stack: 'Error: handling foo',
type,
handling: ErrorHandling.HANDLED,
source_type: 'browser',
causes: undefined,
fingerprint: undefined,
},
type: RumEventType.ERROR,
view: {
in_foreground: true,
},
},
type: RumEventType.ERROR,
view: {
in_foreground: true,
},
},
savedCommonContext: undefined,
startTime: 1234 as RelativeTime,
domainContext: { error },
savedCommonContext: undefined,
startTime: 1234 as RelativeTime,
domainContext: { error },
})
})

@@ -99,2 +123,35 @@ })

it('should extract fingerprint from error', () => {
const { rawRumEvents } = setupBuilder.build()
interface DatadogError extends Error {
dd_fingerprint?: string
}
const error = new Error('foo')
;(error as DatadogError).dd_fingerprint = 'my-fingerprint'
addError({
error,
handlingStack: 'Error: handling foo',
startClocks: { relative: 1234 as RelativeTime, timeStamp: 123456789 as TimeStamp },
})
expect((rawRumEvents[0].rawRumEvent as RawRumErrorEvent).error.fingerprint).toEqual('my-fingerprint')
})
it('should sanitize error fingerprint', () => {
const { rawRumEvents } = setupBuilder.build()
const error = new Error('foo')
;(error as any).dd_fingerprint = 2
addError({
error,
handlingStack: 'Error: handling foo',
startClocks: { relative: 1234 as RelativeTime, timeStamp: 123456789 as TimeStamp },
})
expect((rawRumEvents[0].rawRumEvent as RawRumErrorEvent).error.fingerprint).toEqual('2')
})
it('should save the specified customer context', () => {

@@ -200,2 +257,3 @@ const { rawRumEvents } = setupBuilder.build()

causes: undefined,
fingerprint: undefined,
},

@@ -202,0 +260,0 @@ view: {

@@ -107,2 +107,3 @@ import type { Context, RawError, ClocksState } from '@datadog/browser-core'

source_type: 'browser',
fingerprint: error.fingerprint,
},

@@ -109,0 +110,0 @@ type: RumEventType.ERROR as const,

import type { RawError, Subscription } from '@datadog/browser-core'
import { ErrorHandling, ErrorSource, Observable, clocksNow } from '@datadog/browser-core'
import { ErrorHandling, ErrorSource, Observable, clocksNow, resetConsoleObservable } from '@datadog/browser-core'
import type { Clock } from '@datadog/browser-core/test'

@@ -23,2 +23,3 @@ import { mockClock } from '@datadog/browser-core/test'

afterEach(() => {
resetConsoleObservable()
subscription.unsubscribe()

@@ -36,2 +37,3 @@ clock.cleanup()

stack: jasmine.any(String),
fingerprint: undefined,
source: ErrorSource.CONSOLE,

@@ -42,2 +44,23 @@ handling: ErrorHandling.HANDLED,

})
it('should retrieve fingerprint from console error', () => {
interface DatadogError extends Error {
dd_fingerprint?: string
}
const error = new Error('foo')
;(error as DatadogError).dd_fingerprint = 'my-fingerprint'
// eslint-disable-next-line no-console
console.error(error)
expect(notifyLog).toHaveBeenCalledWith({
startClocks: clocksNow(),
message: jasmine.any(String),
stack: jasmine.any(String),
source: ErrorSource.CONSOLE,
handling: ErrorHandling.HANDLED,
handlingStack: jasmine.any(String),
fingerprint: 'my-fingerprint',
})
})
})

@@ -10,2 +10,3 @@ import type { Observable, RawError } from '@datadog/browser-core'

stack: consoleError.stack,
fingerprint: consoleError.fingerprint,
source: ErrorSource.CONSOLE,

@@ -12,0 +13,0 @@ handling: ErrorHandling.HANDLED,

@@ -62,2 +62,3 @@ import type {

handling_stack?: string
fingerprint?: string
source: ErrorSource

@@ -64,0 +65,0 @@ message: string

@@ -208,2 +208,6 @@ /* eslint-disable */

/**
* Fingerprint used for Error Tracking custom grouping
*/
fingerprint?: string
/**
* The type of the error

@@ -210,0 +214,0 @@ */

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc