@openreplay/tracker 16.2.0 → 16.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/lib/index.js CHANGED
@@ -4693,11 +4693,52 @@ class IFrameOffsets {
4693
4693
 
4694
4694
  var InlineCssMode;
4695
4695
  (function (InlineCssMode) {
4696
- InlineCssMode[InlineCssMode["None"] = 0] = "None";
4697
- InlineCssMode[InlineCssMode["RemoteOnly"] = 1] = "RemoteOnly";
4698
- InlineCssMode[InlineCssMode["RemoteWithForceFetch"] = 2] = "RemoteWithForceFetch";
4699
- InlineCssMode[InlineCssMode["All"] = 3] = "All";
4696
+ /** default behavior -- will parse and cache the css file on backend */
4697
+ InlineCssMode[InlineCssMode["Disabled"] = 0] = "Disabled";
4698
+ /** will attempt to record the linked css file as AdoptedStyleSheet object */
4699
+ InlineCssMode[InlineCssMode["Inline"] = 1] = "Inline";
4700
+ /** will fetch the file, then simulated AdoptedStyleSheets behavior programmaticaly for the replay */
4701
+ InlineCssMode[InlineCssMode["InlineFetched"] = 2] = "InlineFetched";
4702
+ /** will fetch the file, then save it as plain css inside <style> node */
4703
+ InlineCssMode[InlineCssMode["PlainFetched"] = 3] = "PlainFetched";
4700
4704
  })(InlineCssMode || (InlineCssMode = {}));
4705
+ function getInlineOptions(mode) {
4706
+ switch (mode) {
4707
+ case InlineCssMode.Inline:
4708
+ return {
4709
+ inlineRemoteCss: true,
4710
+ inlinerOptions: {
4711
+ forceFetch: false,
4712
+ forcePlain: false,
4713
+ },
4714
+ };
4715
+ case InlineCssMode.InlineFetched:
4716
+ return {
4717
+ inlineRemoteCss: true,
4718
+ inlinerOptions: {
4719
+ forceFetch: true,
4720
+ forcePlain: false,
4721
+ },
4722
+ };
4723
+ case InlineCssMode.PlainFetched:
4724
+ return {
4725
+ inlineRemoteCss: true,
4726
+ inlinerOptions: {
4727
+ forceFetch: true,
4728
+ forcePlain: true,
4729
+ },
4730
+ };
4731
+ case InlineCssMode.Disabled:
4732
+ default:
4733
+ return {
4734
+ inlineRemoteCss: false,
4735
+ inlinerOptions: {
4736
+ forceFetch: false,
4737
+ forcePlain: false,
4738
+ },
4739
+ };
4740
+ }
4741
+ }
4701
4742
  const attachShadowNativeFn = IN_BROWSER ? Element.prototype.attachShadow : () => new ShadowRoot();
4702
4743
  class TopObserver extends Observer {
4703
4744
  constructor(params) {
@@ -4706,7 +4747,11 @@ class TopObserver extends Observer {
4706
4747
  disableSprites: false,
4707
4748
  inlineCss: 0,
4708
4749
  }, params.options);
4709
- super(params.app, true, opts);
4750
+ const observerOptions = {
4751
+ disableSprites: opts.disableSprites,
4752
+ ...getInlineOptions(opts.inlineCss)
4753
+ };
4754
+ super(params.app, true, observerOptions);
4710
4755
  this.iframeOffsets = new IFrameOffsets();
4711
4756
  this.contextCallbacks = [];
4712
4757
  // Attached once per Tracker instance
@@ -5126,43 +5171,6 @@ function getTimezone() {
5126
5171
  return `UTC${sign}${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
5127
5172
  }
5128
5173
  const delay = (ms) => new Promise((res) => setTimeout(res, ms));
5129
- function getInlineOptions(mode) {
5130
- switch (mode) {
5131
- case InlineCssMode.RemoteOnly:
5132
- return {
5133
- inlineRemoteCss: true,
5134
- inlinerOptions: {
5135
- forceFetch: false,
5136
- forcePlain: false,
5137
- },
5138
- };
5139
- case InlineCssMode.RemoteWithForceFetch:
5140
- return {
5141
- inlineRemoteCss: true,
5142
- inlinerOptions: {
5143
- forceFetch: true,
5144
- forcePlain: false,
5145
- },
5146
- };
5147
- case InlineCssMode.All:
5148
- return {
5149
- inlineRemoteCss: true,
5150
- inlinerOptions: {
5151
- forceFetch: true,
5152
- forcePlain: true,
5153
- },
5154
- };
5155
- case InlineCssMode.None:
5156
- default:
5157
- return {
5158
- inlineRemoteCss: false,
5159
- inlinerOptions: {
5160
- forceFetch: false,
5161
- forcePlain: false,
5162
- },
5163
- };
5164
- }
5165
- }
5166
5174
  const proto = {
5167
5175
  // ask if there are any tabs alive
5168
5176
  ask: 'never-gonna-give-you-up',
@@ -5194,7 +5202,7 @@ class App {
5194
5202
  this.stopCallbacks = [];
5195
5203
  this.commitCallbacks = [];
5196
5204
  this.activityState = ActivityState.NotActive;
5197
- this.version = '16.2.0'; // TODO: version compatability check inside each plugin.
5205
+ this.version = '16.2.1'; // TODO: version compatability check inside each plugin.
5198
5206
  this.socketMode = false;
5199
5207
  this.compressionThreshold = 24 * 1000;
5200
5208
  this.bc = null;
@@ -5517,19 +5525,6 @@ class App {
5517
5525
  this.onUxtCb = [];
5518
5526
  this.contextId = Math.random().toString(36).slice(2);
5519
5527
  this.projectKey = projectKey;
5520
- this.inlineCss = getInlineOptions(options.inlineCss ?? 0);
5521
- if (Object.keys(options).findIndex((k) => ['fixedCanvasScaling', 'disableCanvas'].includes(k)) !==
5522
- -1) {
5523
- console.warn('Openreplay: canvas options are moving to separate key "canvas" in next update. Please update your configuration.');
5524
- options = {
5525
- ...options,
5526
- canvas: {
5527
- __save_canvas_locally: options.__save_canvas_locally,
5528
- fixedCanvasScaling: options.fixedCanvasScaling,
5529
- disableCanvas: options.disableCanvas,
5530
- },
5531
- };
5532
- }
5533
5528
  this.networkOptions = options.network;
5534
5529
  const defaultOptions = {
5535
5530
  revID: '',
@@ -5544,13 +5539,10 @@ class App {
5544
5539
  __is_snippet: false,
5545
5540
  __debug_report_edp: null,
5546
5541
  __debug__: LogLevel.Silent,
5547
- __save_canvas_locally: false,
5548
5542
  localStorage: null,
5549
5543
  sessionStorage: null,
5550
5544
  forceSingleTab: false,
5551
5545
  assistSocketHost: '',
5552
- fixedCanvasScaling: false,
5553
- disableCanvas: false,
5554
5546
  captureIFrames: true,
5555
5547
  obscureTextEmails: false,
5556
5548
  obscureTextNumbers: false,
@@ -5588,7 +5580,7 @@ class App {
5588
5580
  forceNgOff: Boolean(options.forceNgOff),
5589
5581
  maintainer: this.options.nodes?.maintainer,
5590
5582
  });
5591
- this.observer = new TopObserver({ app: this, options: { ...options, ...this.inlineCss } });
5583
+ this.observer = new TopObserver({ app: this, options });
5592
5584
  this.ticker = new Ticker(this);
5593
5585
  this.ticker.attach(() => this.commit());
5594
5586
  this.debug = new Logger(this.options.__debug__);
@@ -8351,6 +8343,152 @@ function isObject(thing) {
8351
8343
  return thing !== null && typeof thing === 'object';
8352
8344
  }
8353
8345
 
8346
+ const sensitiveParams = new Set([
8347
+ "password",
8348
+ "pass",
8349
+ "pwd",
8350
+ "mdp",
8351
+ "token",
8352
+ "bearer",
8353
+ "jwt",
8354
+ "api_key",
8355
+ "api-key",
8356
+ "apiKey",
8357
+ "secret",
8358
+ "ssn",
8359
+ "zip",
8360
+ "zipcode",
8361
+ "x-api-key",
8362
+ "www-authenticate",
8363
+ "x-csrf-token",
8364
+ "x-requested-with",
8365
+ "x-forwarded-for",
8366
+ "x-real-ip",
8367
+ "cookie",
8368
+ "authorization",
8369
+ "auth",
8370
+ "proxy-authorization",
8371
+ "set-cookie",
8372
+ "account_key",
8373
+ ]);
8374
+ function numDigits(x) {
8375
+ return (Math.log10((x ^ (x >> 31)) - (x >> 31)) | 0) + 1;
8376
+ }
8377
+ function obscure(value) {
8378
+ if (typeof value === "number") {
8379
+ const digits = numDigits(value);
8380
+ return "9".repeat(digits);
8381
+ }
8382
+ if (typeof value === "string") {
8383
+ return value.replace(/[^\f\n\r\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff\s]/g, '*');
8384
+ }
8385
+ return value;
8386
+ }
8387
+ function filterHeaders(headers) {
8388
+ const filteredHeaders = {};
8389
+ if (Array.isArray(headers)) {
8390
+ headers.forEach(({ name, value }) => {
8391
+ if (sensitiveParams.has(name.toLowerCase())) {
8392
+ filteredHeaders[name] = obscure(value);
8393
+ }
8394
+ else {
8395
+ filteredHeaders[name] = value;
8396
+ }
8397
+ });
8398
+ }
8399
+ else {
8400
+ for (const [key, value] of Object.entries(headers)) {
8401
+ if (sensitiveParams.has(key.toLowerCase())) {
8402
+ filteredHeaders[key] = obscure(value);
8403
+ }
8404
+ else {
8405
+ filteredHeaders[key] = value;
8406
+ }
8407
+ }
8408
+ }
8409
+ return filteredHeaders;
8410
+ }
8411
+ function filterBody(body) {
8412
+ if (!body) {
8413
+ return body;
8414
+ }
8415
+ let parsedBody;
8416
+ let isJSON = false;
8417
+ try {
8418
+ parsedBody = JSON.parse(body);
8419
+ isJSON = true;
8420
+ }
8421
+ catch (e) {
8422
+ // not json
8423
+ }
8424
+ if (isJSON) {
8425
+ obscureSensitiveData(parsedBody);
8426
+ return JSON.stringify(parsedBody);
8427
+ }
8428
+ else {
8429
+ const isUrlSearch = typeof body === "string" && body.includes("?") && body.includes("=");
8430
+ if (isUrlSearch) {
8431
+ try {
8432
+ const params = new URLSearchParams(body);
8433
+ for (const key of params.keys()) {
8434
+ if (sensitiveParams.has(key.toLowerCase())) {
8435
+ const value = obscure(params.get(key));
8436
+ params.set(key, value);
8437
+ }
8438
+ }
8439
+ return params.toString();
8440
+ }
8441
+ catch (e) {
8442
+ // not url query ?
8443
+ return body;
8444
+ }
8445
+ }
8446
+ else {
8447
+ // not json or url query
8448
+ return body;
8449
+ }
8450
+ }
8451
+ }
8452
+ function sanitizeObject(obj) {
8453
+ obscureSensitiveData(obj);
8454
+ return obj;
8455
+ }
8456
+ function obscureSensitiveData(obj) {
8457
+ if (Array.isArray(obj)) {
8458
+ obj.forEach(obscureSensitiveData);
8459
+ }
8460
+ else if (obj && typeof obj === "object") {
8461
+ for (const key in obj) {
8462
+ if (Object.hasOwn(obj, key)) {
8463
+ if (sensitiveParams.has(key.toLowerCase())) {
8464
+ obj[key] = obscure(obj[key]);
8465
+ }
8466
+ else if (obj[key] !== null && typeof obj[key] === "object") {
8467
+ obscureSensitiveData(obj[key]);
8468
+ }
8469
+ }
8470
+ }
8471
+ }
8472
+ }
8473
+ function tryFilterUrl(url) {
8474
+ if (!url)
8475
+ return "";
8476
+ try {
8477
+ const urlObj = new URL(url);
8478
+ if (urlObj.searchParams) {
8479
+ for (const key of urlObj.searchParams.keys()) {
8480
+ if (sensitiveParams.has(key.toLowerCase())) {
8481
+ urlObj.searchParams.set(key, "******");
8482
+ }
8483
+ }
8484
+ }
8485
+ return urlObj.toString();
8486
+ }
8487
+ catch (e) {
8488
+ return url;
8489
+ }
8490
+ }
8491
+
8354
8492
  /**
8355
8493
  * I know we're not using most of the information from this class
8356
8494
  * but it can be useful in the future if we will decide to display more stuff in our ui
@@ -8382,13 +8520,18 @@ class NetworkMessage {
8382
8520
  }
8383
8521
  getMessage() {
8384
8522
  const { reqHs, resHs } = this.writeHeaders();
8523
+ const reqBody = this.method === 'GET'
8524
+ ? JSON.stringify(sanitizeObject(this.getData)) : filterBody(this.requestData);
8385
8525
  const request = {
8386
- headers: reqHs,
8387
- body: this.method === 'GET' ? JSON.stringify(this.getData) : this.requestData,
8526
+ headers: filterHeaders(reqHs),
8527
+ body: reqBody,
8528
+ };
8529
+ const response = {
8530
+ headers: filterHeaders(resHs),
8531
+ body: filterBody(this.response)
8388
8532
  };
8389
- const response = { headers: resHs, body: this.response };
8390
8533
  const messageInfo = this.sanitize({
8391
- url: this.url,
8534
+ url: tryFilterUrl(this.url),
8392
8535
  method: this.method,
8393
8536
  status: this.status,
8394
8537
  request,
@@ -8704,9 +8847,10 @@ class ResponseProxyHandler {
8704
8847
  if (typeof this.resp.body.getReader !== 'function') {
8705
8848
  return;
8706
8849
  }
8707
- const _getReader = this.resp.body.getReader;
8850
+ const clonedResp = this.resp.clone();
8851
+ const _getReader = clonedResp.body.getReader;
8708
8852
  // @ts-ignore
8709
- this.resp.body.getReader = () => {
8853
+ clonedResp.body.getReader = () => {
8710
8854
  const reader = _getReader.apply(this.resp.body);
8711
8855
  // when readyState is already 4,
8712
8856
  // it's not a chunked stream, or it had already been read.
@@ -9548,7 +9692,7 @@ class API {
9548
9692
  this.signalStartIssue = (reason, missingApi) => {
9549
9693
  const doNotTrack = this.checkDoNotTrack();
9550
9694
  console.log("Tracker couldn't start due to:", JSON.stringify({
9551
- trackerVersion: '16.2.0',
9695
+ trackerVersion: '16.2.1',
9552
9696
  projectKey: this.options.projectKey,
9553
9697
  doNotTrack,
9554
9698
  reason: missingApi.length ? `missing api: ${missingApi.join(',')}` : reason,