clarity-js 0.6.37 → 0.6.40
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/build/clarity.js +132 -70
- package/build/clarity.min.js +1 -1
- package/build/clarity.module.js +132 -70
- package/package.json +1 -1
- package/src/core/scrub.ts +33 -10
- package/src/core/version.ts +1 -1
- package/src/index.ts +1 -1
- package/src/interaction/pointer.ts +1 -1
- package/src/layout/dom.ts +18 -14
- package/src/layout/encode.ts +1 -1
- package/src/layout/selector.ts +47 -27
- package/test/core.test.ts +26 -2
- package/test/helper.ts +30 -2
- package/test/html/core.html +1 -1
- package/types/data.d.ts +3 -1
- package/types/index.d.ts +6 -1
- package/types/layout.d.ts +13 -6
package/build/clarity.module.js
CHANGED
|
@@ -111,7 +111,7 @@ function stop$B() {
|
|
|
111
111
|
startTime = 0;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
var version$1 = "0.6.
|
|
114
|
+
var version$1 = "0.6.40";
|
|
115
115
|
|
|
116
116
|
// tslint:disable: no-bitwise
|
|
117
117
|
function hash (input) {
|
|
@@ -138,9 +138,9 @@ var buffer = null;
|
|
|
138
138
|
var update$2 = false;
|
|
139
139
|
function start$E() {
|
|
140
140
|
update$2 = false;
|
|
141
|
-
reset$
|
|
141
|
+
reset$p();
|
|
142
142
|
}
|
|
143
|
-
function reset$
|
|
143
|
+
function reset$p() {
|
|
144
144
|
// Baseline state holds the previous values - if it is updated in the current payload,
|
|
145
145
|
// reset the state to current value after sending the previous state
|
|
146
146
|
if (update$2) {
|
|
@@ -208,14 +208,14 @@ function compute$c() {
|
|
|
208
208
|
}
|
|
209
209
|
}
|
|
210
210
|
function stop$A() {
|
|
211
|
-
reset$
|
|
211
|
+
reset$p();
|
|
212
212
|
}
|
|
213
213
|
|
|
214
214
|
var baseline = /*#__PURE__*/Object.freeze({
|
|
215
215
|
__proto__: null,
|
|
216
216
|
get state () { return state$9; },
|
|
217
217
|
start: start$E,
|
|
218
|
-
reset: reset$
|
|
218
|
+
reset: reset$p,
|
|
219
219
|
track: track$7,
|
|
220
220
|
activity: activity,
|
|
221
221
|
visibility: visibility,
|
|
@@ -285,7 +285,7 @@ function max(metric, value) {
|
|
|
285
285
|
function compute$b() {
|
|
286
286
|
encode$1(0 /* Event.Metric */);
|
|
287
287
|
}
|
|
288
|
-
function reset$
|
|
288
|
+
function reset$o() {
|
|
289
289
|
updates$3 = {};
|
|
290
290
|
}
|
|
291
291
|
|
|
@@ -304,7 +304,7 @@ function start$C() {
|
|
|
304
304
|
interval = 60000 /* Setting.PingInterval */;
|
|
305
305
|
last = 0;
|
|
306
306
|
}
|
|
307
|
-
function reset$
|
|
307
|
+
function reset$n() {
|
|
308
308
|
if (timeout$6) {
|
|
309
309
|
clearTimeout(timeout$6);
|
|
310
310
|
}
|
|
@@ -332,7 +332,7 @@ var ping$1 = /*#__PURE__*/Object.freeze({
|
|
|
332
332
|
__proto__: null,
|
|
333
333
|
get data () { return data$h; },
|
|
334
334
|
start: start$C,
|
|
335
|
-
reset: reset$
|
|
335
|
+
reset: reset$n,
|
|
336
336
|
stop: stop$y
|
|
337
337
|
});
|
|
338
338
|
|
|
@@ -363,7 +363,7 @@ function track$6(event, time) {
|
|
|
363
363
|
function compute$a() {
|
|
364
364
|
encode$1(36 /* Event.Summary */);
|
|
365
365
|
}
|
|
366
|
-
function reset$
|
|
366
|
+
function reset$m() {
|
|
367
367
|
data$g = {};
|
|
368
368
|
}
|
|
369
369
|
|
|
@@ -374,7 +374,7 @@ var summary = /*#__PURE__*/Object.freeze({
|
|
|
374
374
|
stop: stop$x,
|
|
375
375
|
track: track$6,
|
|
376
376
|
compute: compute$a,
|
|
377
|
-
reset: reset$
|
|
377
|
+
reset: reset$m
|
|
378
378
|
});
|
|
379
379
|
|
|
380
380
|
var data$f = null;
|
|
@@ -416,7 +416,7 @@ var upgrade$1 = /*#__PURE__*/Object.freeze({
|
|
|
416
416
|
|
|
417
417
|
var data$e = null;
|
|
418
418
|
function start$z() {
|
|
419
|
-
reset$
|
|
419
|
+
reset$l();
|
|
420
420
|
}
|
|
421
421
|
function set(variable, value) {
|
|
422
422
|
var values = typeof value === "string" /* Constant.String */ ? [value] : value;
|
|
@@ -447,11 +447,11 @@ function log$2(variable, value) {
|
|
|
447
447
|
function compute$9() {
|
|
448
448
|
encode$1(34 /* Event.Variable */);
|
|
449
449
|
}
|
|
450
|
-
function reset$
|
|
450
|
+
function reset$l() {
|
|
451
451
|
data$e = {};
|
|
452
452
|
}
|
|
453
453
|
function stop$v() {
|
|
454
|
-
reset$
|
|
454
|
+
reset$l();
|
|
455
455
|
}
|
|
456
456
|
|
|
457
457
|
var variable = /*#__PURE__*/Object.freeze({
|
|
@@ -461,7 +461,7 @@ var variable = /*#__PURE__*/Object.freeze({
|
|
|
461
461
|
set: set,
|
|
462
462
|
identify: identify,
|
|
463
463
|
compute: compute$9,
|
|
464
|
-
reset: reset$
|
|
464
|
+
reset: reset$l,
|
|
465
465
|
stop: stop$v
|
|
466
466
|
});
|
|
467
467
|
|
|
@@ -600,6 +600,11 @@ function compute$8() {
|
|
|
600
600
|
compute$4();
|
|
601
601
|
}
|
|
602
602
|
|
|
603
|
+
var catchallRegex = /\S/gi;
|
|
604
|
+
var unicodeRegex = true;
|
|
605
|
+
var digitRegex = null;
|
|
606
|
+
var letterRegex = null;
|
|
607
|
+
var currencyRegex = null;
|
|
603
608
|
function scrub (value, hint, privacy, mangle) {
|
|
604
609
|
if (mangle === void 0) { mangle = false; }
|
|
605
610
|
if (value) {
|
|
@@ -611,9 +616,9 @@ function scrub (value, hint, privacy, mangle) {
|
|
|
611
616
|
case "*T" /* Layout.Constant.TextTag */:
|
|
612
617
|
case "value":
|
|
613
618
|
case "placeholder":
|
|
614
|
-
|
|
619
|
+
case "click":
|
|
615
620
|
case "input":
|
|
616
|
-
return
|
|
621
|
+
return redact(value);
|
|
617
622
|
}
|
|
618
623
|
return value;
|
|
619
624
|
case 2 /* Privacy.Text */:
|
|
@@ -650,7 +655,7 @@ function mangleText(value) {
|
|
|
650
655
|
return value;
|
|
651
656
|
}
|
|
652
657
|
function mask(value) {
|
|
653
|
-
return value.replace(
|
|
658
|
+
return value.replace(catchallRegex, "\u2022" /* Data.Constant.Mask */);
|
|
654
659
|
}
|
|
655
660
|
function mangleToken(value) {
|
|
656
661
|
var length = ((Math.floor(value.length / 5 /* Data.Setting.WordLength */) + 1) * 5 /* Data.Setting.WordLength */);
|
|
@@ -662,10 +667,23 @@ function mangleToken(value) {
|
|
|
662
667
|
}
|
|
663
668
|
function redact(value) {
|
|
664
669
|
var spaceIndex = -1;
|
|
670
|
+
var gap = 0;
|
|
665
671
|
var hasDigit = false;
|
|
666
672
|
var hasEmail = false;
|
|
667
673
|
var hasWhitespace = false;
|
|
668
674
|
var array = null;
|
|
675
|
+
// Initialize unicode regex, if supported by the browser
|
|
676
|
+
// Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Unicode_Property_Escapes
|
|
677
|
+
if (unicodeRegex && digitRegex === null) {
|
|
678
|
+
try {
|
|
679
|
+
digitRegex = new RegExp("\\p{N}", "gu");
|
|
680
|
+
letterRegex = new RegExp("\\p{L}", "gu");
|
|
681
|
+
currencyRegex = new RegExp("\\p{Sc}", "gu");
|
|
682
|
+
}
|
|
683
|
+
catch (_a) {
|
|
684
|
+
unicodeRegex = false;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
669
687
|
for (var i = 0; i < value.length; i++) {
|
|
670
688
|
var c = value.charCodeAt(i);
|
|
671
689
|
hasDigit = hasDigit || (c >= 48 /* Data.Character.Zero */ && c <= 57 /* Data.Character.Nine */); // Check for digits in the current word
|
|
@@ -678,7 +696,19 @@ function redact(value) {
|
|
|
678
696
|
if (array === null) {
|
|
679
697
|
array = value.split("" /* Data.Constant.Empty */);
|
|
680
698
|
}
|
|
681
|
-
|
|
699
|
+
// Work on a token at a time so we don't have to apply regex to a larger string
|
|
700
|
+
var token = value.substring(spaceIndex + 1, hasWhitespace ? i : i + 1);
|
|
701
|
+
// Check if unicode regex is supported, otherwise fallback to calling mask function on this token
|
|
702
|
+
if (unicodeRegex && currencyRegex !== null) {
|
|
703
|
+
// Do not redact information if the token contains a currency symbol
|
|
704
|
+
token = token.match(currencyRegex) ? token : token.replace(letterRegex, "\u2022" /* Data.Constant.Letter */).replace(digitRegex, "\u2022" /* Data.Constant.Digit */);
|
|
705
|
+
}
|
|
706
|
+
else {
|
|
707
|
+
token = mask(token);
|
|
708
|
+
}
|
|
709
|
+
// Merge token back into array at the right place
|
|
710
|
+
array.splice(spaceIndex + 1 - gap, token.length, token);
|
|
711
|
+
gap += token.length - 1;
|
|
682
712
|
}
|
|
683
713
|
// Reset digit and email flags after every word boundary, except the beginning of string
|
|
684
714
|
if (hasWhitespace) {
|
|
@@ -689,11 +719,6 @@ function redact(value) {
|
|
|
689
719
|
}
|
|
690
720
|
}
|
|
691
721
|
return array ? array.join("" /* Data.Constant.Empty */) : value;
|
|
692
|
-
}
|
|
693
|
-
function mutate(array, start, end) {
|
|
694
|
-
for (var i = start + 1; i < end; i++) {
|
|
695
|
-
array[i] = "\u2022" /* Data.Constant.Mask */;
|
|
696
|
-
}
|
|
697
722
|
}
|
|
698
723
|
|
|
699
724
|
var history$5 = [];
|
|
@@ -714,12 +739,15 @@ function check$4(id, target, input) {
|
|
|
714
739
|
}
|
|
715
740
|
}
|
|
716
741
|
|
|
717
|
-
var
|
|
718
|
-
|
|
719
|
-
|
|
742
|
+
var excludeClassNames = "load,active,fixed,visible,focus,show,collaps,animat" /* Constant.ExcludeClassNames */.split("," /* Constant.Comma */);
|
|
743
|
+
var selectorMap = {};
|
|
744
|
+
function reset$k() {
|
|
745
|
+
selectorMap = {};
|
|
746
|
+
}
|
|
747
|
+
function get$1(input, type) {
|
|
720
748
|
var a = input.attributes;
|
|
721
|
-
var prefix = input.prefix ? input.prefix[
|
|
722
|
-
var suffix =
|
|
749
|
+
var prefix = input.prefix ? input.prefix[type] : null;
|
|
750
|
+
var suffix = type === 0 /* Selector.Alpha */ ? "".concat("~" /* Constant.Tilde */).concat(input.position - 1) : ":nth-of-type(".concat(input.position, ")");
|
|
723
751
|
switch (input.tag) {
|
|
724
752
|
case "STYLE":
|
|
725
753
|
case "TITLE":
|
|
@@ -734,23 +762,33 @@ function selector (input, beta) {
|
|
|
734
762
|
if (prefix === null) {
|
|
735
763
|
return "" /* Constant.Empty */;
|
|
736
764
|
}
|
|
737
|
-
prefix = "".concat(prefix
|
|
765
|
+
prefix = "".concat(prefix).concat(">" /* Constant.Separator */);
|
|
738
766
|
input.tag = input.tag.indexOf("svg:" /* Constant.SvgPrefix */) === 0 ? input.tag.substr("svg:" /* Constant.SvgPrefix */.length) : input.tag;
|
|
739
767
|
var selector = "".concat(prefix).concat(input.tag).concat(suffix);
|
|
740
|
-
var
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
768
|
+
var id = "id" /* Constant.Id */ in a && a["id" /* Constant.Id */].length > 0 ? a["id" /* Constant.Id */] : null;
|
|
769
|
+
var classes = input.tag !== "BODY" /* Constant.BodyTag */ && "class" /* Constant.Class */ in a && a["class" /* Constant.Class */].length > 0 ? a["class" /* Constant.Class */].trim().split(/\s+/).filter(function (c) { return filter(c); }).join("." /* Constant.Period */) : null;
|
|
770
|
+
if (classes && classes.length > 0) {
|
|
771
|
+
if (type === 0 /* Selector.Alpha */) {
|
|
772
|
+
// In Alpha mode, update selector to use class names, with relative positioning within the parent id container.
|
|
773
|
+
// If the node has valid class name(s) then drop relative positioning within the parent path to keep things simple.
|
|
774
|
+
var key = "".concat(getDomPath(prefix)).concat(input.tag).concat("." /* Constant.Dot */).concat(classes);
|
|
775
|
+
if (!(key in selectorMap)) {
|
|
776
|
+
selectorMap[key] = [];
|
|
777
|
+
}
|
|
778
|
+
if (selectorMap[key].indexOf(input.id) < 0) {
|
|
779
|
+
selectorMap[key].push(input.id);
|
|
780
|
+
}
|
|
781
|
+
selector = "".concat(key).concat("~" /* Constant.Tilde */).concat(selectorMap[key].indexOf(input.id));
|
|
782
|
+
}
|
|
783
|
+
else {
|
|
784
|
+
// In Beta mode, we continue to look at query selectors in context of the full page
|
|
785
|
+
selector = "".concat(prefix).concat(input.tag, ".").concat(classes).concat(suffix);
|
|
786
|
+
}
|
|
753
787
|
}
|
|
788
|
+
// Update selector to use "id" field when available. There are two exceptions:
|
|
789
|
+
// (1) if "id" appears to be an auto generated string token, e.g. guid or a random id containing digits
|
|
790
|
+
// (2) if "id" appears inside a shadow DOM, in which case we continue to prefix up to shadow DOM to prevent conflicts
|
|
791
|
+
selector = id && filter(id) ? "".concat(getDomPrefix(prefix)).concat("#" /* Constant.Hash */).concat(id) : selector;
|
|
754
792
|
return selector;
|
|
755
793
|
}
|
|
756
794
|
}
|
|
@@ -759,22 +797,42 @@ function getDomPrefix(prefix) {
|
|
|
759
797
|
var iframeDomStart = prefix.lastIndexOf("".concat("iframe:" /* Constant.IFramePrefix */).concat("HTML" /* Constant.HTML */));
|
|
760
798
|
var domStart = Math.max(shadowDomStart, iframeDomStart);
|
|
761
799
|
if (domStart < 0) {
|
|
762
|
-
return ""
|
|
800
|
+
return "" /* Constant.Empty */;
|
|
801
|
+
}
|
|
802
|
+
return prefix.substring(0, prefix.indexOf(">" /* Constant.Separator */, domStart) + 1);
|
|
803
|
+
}
|
|
804
|
+
function getDomPath(input) {
|
|
805
|
+
var parts = input.split(">" /* Constant.Separator */);
|
|
806
|
+
for (var i = 0; i < parts.length; i++) {
|
|
807
|
+
var tIndex = parts[i].indexOf("~" /* Constant.Tilde */);
|
|
808
|
+
var dIndex = parts[i].indexOf("." /* Constant.Dot */);
|
|
809
|
+
parts[i] = parts[i].substring(0, dIndex > 0 ? dIndex : (tIndex > 0 ? tIndex : parts[i].length));
|
|
763
810
|
}
|
|
764
|
-
|
|
765
|
-
return prefix.substr(0, domEnd);
|
|
811
|
+
return parts.join(">" /* Constant.Separator */);
|
|
766
812
|
}
|
|
767
|
-
// Check if the given input string has digits or
|
|
768
|
-
function
|
|
813
|
+
// Check if the given input string has digits or excluded class names
|
|
814
|
+
function filter(value) {
|
|
815
|
+
if (!value) {
|
|
816
|
+
return false;
|
|
817
|
+
} // Do not process empty strings
|
|
818
|
+
if (excludeClassNames.some(function (x) { return value.toLowerCase().indexOf(x) >= 0; })) {
|
|
819
|
+
return false;
|
|
820
|
+
}
|
|
769
821
|
for (var i = 0; i < value.length; i++) {
|
|
770
822
|
var c = value.charCodeAt(i);
|
|
771
823
|
if (c >= 48 /* Character.Zero */ && c <= 57 /* Character.Nine */) {
|
|
772
|
-
return
|
|
824
|
+
return false;
|
|
773
825
|
}
|
|
774
826
|
}
|
|
775
|
-
return
|
|
827
|
+
return true;
|
|
776
828
|
}
|
|
777
829
|
|
|
830
|
+
var selector = /*#__PURE__*/Object.freeze({
|
|
831
|
+
__proto__: null,
|
|
832
|
+
reset: reset$k,
|
|
833
|
+
get: get$1
|
|
834
|
+
});
|
|
835
|
+
|
|
778
836
|
// Track the start time to be able to compute duration at the end of the task
|
|
779
837
|
var idleTimeout = 5000;
|
|
780
838
|
var tracker = {};
|
|
@@ -1094,7 +1152,7 @@ function encode$4 (type, timer, ts) {
|
|
|
1094
1152
|
}
|
|
1095
1153
|
tokens.push(suspend ? "*M" /* Constant.SuspendMutationTag */ : data[key]);
|
|
1096
1154
|
if (box && box.length === 2) {
|
|
1097
|
-
tokens.push("".concat("#" /* Constant.
|
|
1155
|
+
tokens.push("".concat("#" /* Constant.Hash */).concat(str$1(box[0]), ".").concat(str$1(box[1])));
|
|
1098
1156
|
}
|
|
1099
1157
|
break;
|
|
1100
1158
|
case "attributes":
|
|
@@ -1385,7 +1443,7 @@ function observe$8(root) {
|
|
|
1385
1443
|
bind(root, "mousedown", mouse.bind(this, 13 /* Event.MouseDown */, root), true);
|
|
1386
1444
|
bind(root, "mouseup", mouse.bind(this, 14 /* Event.MouseUp */, root), true);
|
|
1387
1445
|
bind(root, "mousemove", mouse.bind(this, 12 /* Event.MouseMove */, root), true);
|
|
1388
|
-
bind(root, "
|
|
1446
|
+
bind(root, "wheel", mouse.bind(this, 15 /* Event.MouseWheel */, root), true);
|
|
1389
1447
|
bind(root, "dblclick", mouse.bind(this, 16 /* Event.DoubleClick */, root), true);
|
|
1390
1448
|
bind(root, "touchstart", touch.bind(this, 17 /* Event.TouchStart */, root), true);
|
|
1391
1449
|
bind(root, "touchend", touch.bind(this, 18 /* Event.TouchEnd */, root), true);
|
|
@@ -2319,6 +2377,7 @@ var override = [];
|
|
|
2319
2377
|
var unmask = [];
|
|
2320
2378
|
var updatedFragments = {};
|
|
2321
2379
|
var maskText = [];
|
|
2380
|
+
var maskInput = [];
|
|
2322
2381
|
var maskDisable = [];
|
|
2323
2382
|
// The WeakMap object is a collection of key/value pairs in which the keys are weakly referenced
|
|
2324
2383
|
var idMap = null; // Maps node => id.
|
|
@@ -2340,12 +2399,14 @@ function reset$7() {
|
|
|
2340
2399
|
hashMap = {};
|
|
2341
2400
|
override = [];
|
|
2342
2401
|
unmask = [];
|
|
2343
|
-
maskText = "password,
|
|
2402
|
+
maskText = "address,password,contact" /* Mask.Text */.split("," /* Constant.Comma */);
|
|
2403
|
+
maskInput = "password,secret,pass,social,ssn,name,code,dob,cell,mob,contact,hidden,account,cvv,ccv,email,tel,phone,address,addr,card,zip" /* Mask.Input */.split("," /* Constant.Comma */);
|
|
2344
2404
|
maskDisable = "radio,checkbox,range,button,reset,submit" /* Mask.Disable */.split("," /* Constant.Comma */);
|
|
2345
2405
|
idMap = new WeakMap();
|
|
2346
2406
|
iframeMap = new WeakMap();
|
|
2347
2407
|
privacyMap = new WeakMap();
|
|
2348
2408
|
fraudMap = new WeakMap();
|
|
2409
|
+
reset$k();
|
|
2349
2410
|
}
|
|
2350
2411
|
// We parse new root nodes for any regions or masked nodes in the beginning (document) and
|
|
2351
2412
|
// later whenever there are new additions or modifications to DOM (mutations)
|
|
@@ -2522,36 +2583,36 @@ function privacy(node, value, parent) {
|
|
|
2522
2583
|
metadata.privacy = 2 /* Privacy.Text */;
|
|
2523
2584
|
break;
|
|
2524
2585
|
case tag === "*T" /* Constant.TextTag */:
|
|
2525
|
-
// If it's a text node belonging to a STYLE or TITLE tag or one of
|
|
2586
|
+
// If it's a text node belonging to a STYLE or TITLE tag or one of scrub exceptions, then capture content
|
|
2526
2587
|
var pTag = parent && parent.data ? parent.data.tag : "" /* Constant.Empty */;
|
|
2527
|
-
var pSelector_1 = parent && parent.selector ? parent.selector[
|
|
2528
|
-
|
|
2588
|
+
var pSelector_1 = parent && parent.selector ? parent.selector[1 /* Selector.Default */] : "" /* Constant.Empty */;
|
|
2589
|
+
var tags = ["STYLE" /* Constant.StyleTag */, "TITLE" /* Constant.TitleTag */, "svg:style" /* Constant.SvgStyle */];
|
|
2590
|
+
metadata.privacy = tags.includes(pTag) || override.some(function (x) { return pSelector_1.indexOf(x) >= 0; }) ? 0 /* Privacy.None */ : current;
|
|
2529
2591
|
break;
|
|
2530
2592
|
case "type" /* Constant.Type */ in attributes:
|
|
2531
2593
|
// If this node has an explicit type assigned to it, go through masking rules to determine right privacy setting
|
|
2532
|
-
metadata.privacy = inspect(attributes["type" /* Constant.Type */], metadata);
|
|
2594
|
+
metadata.privacy = inspect(attributes["type" /* Constant.Type */], maskInput, metadata);
|
|
2533
2595
|
break;
|
|
2534
2596
|
case tag === "INPUT" /* Constant.InputTag */ && current === 0 /* Privacy.None */:
|
|
2535
2597
|
// If even default privacy setting is to not mask, we still scan through input fields for any sensitive information
|
|
2536
2598
|
var field_1 = "" /* Constant.Empty */;
|
|
2537
2599
|
Object.keys(attributes).forEach(function (x) { return field_1 += attributes[x].toLowerCase(); });
|
|
2538
|
-
metadata.privacy = inspect(field_1, metadata);
|
|
2600
|
+
metadata.privacy = inspect(field_1, maskInput, metadata);
|
|
2539
2601
|
break;
|
|
2540
2602
|
case current === 1 /* Privacy.Sensitive */ && tag === "INPUT" /* Constant.InputTag */:
|
|
2603
|
+
// Look through class names to aggressively mask content
|
|
2604
|
+
metadata.privacy = inspect(attributes["class" /* Constant.Class */], maskText, metadata);
|
|
2541
2605
|
// If it's a button or an input option, make an exception to disable masking
|
|
2542
2606
|
metadata.privacy = maskDisable.indexOf(attributes["type" /* Constant.Type */]) >= 0 ? 0 /* Privacy.None */ : current;
|
|
2543
2607
|
break;
|
|
2544
2608
|
case current === 1 /* Privacy.Sensitive */:
|
|
2545
2609
|
// In a mode where we mask sensitive information by default, look through class names to aggressively mask content
|
|
2546
|
-
metadata.privacy = inspect(attributes["class" /* Constant.Class */], metadata);
|
|
2547
|
-
break;
|
|
2548
|
-
default:
|
|
2549
|
-
metadata.privacy = parent ? parent.metadata.privacy : metadata.privacy;
|
|
2610
|
+
metadata.privacy = inspect(attributes["class" /* Constant.Class */], maskText, metadata);
|
|
2550
2611
|
break;
|
|
2551
2612
|
}
|
|
2552
2613
|
}
|
|
2553
|
-
function inspect(input, metadata) {
|
|
2554
|
-
if (input &&
|
|
2614
|
+
function inspect(input, lookup, metadata) {
|
|
2615
|
+
if (input && lookup.some(function (x) { return input.indexOf(x) >= 0; })) {
|
|
2555
2616
|
return 2 /* Privacy.Text */;
|
|
2556
2617
|
}
|
|
2557
2618
|
return metadata.privacy;
|
|
@@ -2589,10 +2650,11 @@ function updateSelector(value) {
|
|
|
2589
2650
|
var prefix = parent ? parent.selector : null;
|
|
2590
2651
|
var d = value.data;
|
|
2591
2652
|
var p = position(parent, value);
|
|
2592
|
-
var s = { tag: d.tag, prefix: prefix, position: p, attributes: d.attributes };
|
|
2593
|
-
value.selector = [
|
|
2653
|
+
var s = { id: value.id, tag: d.tag, prefix: prefix, position: p, attributes: d.attributes };
|
|
2654
|
+
value.selector = [get$1(s, 0 /* Selector.Alpha */), get$1(s, 1 /* Selector.Beta */)];
|
|
2594
2655
|
value.hash = value.selector.map(function (x) { return x ? hash(x) : null; });
|
|
2595
2656
|
value.hash.forEach(function (h) { return hashMap[h] = value.id; });
|
|
2657
|
+
// Match fragment configuration against both alpha and beta hash
|
|
2596
2658
|
if (value.hash.some(function (h) { return fragments.indexOf(h) !== -1; })) {
|
|
2597
2659
|
value.fragment = value.id;
|
|
2598
2660
|
}
|
|
@@ -3136,7 +3198,7 @@ function queue(tokens, transmit) {
|
|
|
3136
3198
|
// We enrich the data going out with the existing upload. In these cases, call to upload comes with 'transmit' set to false.
|
|
3137
3199
|
if (transmit && timeout === null) {
|
|
3138
3200
|
if (type !== 25 /* Event.Ping */) {
|
|
3139
|
-
reset$
|
|
3201
|
+
reset$n();
|
|
3140
3202
|
}
|
|
3141
3203
|
timeout = setTimeout(upload, gap);
|
|
3142
3204
|
queuedTime = now;
|
|
@@ -3591,7 +3653,7 @@ function encode$1 (event) {
|
|
|
3591
3653
|
tokens.push(b.data.activityTime);
|
|
3592
3654
|
queue(tokens, false);
|
|
3593
3655
|
}
|
|
3594
|
-
reset$
|
|
3656
|
+
reset$p();
|
|
3595
3657
|
break;
|
|
3596
3658
|
case 25 /* Event.Ping */:
|
|
3597
3659
|
tokens.push(data$h.gap);
|
|
@@ -3624,7 +3686,7 @@ function encode$1 (event) {
|
|
|
3624
3686
|
tokens.push(v);
|
|
3625
3687
|
tokens.push(data$e[v]);
|
|
3626
3688
|
}
|
|
3627
|
-
reset$
|
|
3689
|
+
reset$l();
|
|
3628
3690
|
queue(tokens, false);
|
|
3629
3691
|
}
|
|
3630
3692
|
break;
|
|
@@ -3639,7 +3701,7 @@ function encode$1 (event) {
|
|
|
3639
3701
|
// However, for data over the wire, we round it off to milliseconds precision.
|
|
3640
3702
|
tokens.push(Math.round(updates$3[m]));
|
|
3641
3703
|
}
|
|
3642
|
-
reset$
|
|
3704
|
+
reset$o();
|
|
3643
3705
|
queue(tokens, false);
|
|
3644
3706
|
}
|
|
3645
3707
|
break;
|
|
@@ -3665,7 +3727,7 @@ function encode$1 (event) {
|
|
|
3665
3727
|
tokens.push(key);
|
|
3666
3728
|
tokens.push([].concat.apply([], data$g[e]));
|
|
3667
3729
|
}
|
|
3668
|
-
reset$
|
|
3730
|
+
reset$m();
|
|
3669
3731
|
queue(tokens, false);
|
|
3670
3732
|
}
|
|
3671
3733
|
break;
|
package/package.json
CHANGED
package/src/core/scrub.ts
CHANGED
|
@@ -2,6 +2,12 @@ import { Privacy } from "@clarity-types/core";
|
|
|
2
2
|
import * as Data from "@clarity-types/data";
|
|
3
3
|
import * as Layout from "@clarity-types/layout";
|
|
4
4
|
|
|
5
|
+
const catchallRegex = /\S/gi;
|
|
6
|
+
let unicodeRegex = true;
|
|
7
|
+
let digitRegex = null;
|
|
8
|
+
let letterRegex = null;
|
|
9
|
+
let currencyRegex = null;
|
|
10
|
+
|
|
5
11
|
export default function(value: string, hint: string, privacy: Privacy, mangle: boolean = false): string {
|
|
6
12
|
if (value) {
|
|
7
13
|
switch (privacy) {
|
|
@@ -12,9 +18,9 @@ export default function(value: string, hint: string, privacy: Privacy, mangle: b
|
|
|
12
18
|
case Layout.Constant.TextTag:
|
|
13
19
|
case "value":
|
|
14
20
|
case "placeholder":
|
|
15
|
-
|
|
21
|
+
case "click":
|
|
16
22
|
case "input":
|
|
17
|
-
return
|
|
23
|
+
return redact(value);
|
|
18
24
|
}
|
|
19
25
|
return value;
|
|
20
26
|
case Privacy.Text:
|
|
@@ -53,7 +59,7 @@ function mangleText(value: string): string {
|
|
|
53
59
|
}
|
|
54
60
|
|
|
55
61
|
function mask(value: string): string {
|
|
56
|
-
return value.replace(
|
|
62
|
+
return value.replace(catchallRegex, Data.Constant.Mask);
|
|
57
63
|
}
|
|
58
64
|
|
|
59
65
|
function mangleToken(value: string): string {
|
|
@@ -67,10 +73,22 @@ function mangleToken(value: string): string {
|
|
|
67
73
|
|
|
68
74
|
function redact(value: string): string {
|
|
69
75
|
let spaceIndex = -1;
|
|
76
|
+
let gap = 0;
|
|
70
77
|
let hasDigit = false;
|
|
71
78
|
let hasEmail = false;
|
|
72
79
|
let hasWhitespace = false;
|
|
73
80
|
let array = null;
|
|
81
|
+
|
|
82
|
+
// Initialize unicode regex, if supported by the browser
|
|
83
|
+
// Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Unicode_Property_Escapes
|
|
84
|
+
if (unicodeRegex && digitRegex === null) {
|
|
85
|
+
try {
|
|
86
|
+
digitRegex = new RegExp("\\p{N}", "gu");
|
|
87
|
+
letterRegex = new RegExp("\\p{L}", "gu");
|
|
88
|
+
currencyRegex = new RegExp("\\p{Sc}", "gu");
|
|
89
|
+
} catch { unicodeRegex = false; }
|
|
90
|
+
}
|
|
91
|
+
|
|
74
92
|
for (let i = 0; i < value.length; i++) {
|
|
75
93
|
let c = value.charCodeAt(i);
|
|
76
94
|
hasDigit = hasDigit || (c >= Data.Character.Zero && c <= Data.Character.Nine); // Check for digits in the current word
|
|
@@ -82,7 +100,18 @@ function redact(value: string): string {
|
|
|
82
100
|
// Performance optimization: Lazy load string -> array conversion only when required
|
|
83
101
|
if (hasDigit || hasEmail) {
|
|
84
102
|
if (array === null) { array = value.split(Data.Constant.Empty); }
|
|
85
|
-
|
|
103
|
+
// Work on a token at a time so we don't have to apply regex to a larger string
|
|
104
|
+
let token = value.substring(spaceIndex + 1, hasWhitespace ? i : i + 1);
|
|
105
|
+
// Check if unicode regex is supported, otherwise fallback to calling mask function on this token
|
|
106
|
+
if (unicodeRegex && currencyRegex !== null) {
|
|
107
|
+
// Do not redact information if the token contains a currency symbol
|
|
108
|
+
token = token.match(currencyRegex) ? token : token.replace(letterRegex, Data.Constant.Letter).replace(digitRegex, Data.Constant.Digit);
|
|
109
|
+
} else {
|
|
110
|
+
token = mask(token);
|
|
111
|
+
}
|
|
112
|
+
// Merge token back into array at the right place
|
|
113
|
+
array.splice(spaceIndex + 1 - gap, token.length, token);
|
|
114
|
+
gap += token.length - 1;
|
|
86
115
|
}
|
|
87
116
|
// Reset digit and email flags after every word boundary, except the beginning of string
|
|
88
117
|
if (hasWhitespace) {
|
|
@@ -94,9 +123,3 @@ function redact(value: string): string {
|
|
|
94
123
|
}
|
|
95
124
|
return array ? array.join(Data.Constant.Empty) : value;
|
|
96
125
|
}
|
|
97
|
-
|
|
98
|
-
function mutate(array: string[], start: number, end: number): void {
|
|
99
|
-
for (let i = start + 1; i < end; i++) {
|
|
100
|
-
array[i] = Data.Constant.Mask;
|
|
101
|
-
}
|
|
102
|
-
}
|
package/src/core/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
let version = "0.6.
|
|
1
|
+
let version = "0.6.40";
|
|
2
2
|
export default version;
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as clarity from "./clarity";
|
|
2
2
|
import hash from "./core/hash";
|
|
3
|
-
import selector from "./layout/selector";
|
|
3
|
+
import * as selector from "./layout/selector";
|
|
4
4
|
import { get, getNode, lookup } from "./layout/dom";
|
|
5
5
|
|
|
6
6
|
const helper = { hash, selector, get, getNode, lookup }
|
|
@@ -20,7 +20,7 @@ export function observe(root: Node): void {
|
|
|
20
20
|
bind(root, "mousedown", mouse.bind(this, Event.MouseDown, root), true);
|
|
21
21
|
bind(root, "mouseup", mouse.bind(this, Event.MouseUp, root), true);
|
|
22
22
|
bind(root, "mousemove", mouse.bind(this, Event.MouseMove, root), true);
|
|
23
|
-
bind(root, "
|
|
23
|
+
bind(root, "wheel", mouse.bind(this, Event.MouseWheel, root), true);
|
|
24
24
|
bind(root, "dblclick", mouse.bind(this, Event.DoubleClick, root), true);
|
|
25
25
|
bind(root, "touchstart", touch.bind(this, Event.TouchStart, root), true);
|
|
26
26
|
bind(root, "touchend", touch.bind(this, Event.TouchEnd, root), true);
|