clarity-js 0.6.43 → 0.7.0

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/test/core.test.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { assert } from 'chai';
2
2
  import { Browser, Page } from 'playwright';
3
- import { clicks, inputs, launch, markup, node, text } from './helper';
4
- import { Data, decode } from "clarity-decode";
3
+ import { changes, clicks, inputs, launch, markup, node, text } from './helper';
4
+ import { decode } from "clarity-decode";
5
5
 
6
6
  let browser: Browser;
7
7
  let page: Page;
@@ -34,8 +34,10 @@ describe('Core Tests', () => {
34
34
  let password = node(decoded, "attributes.id", "pwd");
35
35
  let search = node(decoded, "attributes.id", "search");
36
36
  let card = node(decoded, "attributes.id", "cardnum");
37
+ let textarea = text(decoded, "textarea");
37
38
  let click = clicks(decoded)[0];
38
39
  let input = inputs(decoded)[0];
40
+ let group = changes(decoded);
39
41
 
40
42
  // Non-sensitive fields continue to pass through with sensitive bits masked off
41
43
  assert.equal(heading, "Thanks for your order #▫▪▪▫▫▫▪▪");
@@ -43,13 +45,23 @@ describe('Core Tests', () => {
43
45
  // Sensitive fields, including input fields, are randomized and masked
44
46
  assert.equal(address, "•••••• ••••• ••••• ••••• ••••• •••••");
45
47
  assert.equal(email.attributes.value, "••••• •••• •••• ••••");
46
- assert.equal(password.attributes.value, "••••• ••••");
47
- assert.equal(search.attributes.value, "hello ▪▫▪▪▪");
48
- assert.equal(card.attributes.value, "▫▫▫▫");
48
+ assert.equal(password.attributes.value, "••••");
49
+ assert.equal(search.attributes.value, "••••• •••• ••••");
50
+ assert.equal(card.attributes.value, "•••••");
51
+ assert.equal(textarea, "••••• •••••");
49
52
 
50
53
  // Clicked text and input value should be consistent with uber masking configuration
51
54
  assert.equal(click.data.text, "Hello ▪▪▪▫▪");
52
- assert.equal(input.data.value, "query with ▪▪▪▪▫▪▪");
55
+ assert.equal(input.data.value, "••••• •••• •••• ••••");
56
+ assert.equal(group.length, 2);
57
+ // Search change - we should captured mangled input and hash
58
+ assert.equal(group[0].data.type, "search");
59
+ assert.equal(group[0].data.value, "••••• •••• •••• ••••");
60
+ assert.equal(group[0].data.checksum, "4y7m6");
61
+ // Password change - we should capture placholder value and empty hash
62
+ assert.equal(group[1].data.type, "password");
63
+ assert.equal(group[1].data.value, "••••");
64
+ assert.equal(group[1].data.checksum, "");
53
65
  });
54
66
 
55
67
  it('should mask all text in strict mode', async () => {
@@ -68,7 +80,7 @@ describe('Core Tests', () => {
68
80
  assert.equal(heading, "• ••••• ••••• ••••• ••••• •••••");
69
81
  assert.equal(address, "•••••• ••••• ••••• ••••• ••••• •••••");
70
82
  assert.equal(email.attributes.value, "••••• •••• •••• ••••");
71
- assert.equal(password.attributes.value, "••••• ••••");
83
+ assert.equal(password.attributes.value, "••••");
72
84
  assert.equal(search.attributes.value, "••••• •••• ••••");
73
85
  assert.equal(card.attributes.value, "•••••");
74
86
 
@@ -89,19 +101,19 @@ describe('Core Tests', () => {
89
101
  let click = clicks(decoded)[0];
90
102
  let input = inputs(decoded)[0];
91
103
 
92
- // Text flows through unmasked for non-sensitive fields, including input fields
104
+ // Text flows through unmasked for non-sensitive fields, with exception of input fields
93
105
  assert.equal(heading, "Thanks for your order #2AB700GH");
94
106
  assert.equal(address, "1 Microsoft Way, Redmond, WA - 98052");
95
- assert.equal(search.attributes.value, "hello w0rld");
107
+ assert.equal(search.attributes.value, "••••• •••• ••••");
96
108
 
97
109
  // Sensitive fields are still masked
98
110
  assert.equal(email.attributes.value, "••••• •••• •••• ••••");
99
- assert.equal(password.attributes.value, "••••• ••••");
111
+ assert.equal(password.attributes.value, "••••");
100
112
  assert.equal(card.attributes.value, "•••••");
101
113
 
102
- // Clicked text and input value (non-sensitive) both come through without masking in relaxed mode
114
+ // Clicked text comes through unmasked in relaxed mode but input is still masked
103
115
  assert.equal(click.data.text, "Hello Wor1d");
104
- assert.equal(input.data.value, "query with numb3rs");
116
+ assert.equal(input.data.value, "••••• •••• •••• ••••");
105
117
  });
106
118
 
107
119
  it('should respect mask config even in relaxed mode', async () => {
@@ -114,8 +126,8 @@ describe('Core Tests', () => {
114
126
  // Masked sub-trees continue to stay masked
115
127
  assert.equal(subtree, "••••• •••••");
116
128
 
117
- // Clicked text is masked due to masked configuration while input value is not masked in relaxed mode
129
+ // Clicked text is masked due to masked configuration and input value is also masked
118
130
  assert.equal(click.data.text, "••••• •••• ••••");
119
- assert.equal(input.data.value, "query with numb3rs");
131
+ assert.equal(input.data.value, "••••• •••• •••• ••••");
120
132
  });
121
133
  });
package/test/helper.ts CHANGED
@@ -26,7 +26,11 @@ export async function markup(page: Page, file: string, override: Core.Config = n
26
26
  `));
27
27
  await page.hover("#two");
28
28
  await page.click("#child");
29
- await page.locator('#search').fill('query with numb3rs');
29
+ await page.locator('#search').fill('');
30
+ await page.locator('#search').type('query with numb3rs');
31
+ await page.locator('#pwd').type('p1ssw0rd');
32
+ await page.locator('#eml').fill('');
33
+ await page.locator('#eml').type('hello@world.com');
30
34
  await page.waitForFunction("payloads && payloads.length > 2");
31
35
  return await page.evaluate('payloads');
32
36
  }
@@ -57,6 +61,19 @@ export function inputs(decoded: Data.DecodedPayload[]): Interaction.InputEvent[]
57
61
  return output;
58
62
  }
59
63
 
64
+ export function changes(decoded: Data.DecodedPayload[]): Interaction.ChangeEvent[] {
65
+ let output: Interaction.ChangeEvent[] = [];
66
+ for (let i = decoded.length - 1; i >= 0; i--) {
67
+ if (decoded[i].change) {
68
+ for (let j = 0; j < decoded[i].change.length;j++)
69
+ {
70
+ output.push(decoded[i].change[j]);
71
+ }
72
+ }
73
+ }
74
+ return output;
75
+ }
76
+
60
77
  export function node(decoded: Data.DecodedPayload[], key: string, value: string | number, tag: string = null): Layout.DomData {
61
78
  let sub = null;
62
79
 
@@ -2,6 +2,7 @@
2
2
  <html>
3
3
  <head>
4
4
  <title>Core Tests</title>
5
+ <style>input, textarea, select { margin: 10px; display: block; }</style>
5
6
  </head>
6
7
  <body>
7
8
  <div>
@@ -16,6 +17,7 @@
16
17
  <input type="password" id="pwd" title="Password" maxlength="16" value="passw0rd">
17
18
  <input type="search" id="search" title="Search" value="hello w0rld">
18
19
  <input type="text" id="cardnum" title="CC" value="1234">
20
+ <textarea id="textarea" autocapitalize="off" role="combobox" rows="5" placeholder="" spellcheck="false">Hell0 World</textarea>
19
21
  </form>
20
22
  </body>
21
23
  </html>
package/types/core.d.ts CHANGED
@@ -4,7 +4,7 @@ type TaskFunction = () => Promise<void>;
4
4
  type TaskResolve = () => void;
5
5
  type UploadCallback = (data: string) => void;
6
6
  type Region = [number /* RegionId */, string /* Query Selector */];
7
- type Fraud = [number /* FraudId */, string /* Query Selector */];
7
+ type Checksum = [number /* FraudId */, string /* Query Selector */];
8
8
  export type Extract = ExtractSource /* Extraction Source */ | number /* Extract Id */ | string | string[] /* Hash or Query Selector or String Token */;
9
9
 
10
10
  /* Enum */
@@ -123,12 +123,14 @@ export interface Config {
123
123
  lean?: boolean;
124
124
  track?: boolean;
125
125
  content?: boolean;
126
+ drop?: string[];
126
127
  mask?: string[];
127
128
  unmask?: string[];
128
129
  regions?: Region[];
129
130
  extract?: Extract[];
130
131
  cookies?: string[];
131
- fraud?: Fraud[];
132
+ fraud?: boolean;
133
+ checksum?: Checksum[];
132
134
  report?: string;
133
135
  upload?: string | UploadCallback;
134
136
  fallback?: string;
package/types/data.d.ts CHANGED
@@ -62,7 +62,8 @@ export const enum Event {
62
62
  Clipboard = 38,
63
63
  Submit = 39,
64
64
  Extract = 40,
65
- Fraud = 41
65
+ Fraud = 41,
66
+ Change = 42
66
67
  }
67
68
 
68
69
  export const enum Metric {
@@ -98,6 +99,9 @@ export const enum Metric {
98
99
  SinglePage = 29,
99
100
  UsedMemory = 30,
100
101
  Iframed = 31,
102
+ MaxTouchPoints = 32,
103
+ HardwareConcurrency = 33,
104
+ DeviceMemory = 34
101
105
  }
102
106
 
103
107
  export const enum Dimension {
@@ -126,7 +130,9 @@ export const enum Dimension {
126
130
  Platform = 22,
127
131
  PlatformVersion = 23,
128
132
  Brand = 24,
129
- Model = 25
133
+ Model = 25,
134
+ DevicePixelRatio = 26,
135
+ ConnectionType = 27
130
136
  }
131
137
 
132
138
  export const enum Check {
@@ -207,7 +213,8 @@ export const enum Setting {
207
213
  UploadFactor = 3, // Slow down sequence by specified factor
208
214
  MinUploadDelay = 100, // Minimum time before we are ready to flush events to the server
209
215
  MaxUploadDelay = 30 * Time.Second, // Do flush out payload once every 30s,
210
- ExtractLimit = 10000 // Do not extract more than 10000 characters
216
+ ExtractLimit = 10000, // Do not extract more than 10000 characters
217
+ ChecksumPrecision = 24 // n-bit integer to represent token hash
211
218
  }
212
219
 
213
220
  export const enum Character {
@@ -234,6 +241,7 @@ export const enum Constant {
234
241
  Space = " ",
235
242
  Expires = "expires=",
236
243
  Domain = "domain=",
244
+ Dropped = "*na*",
237
245
  Comma = ",",
238
246
  Dot = ".",
239
247
  Semicolon = ";",
@@ -20,5 +20,5 @@ export interface LogData {
20
20
  export interface FraudData {
21
21
  id: number;
22
22
  target: number;
23
- hash: string;
23
+ checksum: string;
24
24
  }
@@ -12,6 +12,7 @@ export const enum BrowsingContext {
12
12
 
13
13
  export const enum Setting {
14
14
  LookAhead = 500, // 500ms
15
+ InputLookAhead = 1000, // 1s
15
16
  Distance = 20, // 20 pixels
16
17
  Interval = 25, // 25 milliseconds
17
18
  TimelineSpan = 2 * Time.Second, // 2 seconds
@@ -54,6 +55,12 @@ export interface SubmitState {
54
55
  data: SubmitData;
55
56
  }
56
57
 
58
+ export interface ChangeState {
59
+ time: number;
60
+ event: number;
61
+ data: ChangeData;
62
+ }
63
+
57
64
  export interface InputState {
58
65
  time: number;
59
66
  event: number;
@@ -76,6 +83,13 @@ export interface TimelineData {
76
83
  context: number;
77
84
  }
78
85
 
86
+ export interface ChangeData {
87
+ target: Target;
88
+ type: string;
89
+ value: string;
90
+ checksum: string;
91
+ }
92
+
79
93
  export interface InputData {
80
94
  target: Target;
81
95
  value: string;
package/types/layout.d.ts CHANGED
@@ -30,8 +30,9 @@ export const enum RegionVisibility {
30
30
 
31
31
  export const enum Mask {
32
32
  Text = "address,password,contact",
33
- Input = "password,secret,pass,social,ssn,name,code,dob,cell,mob,contact,hidden,account,cvv,ccv,email,tel,phone,address,addr,card,zip",
34
- Disable = "radio,checkbox,range,button,reset,submit"
33
+ Disable = "radio,checkbox,range,button,reset,submit",
34
+ Exclude = "password,secret,pass,social,ssn,code,hidden",
35
+ Tags = "INPUT,SELECT,TEXTAREA"
35
36
  }
36
37
 
37
38
  export const enum Constant {