humanbehavior-js 0.4.23 → 0.4.24
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/cjs/angular/index.cjs +176 -17
- package/dist/cjs/angular/index.cjs.map +1 -1
- package/dist/cjs/index.cjs +176 -17
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/install-wizard.cjs +5 -5
- package/dist/cjs/install-wizard.cjs.map +1 -1
- package/dist/cjs/react/index.cjs +176 -17
- package/dist/cjs/react/index.cjs.map +1 -1
- package/dist/cjs/remix/index.cjs +176 -17
- package/dist/cjs/remix/index.cjs.map +1 -1
- package/dist/cjs/svelte/index.cjs +176 -17
- package/dist/cjs/svelte/index.cjs.map +1 -1
- package/dist/cjs/vue/index.cjs +176 -17
- package/dist/cjs/vue/index.cjs.map +1 -1
- package/dist/cjs/wizard/index.cjs +5 -5
- package/dist/cjs/wizard/index.cjs.map +1 -1
- package/dist/cli/ai-auto-install.js +5 -5
- package/dist/cli/ai-auto-install.js.map +1 -1
- package/dist/cli/auto-install.js +5 -5
- package/dist/cli/auto-install.js.map +1 -1
- package/dist/esm/angular/index.js +176 -17
- package/dist/esm/angular/index.js.map +1 -1
- package/dist/esm/index.js +176 -17
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/install-wizard.js +5 -5
- package/dist/esm/install-wizard.js.map +1 -1
- package/dist/esm/react/index.js +176 -17
- package/dist/esm/react/index.js.map +1 -1
- package/dist/esm/remix/index.js +176 -17
- package/dist/esm/remix/index.js.map +1 -1
- package/dist/esm/svelte/index.js +176 -17
- package/dist/esm/svelte/index.js.map +1 -1
- package/dist/esm/vue/index.js +176 -17
- package/dist/esm/vue/index.js.map +1 -1
- package/dist/esm/wizard/index.js +5 -5
- package/dist/esm/wizard/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/types/angular/index.d.ts +22 -0
- package/dist/types/index.d.ts +40 -2
- package/dist/types/install-wizard.d.ts +1 -1
- package/dist/types/react/index.d.ts +22 -0
- package/dist/types/remix/index.d.ts +22 -0
- package/dist/types/svelte/index.d.ts +22 -0
- package/dist/types/wizard/index.d.ts +1 -1
- package/package.json +1 -1
- package/readme.md +59 -5
- package/src/redact.ts +135 -15
- package/src/tracker.ts +69 -4
- package/src/wizard/core/install-wizard.ts +5 -5
|
@@ -2,6 +2,12 @@ interface RedactionOptions {
|
|
|
2
2
|
redactedText?: string;
|
|
3
3
|
excludeSelectors?: string[];
|
|
4
4
|
userFields?: string[];
|
|
5
|
+
redactionStrategy?: {
|
|
6
|
+
mode: 'privacy-first' | 'visibility-first';
|
|
7
|
+
unredactFields?: string[];
|
|
8
|
+
redactFields?: string[];
|
|
9
|
+
};
|
|
10
|
+
legacyRedactFields?: string[];
|
|
5
11
|
}
|
|
6
12
|
|
|
7
13
|
declare global {
|
|
@@ -46,6 +52,11 @@ declare class HumanBehaviorTracker {
|
|
|
46
52
|
ingestionUrl?: string;
|
|
47
53
|
logLevel?: 'none' | 'error' | 'warn' | 'info' | 'debug';
|
|
48
54
|
redactFields?: string[];
|
|
55
|
+
redactionStrategy?: {
|
|
56
|
+
mode: 'privacy-first' | 'visibility-first';
|
|
57
|
+
unredactFields?: string[];
|
|
58
|
+
redactFields?: string[];
|
|
59
|
+
};
|
|
49
60
|
enableAutomaticTracking?: boolean;
|
|
50
61
|
suppressConsoleErrors?: boolean;
|
|
51
62
|
recordCanvas?: boolean;
|
|
@@ -62,6 +73,12 @@ declare class HumanBehaviorTracker {
|
|
|
62
73
|
constructor(apiKey: string | undefined, ingestionUrl?: string, options?: {
|
|
63
74
|
enableAutomaticProperties?: boolean;
|
|
64
75
|
propertyDenylist?: string[];
|
|
76
|
+
redactionStrategy?: {
|
|
77
|
+
mode: 'privacy-first' | 'visibility-first';
|
|
78
|
+
unredactFields?: string[];
|
|
79
|
+
redactFields?: string[];
|
|
80
|
+
};
|
|
81
|
+
redactFields?: string[];
|
|
65
82
|
});
|
|
66
83
|
private init;
|
|
67
84
|
private ensureInitialized;
|
|
@@ -162,6 +179,11 @@ declare class HumanBehaviorTracker {
|
|
|
162
179
|
* @param options Optional configuration for redaction behavior
|
|
163
180
|
*/
|
|
164
181
|
redact(options?: RedactionOptions): Promise<void>;
|
|
182
|
+
/**
|
|
183
|
+
* Set specific fields to be redacted (for visibility-first mode)
|
|
184
|
+
* @param fields Array of CSS selectors for fields to redact
|
|
185
|
+
*/
|
|
186
|
+
setRedactedFields(fields: string[]): void;
|
|
165
187
|
/**
|
|
166
188
|
* Set specific fields to be unredacted (everything else stays redacted by rrweb)
|
|
167
189
|
* @param fields Array of CSS selectors for fields to unredact (e.g., ['#username', '#comment'])
|
package/dist/types/index.d.ts
CHANGED
|
@@ -2,12 +2,25 @@ interface RedactionOptions {
|
|
|
2
2
|
redactedText?: string;
|
|
3
3
|
excludeSelectors?: string[];
|
|
4
4
|
userFields?: string[];
|
|
5
|
+
redactionStrategy?: {
|
|
6
|
+
mode: 'privacy-first' | 'visibility-first';
|
|
7
|
+
unredactFields?: string[];
|
|
8
|
+
redactFields?: string[];
|
|
9
|
+
};
|
|
10
|
+
legacyRedactFields?: string[];
|
|
5
11
|
}
|
|
6
12
|
declare class RedactionManager {
|
|
7
13
|
private redactedText;
|
|
8
14
|
private unredactedFields;
|
|
15
|
+
private redactedFields;
|
|
16
|
+
private redactionMode;
|
|
9
17
|
private excludeSelectors;
|
|
10
18
|
constructor(options?: RedactionOptions);
|
|
19
|
+
/**
|
|
20
|
+
* Set specific fields to be redacted (for visibility-first mode)
|
|
21
|
+
* @param fields Array of CSS selectors for fields to redact
|
|
22
|
+
*/
|
|
23
|
+
setFieldsToRedact(fields: string[]): void;
|
|
11
24
|
/**
|
|
12
25
|
* Set specific fields to be unredacted (everything else stays redacted by rrweb)
|
|
13
26
|
* @param fields Array of CSS selectors for fields to unredact
|
|
@@ -26,6 +39,10 @@ declare class RedactionManager {
|
|
|
26
39
|
* Check if any fields are currently unredacted
|
|
27
40
|
*/
|
|
28
41
|
hasUnredactedFields(): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Get the current redaction mode
|
|
44
|
+
*/
|
|
45
|
+
getRedactionMode(): 'privacy-first' | 'visibility-first';
|
|
29
46
|
/**
|
|
30
47
|
* Get the currently unredacted fields
|
|
31
48
|
*/
|
|
@@ -36,9 +53,10 @@ declare class RedactionManager {
|
|
|
36
53
|
*/
|
|
37
54
|
getMaskTextSelector(): string | null;
|
|
38
55
|
/**
|
|
39
|
-
*
|
|
56
|
+
* Apply redaction classes to DOM elements (for visibility-first mode)
|
|
57
|
+
* Adds 'rr-mask' class to elements that should be redacted
|
|
40
58
|
*/
|
|
41
|
-
|
|
59
|
+
applyRedactionClasses(): void;
|
|
42
60
|
/**
|
|
43
61
|
* Apply unredaction classes to DOM elements
|
|
44
62
|
* Removes 'rr-mask' class from elements that should be unredacted
|
|
@@ -60,6 +78,10 @@ declare class RedactionManager {
|
|
|
60
78
|
* Check if an element is currently unredacted
|
|
61
79
|
*/
|
|
62
80
|
isElementUnredacted(element: HTMLElement): boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Check if an element should be unredacted
|
|
83
|
+
*/
|
|
84
|
+
shouldUnredactElement(element: HTMLElement): boolean;
|
|
63
85
|
}
|
|
64
86
|
declare const redactionManager: RedactionManager;
|
|
65
87
|
|
|
@@ -105,6 +127,11 @@ declare class HumanBehaviorTracker {
|
|
|
105
127
|
ingestionUrl?: string;
|
|
106
128
|
logLevel?: 'none' | 'error' | 'warn' | 'info' | 'debug';
|
|
107
129
|
redactFields?: string[];
|
|
130
|
+
redactionStrategy?: {
|
|
131
|
+
mode: 'privacy-first' | 'visibility-first';
|
|
132
|
+
unredactFields?: string[];
|
|
133
|
+
redactFields?: string[];
|
|
134
|
+
};
|
|
108
135
|
enableAutomaticTracking?: boolean;
|
|
109
136
|
suppressConsoleErrors?: boolean;
|
|
110
137
|
recordCanvas?: boolean;
|
|
@@ -121,6 +148,12 @@ declare class HumanBehaviorTracker {
|
|
|
121
148
|
constructor(apiKey: string | undefined, ingestionUrl?: string, options?: {
|
|
122
149
|
enableAutomaticProperties?: boolean;
|
|
123
150
|
propertyDenylist?: string[];
|
|
151
|
+
redactionStrategy?: {
|
|
152
|
+
mode: 'privacy-first' | 'visibility-first';
|
|
153
|
+
unredactFields?: string[];
|
|
154
|
+
redactFields?: string[];
|
|
155
|
+
};
|
|
156
|
+
redactFields?: string[];
|
|
124
157
|
});
|
|
125
158
|
private init;
|
|
126
159
|
private ensureInitialized;
|
|
@@ -221,6 +254,11 @@ declare class HumanBehaviorTracker {
|
|
|
221
254
|
* @param options Optional configuration for redaction behavior
|
|
222
255
|
*/
|
|
223
256
|
redact(options?: RedactionOptions): Promise<void>;
|
|
257
|
+
/**
|
|
258
|
+
* Set specific fields to be redacted (for visibility-first mode)
|
|
259
|
+
* @param fields Array of CSS selectors for fields to redact
|
|
260
|
+
*/
|
|
261
|
+
setRedactedFields(fields: string[]): void;
|
|
224
262
|
/**
|
|
225
263
|
* Set specific fields to be unredacted (everything else stays redacted by rrweb)
|
|
226
264
|
* @param fields Array of CSS selectors for fields to unredact (e.g., ['#username', '#comment'])
|
|
@@ -4,6 +4,12 @@ interface RedactionOptions {
|
|
|
4
4
|
redactedText?: string;
|
|
5
5
|
excludeSelectors?: string[];
|
|
6
6
|
userFields?: string[];
|
|
7
|
+
redactionStrategy?: {
|
|
8
|
+
mode: 'privacy-first' | 'visibility-first';
|
|
9
|
+
unredactFields?: string[];
|
|
10
|
+
redactFields?: string[];
|
|
11
|
+
};
|
|
12
|
+
legacyRedactFields?: string[];
|
|
7
13
|
}
|
|
8
14
|
|
|
9
15
|
declare global {
|
|
@@ -48,6 +54,11 @@ declare class HumanBehaviorTracker {
|
|
|
48
54
|
ingestionUrl?: string;
|
|
49
55
|
logLevel?: 'none' | 'error' | 'warn' | 'info' | 'debug';
|
|
50
56
|
redactFields?: string[];
|
|
57
|
+
redactionStrategy?: {
|
|
58
|
+
mode: 'privacy-first' | 'visibility-first';
|
|
59
|
+
unredactFields?: string[];
|
|
60
|
+
redactFields?: string[];
|
|
61
|
+
};
|
|
51
62
|
enableAutomaticTracking?: boolean;
|
|
52
63
|
suppressConsoleErrors?: boolean;
|
|
53
64
|
recordCanvas?: boolean;
|
|
@@ -64,6 +75,12 @@ declare class HumanBehaviorTracker {
|
|
|
64
75
|
constructor(apiKey: string | undefined, ingestionUrl?: string, options?: {
|
|
65
76
|
enableAutomaticProperties?: boolean;
|
|
66
77
|
propertyDenylist?: string[];
|
|
78
|
+
redactionStrategy?: {
|
|
79
|
+
mode: 'privacy-first' | 'visibility-first';
|
|
80
|
+
unredactFields?: string[];
|
|
81
|
+
redactFields?: string[];
|
|
82
|
+
};
|
|
83
|
+
redactFields?: string[];
|
|
67
84
|
});
|
|
68
85
|
private init;
|
|
69
86
|
private ensureInitialized;
|
|
@@ -164,6 +181,11 @@ declare class HumanBehaviorTracker {
|
|
|
164
181
|
* @param options Optional configuration for redaction behavior
|
|
165
182
|
*/
|
|
166
183
|
redact(options?: RedactionOptions): Promise<void>;
|
|
184
|
+
/**
|
|
185
|
+
* Set specific fields to be redacted (for visibility-first mode)
|
|
186
|
+
* @param fields Array of CSS selectors for fields to redact
|
|
187
|
+
*/
|
|
188
|
+
setRedactedFields(fields: string[]): void;
|
|
167
189
|
/**
|
|
168
190
|
* Set specific fields to be unredacted (everything else stays redacted by rrweb)
|
|
169
191
|
* @param fields Array of CSS selectors for fields to unredact (e.g., ['#username', '#comment'])
|
|
@@ -5,6 +5,12 @@ interface RedactionOptions {
|
|
|
5
5
|
redactedText?: string;
|
|
6
6
|
excludeSelectors?: string[];
|
|
7
7
|
userFields?: string[];
|
|
8
|
+
redactionStrategy?: {
|
|
9
|
+
mode: 'privacy-first' | 'visibility-first';
|
|
10
|
+
unredactFields?: string[];
|
|
11
|
+
redactFields?: string[];
|
|
12
|
+
};
|
|
13
|
+
legacyRedactFields?: string[];
|
|
8
14
|
}
|
|
9
15
|
|
|
10
16
|
declare global {
|
|
@@ -49,6 +55,11 @@ declare class HumanBehaviorTracker {
|
|
|
49
55
|
ingestionUrl?: string;
|
|
50
56
|
logLevel?: 'none' | 'error' | 'warn' | 'info' | 'debug';
|
|
51
57
|
redactFields?: string[];
|
|
58
|
+
redactionStrategy?: {
|
|
59
|
+
mode: 'privacy-first' | 'visibility-first';
|
|
60
|
+
unredactFields?: string[];
|
|
61
|
+
redactFields?: string[];
|
|
62
|
+
};
|
|
52
63
|
enableAutomaticTracking?: boolean;
|
|
53
64
|
suppressConsoleErrors?: boolean;
|
|
54
65
|
recordCanvas?: boolean;
|
|
@@ -65,6 +76,12 @@ declare class HumanBehaviorTracker {
|
|
|
65
76
|
constructor(apiKey: string | undefined, ingestionUrl?: string, options?: {
|
|
66
77
|
enableAutomaticProperties?: boolean;
|
|
67
78
|
propertyDenylist?: string[];
|
|
79
|
+
redactionStrategy?: {
|
|
80
|
+
mode: 'privacy-first' | 'visibility-first';
|
|
81
|
+
unredactFields?: string[];
|
|
82
|
+
redactFields?: string[];
|
|
83
|
+
};
|
|
84
|
+
redactFields?: string[];
|
|
68
85
|
});
|
|
69
86
|
private init;
|
|
70
87
|
private ensureInitialized;
|
|
@@ -165,6 +182,11 @@ declare class HumanBehaviorTracker {
|
|
|
165
182
|
* @param options Optional configuration for redaction behavior
|
|
166
183
|
*/
|
|
167
184
|
redact(options?: RedactionOptions): Promise<void>;
|
|
185
|
+
/**
|
|
186
|
+
* Set specific fields to be redacted (for visibility-first mode)
|
|
187
|
+
* @param fields Array of CSS selectors for fields to redact
|
|
188
|
+
*/
|
|
189
|
+
setRedactedFields(fields: string[]): void;
|
|
168
190
|
/**
|
|
169
191
|
* Set specific fields to be unredacted (everything else stays redacted by rrweb)
|
|
170
192
|
* @param fields Array of CSS selectors for fields to unredact (e.g., ['#username', '#comment'])
|
|
@@ -2,6 +2,12 @@ interface RedactionOptions {
|
|
|
2
2
|
redactedText?: string;
|
|
3
3
|
excludeSelectors?: string[];
|
|
4
4
|
userFields?: string[];
|
|
5
|
+
redactionStrategy?: {
|
|
6
|
+
mode: 'privacy-first' | 'visibility-first';
|
|
7
|
+
unredactFields?: string[];
|
|
8
|
+
redactFields?: string[];
|
|
9
|
+
};
|
|
10
|
+
legacyRedactFields?: string[];
|
|
5
11
|
}
|
|
6
12
|
|
|
7
13
|
declare global {
|
|
@@ -46,6 +52,11 @@ declare class HumanBehaviorTracker {
|
|
|
46
52
|
ingestionUrl?: string;
|
|
47
53
|
logLevel?: 'none' | 'error' | 'warn' | 'info' | 'debug';
|
|
48
54
|
redactFields?: string[];
|
|
55
|
+
redactionStrategy?: {
|
|
56
|
+
mode: 'privacy-first' | 'visibility-first';
|
|
57
|
+
unredactFields?: string[];
|
|
58
|
+
redactFields?: string[];
|
|
59
|
+
};
|
|
49
60
|
enableAutomaticTracking?: boolean;
|
|
50
61
|
suppressConsoleErrors?: boolean;
|
|
51
62
|
recordCanvas?: boolean;
|
|
@@ -62,6 +73,12 @@ declare class HumanBehaviorTracker {
|
|
|
62
73
|
constructor(apiKey: string | undefined, ingestionUrl?: string, options?: {
|
|
63
74
|
enableAutomaticProperties?: boolean;
|
|
64
75
|
propertyDenylist?: string[];
|
|
76
|
+
redactionStrategy?: {
|
|
77
|
+
mode: 'privacy-first' | 'visibility-first';
|
|
78
|
+
unredactFields?: string[];
|
|
79
|
+
redactFields?: string[];
|
|
80
|
+
};
|
|
81
|
+
redactFields?: string[];
|
|
65
82
|
});
|
|
66
83
|
private init;
|
|
67
84
|
private ensureInitialized;
|
|
@@ -162,6 +179,11 @@ declare class HumanBehaviorTracker {
|
|
|
162
179
|
* @param options Optional configuration for redaction behavior
|
|
163
180
|
*/
|
|
164
181
|
redact(options?: RedactionOptions): Promise<void>;
|
|
182
|
+
/**
|
|
183
|
+
* Set specific fields to be redacted (for visibility-first mode)
|
|
184
|
+
* @param fields Array of CSS selectors for fields to redact
|
|
185
|
+
*/
|
|
186
|
+
setRedactedFields(fields: string[]): void;
|
|
165
187
|
/**
|
|
166
188
|
* Set specific fields to be unredacted (everything else stays redacted by rrweb)
|
|
167
189
|
* @param fields Array of CSS selectors for fields to unredact (e.g., ['#username', '#comment'])
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -85,7 +85,11 @@ const tracker = HumanBehaviorTracker.init(apiKey, options);
|
|
|
85
85
|
**Options:**
|
|
86
86
|
- `ingestionUrl`: Custom ingestion server URL
|
|
87
87
|
- `logLevel`: 'none' | 'error' | 'warn' | 'info' | 'debug'
|
|
88
|
-
- `redactFields`: Array of CSS selectors to
|
|
88
|
+
- `redactFields`: Array of CSS selectors to unredact (DEPRECATED: Use redactionStrategy instead)
|
|
89
|
+
- `redactionStrategy`: Flexible redaction configuration
|
|
90
|
+
- `mode`: 'privacy-first' | 'visibility-first' (default: 'privacy-first')
|
|
91
|
+
- `unredactFields`: Array of CSS selectors to make visible (when mode: 'privacy-first')
|
|
92
|
+
- `redactFields`: Array of CSS selectors to hide (when mode: 'visibility-first')
|
|
89
93
|
- `suppressConsoleErrors`: Boolean to suppress common rrweb errors (default: true)
|
|
90
94
|
- `recordCanvas`: Boolean to enable canvas recording with PostHog-style protection (default: false)
|
|
91
95
|
|
|
@@ -117,14 +121,64 @@ await tracker.trackNavigationEvent('pushState', '/home', '/about');
|
|
|
117
121
|
|
|
118
122
|
### Data Redaction
|
|
119
123
|
|
|
124
|
+
The SDK supports flexible redaction strategies to protect sensitive data:
|
|
125
|
+
|
|
126
|
+
#### Privacy-First Mode (Default)
|
|
127
|
+
Everything is redacted by default. Specify fields to unredact:
|
|
128
|
+
|
|
129
|
+
```javascript
|
|
130
|
+
// Privacy-first: everything redacted, unredact specific fields
|
|
131
|
+
const tracker = HumanBehaviorTracker.init('your-api-key', {
|
|
132
|
+
redactionStrategy: {
|
|
133
|
+
mode: 'privacy-first',
|
|
134
|
+
unredactFields: ['#username', '#comment', '.public-field']
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
#### Visibility-First Mode
|
|
140
|
+
Everything is visible by default. Specify fields to redact:
|
|
141
|
+
|
|
142
|
+
```javascript
|
|
143
|
+
// Visibility-first: everything visible, redact specific fields
|
|
144
|
+
const tracker = HumanBehaviorTracker.init('your-api-key', {
|
|
145
|
+
redactionStrategy: {
|
|
146
|
+
mode: 'visibility-first',
|
|
147
|
+
redactFields: ['#password', '#credit-card', '.sensitive-data']
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
#### Legacy Support
|
|
153
|
+
The old `redactFields` option still works (privacy-first mode):
|
|
154
|
+
|
|
120
155
|
```javascript
|
|
121
|
-
//
|
|
122
|
-
tracker.
|
|
156
|
+
// Legacy: same as privacy-first mode
|
|
157
|
+
const tracker = HumanBehaviorTracker.init('your-api-key', {
|
|
158
|
+
redactFields: ['#username', '#email'] // These fields will be unredacted
|
|
159
|
+
});
|
|
160
|
+
```
|
|
123
161
|
|
|
124
|
-
|
|
125
|
-
|
|
162
|
+
#### Dynamic Redaction
|
|
163
|
+
Change redaction settings at runtime:
|
|
164
|
+
|
|
165
|
+
```javascript
|
|
166
|
+
// Privacy-first: add fields to unredact
|
|
167
|
+
tracker.setUnredactedFields(['#new-field', '.another-field']);
|
|
168
|
+
|
|
169
|
+
// Visibility-first: add fields to redact
|
|
170
|
+
tracker.setRedactedFields(['#sensitive-field', '.private-data']);
|
|
171
|
+
|
|
172
|
+
// Clear all settings
|
|
173
|
+
tracker.clearUnredactedFields(); // Privacy-first
|
|
174
|
+
tracker.clearRedactedFields(); // Visibility-first
|
|
126
175
|
```
|
|
127
176
|
|
|
177
|
+
#### Security Features
|
|
178
|
+
- **Password fields always protected** (cannot be unredacted in any mode)
|
|
179
|
+
- **CSS selector validation** (prevents invalid selectors)
|
|
180
|
+
- **Runtime safety checks** (handles DOM errors gracefully)
|
|
181
|
+
|
|
128
182
|
### Debugging
|
|
129
183
|
|
|
130
184
|
```javascript
|
package/src/redact.ts
CHANGED
|
@@ -10,12 +10,20 @@ const isBrowser = typeof window !== 'undefined';
|
|
|
10
10
|
export interface RedactionOptions {
|
|
11
11
|
redactedText?: string;
|
|
12
12
|
excludeSelectors?: string[];
|
|
13
|
-
userFields?: string[]; // Fields that the user wants to unredact
|
|
13
|
+
userFields?: string[]; // Fields that the user wants to unredact (legacy)
|
|
14
|
+
redactionStrategy?: {
|
|
15
|
+
mode: 'privacy-first' | 'visibility-first';
|
|
16
|
+
unredactFields?: string[]; // Fields to make visible (when mode: 'privacy-first')
|
|
17
|
+
redactFields?: string[]; // Fields to hide (when mode: 'visibility-first')
|
|
18
|
+
};
|
|
19
|
+
legacyRedactFields?: string[]; // For backward compatibility
|
|
14
20
|
}
|
|
15
21
|
|
|
16
22
|
export class RedactionManager {
|
|
17
23
|
private redactedText: string = '[REDACTED]';
|
|
18
24
|
private unredactedFields: Set<string> = new Set(); // Fields that user wants to unredact
|
|
25
|
+
private redactedFields: Set<string> = new Set(); // Fields that user wants to redact
|
|
26
|
+
private redactionMode: 'privacy-first' | 'visibility-first' = 'privacy-first';
|
|
19
27
|
private excludeSelectors: string[] = [
|
|
20
28
|
'[data-no-redact="true"]',
|
|
21
29
|
'.human-behavior-no-redact'
|
|
@@ -28,11 +36,64 @@ export class RedactionManager {
|
|
|
28
36
|
if (options?.excludeSelectors) {
|
|
29
37
|
this.excludeSelectors = [...this.excludeSelectors, ...options.excludeSelectors];
|
|
30
38
|
}
|
|
39
|
+
|
|
40
|
+
// Handle new redaction strategy
|
|
41
|
+
if (options?.redactionStrategy) {
|
|
42
|
+
this.redactionMode = options.redactionStrategy.mode;
|
|
43
|
+
|
|
44
|
+
if (this.redactionMode === 'privacy-first') {
|
|
45
|
+
// Privacy-first: everything redacted by default, unredact specific fields
|
|
46
|
+
if (options.redactionStrategy.unredactFields) {
|
|
47
|
+
this.setFieldsToUnredact(options.redactionStrategy.unredactFields);
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
// Visibility-first: everything visible by default, redact specific fields
|
|
51
|
+
if (options.redactionStrategy.redactFields) {
|
|
52
|
+
this.setFieldsToRedact(options.redactionStrategy.redactFields);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Handle legacy redactFields (backward compatibility)
|
|
58
|
+
if (options?.legacyRedactFields) {
|
|
59
|
+
this.setFieldsToUnredact(options.legacyRedactFields);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Handle legacy userFields
|
|
31
63
|
if (options?.userFields) {
|
|
32
64
|
this.setFieldsToUnredact(options.userFields);
|
|
33
65
|
}
|
|
34
66
|
}
|
|
35
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Set specific fields to be redacted (for visibility-first mode)
|
|
70
|
+
* @param fields Array of CSS selectors for fields to redact
|
|
71
|
+
*/
|
|
72
|
+
public setFieldsToRedact(fields: string[]): void {
|
|
73
|
+
this.redactedFields.clear();
|
|
74
|
+
|
|
75
|
+
// Always include password fields in redacted list
|
|
76
|
+
const passwordFields = [
|
|
77
|
+
'input[type="password"]',
|
|
78
|
+
'input[type="password" i]',
|
|
79
|
+
'[type="password"]',
|
|
80
|
+
'[type="password" i]'
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
// Add password fields and user-specified fields
|
|
84
|
+
[...passwordFields, ...fields].forEach(field => {
|
|
85
|
+
this.redactedFields.add(field);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (this.redactedFields.size > 0) {
|
|
89
|
+
logDebug(`Redaction: Active for ${this.redactedFields.size} field(s):`, Array.from(this.redactedFields));
|
|
90
|
+
} else {
|
|
91
|
+
logDebug('Redaction: No fields to redact');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
this.applyRedactionClasses();
|
|
95
|
+
}
|
|
96
|
+
|
|
36
97
|
/**
|
|
37
98
|
* Set specific fields to be unredacted (everything else stays redacted by rrweb)
|
|
38
99
|
* @param fields Array of CSS selectors for fields to unredact
|
|
@@ -96,6 +157,13 @@ export class RedactionManager {
|
|
|
96
157
|
return this.unredactedFields.size > 0;
|
|
97
158
|
}
|
|
98
159
|
|
|
160
|
+
/**
|
|
161
|
+
* Get the current redaction mode
|
|
162
|
+
*/
|
|
163
|
+
public getRedactionMode(): 'privacy-first' | 'visibility-first' {
|
|
164
|
+
return this.redactionMode;
|
|
165
|
+
}
|
|
166
|
+
|
|
99
167
|
/**
|
|
100
168
|
* Get the currently unredacted fields
|
|
101
169
|
*/
|
|
@@ -108,31 +176,42 @@ export class RedactionManager {
|
|
|
108
176
|
* Returns null if no fields are unredacted (everything stays redacted)
|
|
109
177
|
*/
|
|
110
178
|
public getMaskTextSelector(): string | null {
|
|
111
|
-
if (this.
|
|
112
|
-
|
|
179
|
+
if (this.redactionMode === 'privacy-first') {
|
|
180
|
+
// Privacy-first: mask everything except unredacted fields
|
|
181
|
+
if (this.unredactedFields.size === 0) {
|
|
182
|
+
return null; // Everything stays redacted
|
|
183
|
+
}
|
|
184
|
+
return Array.from(this.unredactedFields).join(',');
|
|
185
|
+
} else {
|
|
186
|
+
// Visibility-first: mask only redacted fields
|
|
187
|
+
if (this.redactedFields.size === 0) {
|
|
188
|
+
return null; // Nothing to redact
|
|
189
|
+
}
|
|
190
|
+
return Array.from(this.redactedFields).join(',');
|
|
113
191
|
}
|
|
114
|
-
return Array.from(this.unredactedFields).join(',');
|
|
115
192
|
}
|
|
116
193
|
|
|
117
194
|
/**
|
|
118
|
-
*
|
|
195
|
+
* Apply redaction classes to DOM elements (for visibility-first mode)
|
|
196
|
+
* Adds 'rr-mask' class to elements that should be redacted
|
|
119
197
|
*/
|
|
120
|
-
public
|
|
121
|
-
if (this.
|
|
122
|
-
return
|
|
198
|
+
public applyRedactionClasses(): void {
|
|
199
|
+
if (this.redactedFields.size === 0) {
|
|
200
|
+
return;
|
|
123
201
|
}
|
|
124
202
|
|
|
125
|
-
//
|
|
126
|
-
|
|
203
|
+
// Add 'rr-mask' class to redacted elements
|
|
204
|
+
this.redactedFields.forEach(selector => {
|
|
127
205
|
try {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
206
|
+
const elements = document.querySelectorAll(selector);
|
|
207
|
+
elements.forEach(element => {
|
|
208
|
+
element.classList.add('rr-mask');
|
|
209
|
+
});
|
|
210
|
+
logDebug(`Added rr-mask class to ${elements.length} element(s) for selector: ${selector}`);
|
|
131
211
|
} catch (e) {
|
|
132
212
|
logWarn(`Invalid selector: ${selector}`);
|
|
133
213
|
}
|
|
134
|
-
}
|
|
135
|
-
return false;
|
|
214
|
+
});
|
|
136
215
|
}
|
|
137
216
|
|
|
138
217
|
/**
|
|
@@ -198,6 +277,47 @@ export class RedactionManager {
|
|
|
198
277
|
public isElementUnredacted(element: HTMLElement): boolean {
|
|
199
278
|
return this.shouldUnredactElement(element);
|
|
200
279
|
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Check if an element should be unredacted
|
|
283
|
+
*/
|
|
284
|
+
public shouldUnredactElement(element: HTMLElement): boolean {
|
|
285
|
+
if (this.redactionMode === 'privacy-first') {
|
|
286
|
+
// Privacy-first: check if element is in unredacted fields
|
|
287
|
+
if (this.unredactedFields.size === 0) {
|
|
288
|
+
return false; // Nothing unredacted
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Check if any selector matches this element
|
|
292
|
+
for (const selector of this.unredactedFields) {
|
|
293
|
+
try {
|
|
294
|
+
if (element.matches(selector)) {
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
} catch (e) {
|
|
298
|
+
logWarn(`Invalid selector: ${selector}`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return false;
|
|
302
|
+
} else {
|
|
303
|
+
// Visibility-first: check if element is NOT in redacted fields
|
|
304
|
+
if (this.redactedFields.size === 0) {
|
|
305
|
+
return true; // Nothing redacted, everything visible
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Check if any selector matches this element
|
|
309
|
+
for (const selector of this.redactedFields) {
|
|
310
|
+
try {
|
|
311
|
+
if (element.matches(selector)) {
|
|
312
|
+
return false; // Element is redacted
|
|
313
|
+
}
|
|
314
|
+
} catch (e) {
|
|
315
|
+
logWarn(`Invalid selector: ${selector}`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return true; // Element is not redacted
|
|
319
|
+
}
|
|
320
|
+
}
|
|
201
321
|
}
|
|
202
322
|
|
|
203
323
|
// Export a default instance
|