From 9199de2b855169376802a318a7a21d1c503fc62f Mon Sep 17 00:00:00 2001 From: Sai Kumar Battinoju <88789928+saikumarrs@users.noreply.github.com> Date: Wed, 19 Feb 2025 13:31:16 +0530 Subject: [PATCH 01/17] fix: replace vulnerable package with custom implementation (#2046) * fix: replace vulnerable package with custom implementation * fix: avoid overwriting options * test: add documentation and unit tests for additional clarity --- package-lock.json | 21 ---- package.json | 1 - .../__tests__/utilities/object.test.ts | 100 ++++++++++++++++++ packages/analytics-js-common/package.json | 1 - .../src/utilities/object.ts | 17 +++ .../src/v1.1/utils/storage/cookie.js | 20 ++-- .../src/v1.1/utils/storage/store.js | 13 ++- 7 files changed, 138 insertions(+), 35 deletions(-) diff --git a/package-lock.json b/package-lock.json index fe5b8373f1..99ccf9e3b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,6 @@ ], "dependencies": { "@lukeed/uuid": "2.0.1", - "@ndhoule/defaults": "2.0.1", "@ndhoule/each": "2.0.1", "@ndhoule/extend": "2.0.0", "@preact/signals-core": "1.8.0", @@ -3984,20 +3983,6 @@ "@tybys/wasm-util": "^0.9.0" } }, - "node_modules/@ndhoule/defaults": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@ndhoule/defaults/-/defaults-2.0.1.tgz", - "integrity": "sha512-wwuxdvaTbgw9mmeZ516RQWx9V+ToC+B6t3g+eM/CgzXsf+JLvunLd9WOn9yHP4tzxV6nDDTx8fEwhU+xWsX4tA==", - "dependencies": { - "@ndhoule/drop": "^2.0.0", - "@ndhoule/rest": "^2.0.0" - } - }, - "node_modules/@ndhoule/drop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@ndhoule/drop/-/drop-2.0.0.tgz", - "integrity": "sha512-vtVA2bqlzvYhwjxbKUYbGM8Ouba4n3cXKWbDyLaZldjUPQ2Mlizv3LR1J7qqRVTUpxZj41vUOQwqXdocrCrWzw==" - }, "node_modules/@ndhoule/each": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@ndhoule/each/-/each-2.0.1.tgz", @@ -4016,11 +4001,6 @@ "resolved": "https://registry.npmjs.org/@ndhoule/keys/-/keys-2.0.0.tgz", "integrity": "sha512-vtCqKBC1Av6dsBA8xpAO+cgk051nfaI+PnmTZep2Px0vYrDvpUmLxv7z40COlWH5yCpu3gzNhepk+02yiQiZNw==" }, - "node_modules/@ndhoule/rest": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@ndhoule/rest/-/rest-2.0.0.tgz", - "integrity": "sha512-oBzJczbr6E/McwdSYWzOJvIcIFvLDHM0NHct+B2T7RQStpLMP+H4ay4kzxk0Wts8MaLw5BSRxxelAhWERRol3w==" - }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -25533,7 +25513,6 @@ "license": "Elastic-2.0", "dependencies": { "@lukeed/uuid": "2.0.1", - "@ndhoule/defaults": "2.0.1", "@preact/signals-core": "1.8.0", "@segment/top-domain": "3.0.1", "crypto-js": "4.2.0", diff --git a/package.json b/package.json index eb15c57c82..836df71fb7 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,6 @@ }, "dependencies": { "@lukeed/uuid": "2.0.1", - "@ndhoule/defaults": "2.0.1", "@ndhoule/each": "2.0.1", "@ndhoule/extend": "2.0.0", "@preact/signals-core": "1.8.0", diff --git a/packages/analytics-js-common/__tests__/utilities/object.test.ts b/packages/analytics-js-common/__tests__/utilities/object.test.ts index 9fd417360e..197d67712f 100644 --- a/packages/analytics-js-common/__tests__/utilities/object.test.ts +++ b/packages/analytics-js-common/__tests__/utilities/object.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable compat/compat */ // eslint-disable-next-line max-classes-per-file import { mergeDeepRight, @@ -130,6 +131,98 @@ const expectedMergedTraitsPayload = { describe('Common Utils - Object', () => { describe('mergeDeepRight', () => { + it.each([ + ['merges flat objects', { a: 1, b: 2 }, { b: 3, c: 4 }, { a: 1, b: 3, c: 4 }], + [ + 'merges nested objects', + { a: { x: 1, y: 2 }, b: 1 }, + { a: { y: 3, z: 4 }, c: 2 }, + { a: { x: 1, y: 3, z: 4 }, b: 1, c: 2 }, + ], + ['replaces arrays at same index', { arr: [1, 2, 3] }, { arr: [4, 5] }, { arr: [4, 5, 3] }], + [ + 'deep merges objects within arrays', + { arr: [{ x: 1 }, { y: 2 }] }, + { arr: [{ z: 3 }, { w: 4 }] }, + { + arr: [ + { x: 1, z: 3 }, + { y: 2, w: 4 }, + ], + }, + ], + [ + 'handles mixed data types', + { a: 1, b: { x: [1, { y: 2 }] } }, + { a: 2, b: { x: [3, { z: 4 }] } }, + { a: 2, b: { x: [3, { y: 2, z: 4 }] } }, + ], + [ + 'preserves null values from right object', + { a: 1, b: { x: 2 } }, + { a: null, b: { x: null } }, + { a: null, b: { x: null } }, + ], + ['handles empty objects', { a: 1 }, {}, { a: 1 }], + [ + 'handles deeply nested arrays', + { a: [1, [2, { x: 3 }]] }, + { a: [4, [5, { y: 6 }]] }, + { a: [4, { 0: 5, 1: { x: 3, y: 6 } }] }, + ], + [ + 'handles arrays of different lengths', + { arr: [1, 2, 3, 4] }, + { arr: [5, 6] }, + { arr: [5, 6, 3, 4] }, + ], + [ + 'preserves array type when merging with non-array', + { arr: [1, 2, 3] }, + { arr: 'not-array' }, + { arr: 'not-array' }, + ], + [ + 'handles undefined values', + { a: 1, b: undefined }, + { b: 2, c: undefined }, + { a: 1, b: 2, c: undefined }, + ], + [ + 'handles Date objects', + { date: new Date('2024-01-01'), data: { x: 1 } }, + { data: { y: 2 } }, + { date: new Date('2024-01-01'), data: { x: 1, y: 2 } }, + ], + [ + 'handles RegExp objects', + { regex: /test/, data: { x: 1 } }, + { data: { y: 2 } }, + { regex: /test/, data: { x: 1, y: 2 } }, + ], + [ + 'handles nested arrays with sparse elements', + // eslint-disable-next-line no-sparse-arrays + { arr: [1, , 3] }, // sparse array + { arr: [4, 5] }, + { arr: [4, 5, 3] }, + ], + [ + 'merges objects with Symbol properties', + { [Symbol.for('test')]: 1, a: 2 }, + { [Symbol.for('test')]: 3, b: 4 }, + { a: 2, b: 4 }, + ], + [ + 'handles circular references gracefully', + { a: 1, b: { x: 2 } }, + { b: { x: 3, y: { ref: 'circular' } } }, + { a: 1, b: { x: 3, y: { ref: 'circular' } } }, + ], + ])('%s', (_, left, right, expected) => { + expect(mergeDeepRight(left, right)).toStrictEqual(expected); + }); + it('should merge right object array items', () => { const mergedArray = mergeDeepRightObjectArrays( identifyTraitsPayloadMock.address, @@ -155,6 +248,13 @@ describe('Common Utils - Object', () => { const mergedArray = mergeDeepRight(identifyTraitsPayloadMock, trackTraitsOverridePayloadMock); expect(mergedArray).toEqual(expectedMergedTraitsPayload); }); + + // Separate test for function equality since it can't be easily compared in .each + it('preserves function references when merging', () => { + const fn = () => 'test'; + const result = mergeDeepRight({ fn, data: { x: 1 } }, { data: { y: 2 } }); + expect(result.fn).toBe(fn); + }); }); describe('isObjectAndNotNull', () => { diff --git a/packages/analytics-js-common/package.json b/packages/analytics-js-common/package.json index 67ff93ad24..d28396779a 100644 --- a/packages/analytics-js-common/package.json +++ b/packages/analytics-js-common/package.json @@ -56,7 +56,6 @@ }, "dependencies": { "@preact/signals-core": "1.8.0", - "@ndhoule/defaults": "2.0.1", "rudder-component-cookie": "0.0.1", "@segment/top-domain": "3.0.1", "crypto-js": "4.2.0", diff --git a/packages/analytics-js-common/src/utilities/object.ts b/packages/analytics-js-common/src/utilities/object.ts index 6c0e4d4913..1b70ca4691 100644 --- a/packages/analytics-js-common/src/utilities/object.ts +++ b/packages/analytics-js-common/src/utilities/object.ts @@ -27,6 +27,14 @@ const isObjectAndNotNull = (value: any): value is object => const isObjectLiteralAndNotNull = (value?: T): value is T => !isNull(value) && Object.prototype.toString.call(value) === '[object Object]'; +/** + * Merges two arrays deeply, right-to-left + * In the case of conflicts, the right array's values replace the left array's values in the + * same index position + * @param leftValue - The left array + * @param rightValue - The right array + * @returns The merged array + */ const mergeDeepRightObjectArrays = ( leftValue: any | any[], rightValue: any | any[], @@ -46,6 +54,15 @@ const mergeDeepRightObjectArrays = ( return mergedArray; }; +/** + * Merges two objects deeply, right-to-left. + * In the case of conflicts, the right object's values take precedence. + * For arrays, the right array's values replace the left array's values in the + * same index position keeping the remaining left array's values in the resultant array. + * @param leftObject - The left object + * @param rightObject - The right object + * @returns The merged object + */ const mergeDeepRight = >( leftObject: Record, rightObject: Record, diff --git a/packages/analytics-js-common/src/v1.1/utils/storage/cookie.js b/packages/analytics-js-common/src/v1.1/utils/storage/cookie.js index 2fb57d1778..84bbb7d995 100644 --- a/packages/analytics-js-common/src/v1.1/utils/storage/cookie.js +++ b/packages/analytics-js-common/src/v1.1/utils/storage/cookie.js @@ -1,7 +1,7 @@ import cookie from 'rudder-component-cookie'; -import defaults from '@ndhoule/defaults'; import topDomain from '@segment/top-domain'; import { clone } from 'ramda'; +import { mergeDeepRight } from '../../../utilities/object'; import { logger } from '../logUtil'; /** @@ -24,13 +24,17 @@ class CookieLocal { let domain = `.${topDomain(window.location.href)}`; if (domain === '.') domain = null; - // the default maxage and path - this.cOpts = defaults(inOpts, { - maxage: 31536000000, - path: '/', - domain, - samesite: 'Lax', - }); + // Override the default options with the provided options + this.cOpts = mergeDeepRight( + { + maxage: 31536000000, + path: '/', + domain, + samesite: 'Lax', + }, + inOpts, + ); + // configure cookies for exactly same domain if (inOpts.sameDomainCookiesOnly) { delete this.cOpts.domain; diff --git a/packages/analytics-js-common/src/v1.1/utils/storage/store.js b/packages/analytics-js-common/src/v1.1/utils/storage/store.js index 7a442753e2..2304bb4212 100644 --- a/packages/analytics-js-common/src/v1.1/utils/storage/store.js +++ b/packages/analytics-js-common/src/v1.1/utils/storage/store.js @@ -1,5 +1,5 @@ -import defaults from '@ndhoule/defaults'; import store from 'storejs'; +import { mergeDeepRight } from '../../../utilities/object'; /** * An object utility to persist user and other values in localstorage @@ -18,10 +18,15 @@ class StoreLocal { options(options = {}) { if (arguments.length === 0) return this.sOpts; - defaults(options, { enabled: true }); + // Override the default options with the provided options + this.sOpts = mergeDeepRight( + { + enabled: true, + }, + options, + ); - this.enabled = options.enabled && this.enabled; - this.sOpts = options; + this.enabled = this.sOpts.enabled && this.enabled; return this.sOpts; } From c1941c6bce9dfe16fba1a49544b39ae4f8104184 Mon Sep 17 00:00:00 2001 From: Sai Kumar Battinoju <88789928+saikumarrs@users.noreply.github.com> Date: Wed, 19 Feb 2025 15:14:35 +0530 Subject: [PATCH 02/17] feat: turn sdk deprecation warnings into errors (#2049) --- packages/analytics-v1.1/src/core/analytics.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/analytics-v1.1/src/core/analytics.js b/packages/analytics-v1.1/src/core/analytics.js index 44f08813af..4c82cdf959 100644 --- a/packages/analytics-v1.1/src/core/analytics.js +++ b/packages/analytics-v1.1/src/core/analytics.js @@ -1660,11 +1660,11 @@ const setAuthToken = instance.setAuthToken.bind(instance); // eslint-disable-next-line no-constant-condition if ('__MODULE_TYPE__' === 'npm') { - logger.warn( + logger.error( 'This RudderStack JavaScript SDK package is deprecated and no longer maintained. While your events are still being tracked and delivered, we strongly recommend you to migrate to the latest [@rudderstack/analytics-js](https://www.npmjs.com/package/@rudderstack/analytics-js) package for enhanced features, security updates, and ongoing support. For more details, visit the migration guide: https://www.rudderstack.com/docs/sources/event-streams/sdks/rudderstack-javascript-sdk/migration-guide/.', ); } else { - logger.warn( + logger.error( 'This version of the RudderStack JavaScript SDK is deprecated and no longer maintained. While your events are still being tracked and delivered, we strongly recommend you to migrate to the latest version (v3) for enhanced features, security updates, and ongoing support. For more details, visit the migration guide: https://www.rudderstack.com/docs/sources/event-streams/sdks/rudderstack-javascript-sdk/migration-guide/.', ); } From 843d944f2d63ee414cbcf9d7c991ba97567cdac3 Mon Sep 17 00:00:00 2001 From: Sai Kumar Battinoju <88789928+saikumarrs@users.noreply.github.com> Date: Wed, 19 Feb 2025 15:20:37 +0530 Subject: [PATCH 03/17] fix: sdk loading snippet to reduce ambiguity in updating url (#2048) * fix: sdk loading snippet to reduce ambiguity in updating url * chore: update loading snippet in all the examples * fix: use the updated snippet version --- examples/angular/sample-app/src/index.html | 10 +++++-- .../app-using-v3-cdn/public/index.html | 14 +++++----- .../hooks/sample-app/src/app/layout.tsx | 10 +++++-- .../nextjs/js/sample-app/src/app/layout.js | 10 +++++-- .../sample-app/src/pages/_document.tsx | 10 +++++-- .../nextjs/ts/sample-app/src/app/layout.tsx | 10 +++++-- .../hooks/sample-app/public/index.html | 10 +++++-- .../reactjs/js/sample-app/public/index.html | 10 +++++-- .../reactjs/ts/sample-app/public/index.html | 10 +++++-- examples/reactjs/vite/sample-app/index.html | 10 +++++-- examples/v3-beacon/index.html | 11 ++++---- examples/v3-legacy-minimum-plugins/index.html | 11 ++++---- examples/v3-legacy/index.html | 11 ++++---- examples/v3-minimum-plugins/index.html | 11 ++++---- examples/v3/index.html | 11 ++++---- .../analytics-js/__fixtures__/msw.handlers.ts | 2 +- .../analytics-js/__tests__/nativeSdkLoader.js | 9 +++--- packages/analytics-js/public/index.html | 25 ++++++++--------- packages/loading-scripts/rollup.config.mjs | 6 ++-- packages/loading-scripts/src/index.ts | 7 +++-- .../sanity-suite/public/v3/index-cdn.html | 28 +++++++++---------- .../sanity-suite/public/v3/index-local.html | 25 ++++++++--------- .../public/v3/index-npm-bundled.html | 10 +++++-- .../sanity-suite/public/v3/index-npm.html | 10 +++++-- .../public/v3/integrations/index-cdn.html | 25 ++++++++--------- .../public/v3/integrations/index-local.html | 25 ++++++++--------- .../v3/integrations/index-npm-bundled.html | 10 +++++-- .../public/v3/integrations/index-npm.html | 10 +++++-- .../public/v3/manualLoadCall/index-cdn.html | 25 ++++++++--------- .../public/v3/manualLoadCall/index-local.html | 25 ++++++++--------- .../v3/manualLoadCall/index-npm-bundled.html | 10 +++++-- .../public/v3/manualLoadCall/index-npm.html | 10 +++++-- 32 files changed, 241 insertions(+), 180 deletions(-) diff --git a/examples/angular/sample-app/src/index.html b/examples/angular/sample-app/src/index.html index a98f255267..f6c7e96c51 100644 --- a/examples/angular/sample-app/src/index.html +++ b/examples/angular/sample-app/src/index.html @@ -9,7 +9,7 @@