@tindalabs/shield 0.1.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/LICENSE +9 -0
- package/README.md +357 -0
- package/dist/assess.d.ts +16 -0
- package/dist/assess.js +220 -0
- package/dist/config/default-extensions-config.json +103 -0
- package/dist/core/ContentProtector.d.ts +63 -0
- package/dist/core/ContentProtector.js +281 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +2 -0
- package/dist/core/mediator/ContentProtectionMediator.d.ts +86 -0
- package/dist/core/mediator/ContentProtectionMediator.js +238 -0
- package/dist/core/mediator/eventDataTypes.d.ts +112 -0
- package/dist/core/mediator/eventDataTypes.js +23 -0
- package/dist/core/mediator/handlers/abstractEventHandler.d.ts +41 -0
- package/dist/core/mediator/handlers/abstractEventHandler.js +59 -0
- package/dist/core/mediator/handlers/devToolsEventHandler.d.ts +9 -0
- package/dist/core/mediator/handlers/devToolsEventHandler.js +95 -0
- package/dist/core/mediator/handlers/eventHandlerRegistry.d.ts +9 -0
- package/dist/core/mediator/handlers/eventHandlerRegistry.js +34 -0
- package/dist/core/mediator/handlers/extensionEventHandlers.d.ts +40 -0
- package/dist/core/mediator/handlers/extensionEventHandlers.js +140 -0
- package/dist/core/mediator/handlers/iFrameEventHandlers.d.ts +27 -0
- package/dist/core/mediator/handlers/iFrameEventHandlers.js +93 -0
- package/dist/core/mediator/handlers/screenShotEventHandlers.d.ts +34 -0
- package/dist/core/mediator/handlers/screenShotEventHandlers.js +111 -0
- package/dist/core/mediator/protection-event.d.ts +77 -0
- package/dist/core/mediator/protection-event.js +32 -0
- package/dist/core/mediator/types.d.ts +105 -0
- package/dist/core/mediator/types.js +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +7 -0
- package/dist/otel.d.ts +24 -0
- package/dist/otel.js +83 -0
- package/dist/policy.d.ts +98 -0
- package/dist/policy.js +97 -0
- package/dist/strategies/AbstractStrategy.d.ts +124 -0
- package/dist/strategies/AbstractStrategy.js +256 -0
- package/dist/strategies/ClipboardStrategy.d.ts +67 -0
- package/dist/strategies/ClipboardStrategy.js +291 -0
- package/dist/strategies/ContextMenuStrategy.d.ts +60 -0
- package/dist/strategies/ContextMenuStrategy.js +454 -0
- package/dist/strategies/DevToolsStrategy.d.ts +55 -0
- package/dist/strategies/DevToolsStrategy.js +314 -0
- package/dist/strategies/ExtensionStrategy.d.ts +66 -0
- package/dist/strategies/ExtensionStrategy.js +486 -0
- package/dist/strategies/IFrameStrategy.d.ts +49 -0
- package/dist/strategies/IFrameStrategy.js +255 -0
- package/dist/strategies/KeyboardStrategy.d.ts +35 -0
- package/dist/strategies/KeyboardStrategy.js +130 -0
- package/dist/strategies/PrintStrategy.d.ts +47 -0
- package/dist/strategies/PrintStrategy.js +201 -0
- package/dist/strategies/ScreenshotStrategy.d.ts +90 -0
- package/dist/strategies/ScreenshotStrategy.js +502 -0
- package/dist/strategies/SelectionStrategy.d.ts +49 -0
- package/dist/strategies/SelectionStrategy.js +216 -0
- package/dist/strategies/WatermarkStrategy.d.ts +56 -0
- package/dist/strategies/WatermarkStrategy.js +287 -0
- package/dist/strategies/index.d.ts +10 -0
- package/dist/strategies/index.js +11 -0
- package/dist/types/assessment.d.ts +62 -0
- package/dist/types/assessment.js +1 -0
- package/dist/types/index.d.ts +278 -0
- package/dist/types/index.js +17 -0
- package/dist/utils/DOMObserver.d.ts +68 -0
- package/dist/utils/DOMObserver.js +134 -0
- package/dist/utils/base/LoggableComponent.d.ts +44 -0
- package/dist/utils/base/LoggableComponent.js +56 -0
- package/dist/utils/detectors/AbstractDevToolsDetector.d.ts +98 -0
- package/dist/utils/detectors/AbstractDevToolsDetector.js +127 -0
- package/dist/utils/detectors/dateToStringDetector.d.ts +43 -0
- package/dist/utils/detectors/dateToStringDetector.js +96 -0
- package/dist/utils/detectors/debugLibDetector.d.ts +64 -0
- package/dist/utils/detectors/debugLibDetector.js +195 -0
- package/dist/utils/detectors/debuggerDetector.d.ts +51 -0
- package/dist/utils/detectors/debuggerDetector.js +211 -0
- package/dist/utils/detectors/defineGetterDetector.d.ts +48 -0
- package/dist/utils/detectors/defineGetterDetector.js +150 -0
- package/dist/utils/detectors/detectorInterface.d.ts +36 -0
- package/dist/utils/detectors/detectorInterface.js +1 -0
- package/dist/utils/detectors/devToolsDetectorManager.d.ts +88 -0
- package/dist/utils/detectors/devToolsDetectorManager.js +243 -0
- package/dist/utils/detectors/funcToStringDetector.d.ts +43 -0
- package/dist/utils/detectors/funcToStringDetector.js +90 -0
- package/dist/utils/detectors/regToStringDetector.d.ts +43 -0
- package/dist/utils/detectors/regToStringDetector.js +129 -0
- package/dist/utils/detectors/sizeDetector.d.ts +54 -0
- package/dist/utils/detectors/sizeDetector.js +134 -0
- package/dist/utils/detectors/timingDetector.d.ts +55 -0
- package/dist/utils/detectors/timingDetector.js +143 -0
- package/dist/utils/dom.d.ts +20 -0
- package/dist/utils/dom.js +83 -0
- package/dist/utils/environment.d.ts +29 -0
- package/dist/utils/environment.js +267 -0
- package/dist/utils/eventManager.d.ts +162 -0
- package/dist/utils/eventManager.js +548 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/intervalManager.d.ts +91 -0
- package/dist/utils/intervalManager.js +221 -0
- package/dist/utils/keyboardShortcutManager/keyboardShortcutManager.d.ts +41 -0
- package/dist/utils/keyboardShortcutManager/keyboardShortcutManager.js +135 -0
- package/dist/utils/keyboardShortcutManager/keyboardShortcuts.d.ts +18 -0
- package/dist/utils/keyboardShortcutManager/keyboardShortcuts.js +195 -0
- package/dist/utils/logging/simple/Loggable.d.ts +33 -0
- package/dist/utils/logging/simple/Loggable.js +1 -0
- package/dist/utils/logging/simple/LoggingDelegate.d.ts +42 -0
- package/dist/utils/logging/simple/LoggingDelegate.js +53 -0
- package/dist/utils/logging/simple/SimpleLoggingService.d.ts +39 -0
- package/dist/utils/logging/simple/SimpleLoggingService.js +58 -0
- package/dist/utils/orientation.d.ts +15 -0
- package/dist/utils/orientation.js +32 -0
- package/dist/utils/protectedContentManager.d.ts +155 -0
- package/dist/utils/protectedContentManager.js +424 -0
- package/dist/utils/securityOverlayManager.d.ts +253 -0
- package/dist/utils/securityOverlayManager.js +786 -0
- package/dist/utils/timeoutManager.d.ts +50 -0
- package/dist/utils/timeoutManager.js +113 -0
- package/package.json +61 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mediator for coordinating communication between protection strategies
|
|
3
|
+
* Implements the mediator pattern to decouple strategies and enable event-based communication
|
|
4
|
+
*/
|
|
5
|
+
export class ContentProtectionMediator {
|
|
6
|
+
/**
|
|
7
|
+
* Create a new ContentProtectionMediator
|
|
8
|
+
* @param debugMode Enable debug mode for detailed logging
|
|
9
|
+
*/
|
|
10
|
+
constructor(debugMode = false) {
|
|
11
|
+
this.subscriptions = new Map();
|
|
12
|
+
this.subscriptionCounter = 0;
|
|
13
|
+
this.eventHistory = [];
|
|
14
|
+
this.MAX_HISTORY_SIZE = 100;
|
|
15
|
+
this.debugMode = debugMode;
|
|
16
|
+
if (this.debugMode) {
|
|
17
|
+
console.log('ContentProtectionMediator: Initialized');
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Subscribe to an event
|
|
22
|
+
* @param eventType Type of event to subscribe to
|
|
23
|
+
* @param handler Handler function for the event
|
|
24
|
+
* @param options Optional subscription options
|
|
25
|
+
* @returns Subscription ID for later unsubscribing
|
|
26
|
+
*/
|
|
27
|
+
subscribe(eventType, handler, options) {
|
|
28
|
+
if (!handler || typeof handler !== 'function') {
|
|
29
|
+
console.error('ContentProtectionMediator: Invalid handler provided to subscribe');
|
|
30
|
+
return '';
|
|
31
|
+
}
|
|
32
|
+
// Generate a unique subscription ID
|
|
33
|
+
const subscriptionId = `sub_${++this.subscriptionCounter}`;
|
|
34
|
+
// Create the subscription object
|
|
35
|
+
const subscription = {
|
|
36
|
+
id: subscriptionId,
|
|
37
|
+
eventType,
|
|
38
|
+
handler,
|
|
39
|
+
filter: options?.filter,
|
|
40
|
+
priority: options?.priority || 0,
|
|
41
|
+
context: options?.context
|
|
42
|
+
};
|
|
43
|
+
// Get or create the array of subscriptions for this event type
|
|
44
|
+
if (!this.subscriptions.has(eventType)) {
|
|
45
|
+
this.subscriptions.set(eventType, []);
|
|
46
|
+
}
|
|
47
|
+
// Add the subscription to the array
|
|
48
|
+
const subscriptionsForType = this.subscriptions.get(eventType);
|
|
49
|
+
subscriptionsForType.push(subscription);
|
|
50
|
+
// Sort by priority (higher numbers first)
|
|
51
|
+
subscriptionsForType.sort((a, b) => (b.priority || 0) - (a.priority || 0));
|
|
52
|
+
if (this.debugMode) {
|
|
53
|
+
console.log(`ContentProtectionMediator: Subscribed to ${eventType} with ID ${subscriptionId}`, options ? `(priority: ${options.priority || 0}, context: ${options.context || 'none'})` : '');
|
|
54
|
+
}
|
|
55
|
+
return subscriptionId;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Unsubscribe from an event
|
|
59
|
+
* @param subscriptionId ID of the subscription to remove
|
|
60
|
+
* @returns True if the subscription was found and removed
|
|
61
|
+
*/
|
|
62
|
+
unsubscribe(subscriptionId) {
|
|
63
|
+
if (!subscriptionId)
|
|
64
|
+
return false;
|
|
65
|
+
let found = false;
|
|
66
|
+
// Check all event types
|
|
67
|
+
for (const [eventType, subscriptions] of this.subscriptions.entries()) {
|
|
68
|
+
const initialLength = subscriptions.length;
|
|
69
|
+
// Filter out the subscription with the given ID
|
|
70
|
+
const filteredSubscriptions = subscriptions.filter(sub => sub.id !== subscriptionId);
|
|
71
|
+
if (filteredSubscriptions.length < initialLength) {
|
|
72
|
+
// We found and removed the subscription
|
|
73
|
+
this.subscriptions.set(eventType, filteredSubscriptions);
|
|
74
|
+
found = true;
|
|
75
|
+
if (this.debugMode) {
|
|
76
|
+
console.log(`ContentProtectionMediator: Unsubscribed from ${eventType} with ID ${subscriptionId}`);
|
|
77
|
+
}
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return found;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Unsubscribe all handlers with a specific context
|
|
85
|
+
* @param context Context to unsubscribe (e.g., strategy name)
|
|
86
|
+
* @returns Number of subscriptions removed
|
|
87
|
+
*/
|
|
88
|
+
unsubscribeByContext(context) {
|
|
89
|
+
if (!context)
|
|
90
|
+
return 0;
|
|
91
|
+
let removedCount = 0;
|
|
92
|
+
// Check all event types
|
|
93
|
+
for (const [eventType, subscriptions] of this.subscriptions.entries()) {
|
|
94
|
+
const initialLength = subscriptions.length;
|
|
95
|
+
// Filter out subscriptions with the given context
|
|
96
|
+
const filteredSubscriptions = subscriptions.filter(sub => sub.context !== context);
|
|
97
|
+
if (filteredSubscriptions.length < initialLength) {
|
|
98
|
+
// We found and removed subscriptions
|
|
99
|
+
const removed = initialLength - filteredSubscriptions.length;
|
|
100
|
+
removedCount += removed;
|
|
101
|
+
this.subscriptions.set(eventType, filteredSubscriptions);
|
|
102
|
+
if (this.debugMode) {
|
|
103
|
+
console.log(`ContentProtectionMediator: Unsubscribed ${removed} handlers for ${context} from ${eventType}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return removedCount;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Publish an event to all subscribers
|
|
111
|
+
* @param event Event to publish
|
|
112
|
+
*/
|
|
113
|
+
publish(event) {
|
|
114
|
+
console.log('Publishing event', event);
|
|
115
|
+
if (!event || !event.type) {
|
|
116
|
+
console.error('ContentProtectionMediator: Invalid event provided to publish');
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
// Add timestamp if not provided
|
|
120
|
+
if (!event.timestamp) {
|
|
121
|
+
event.timestamp = Date.now();
|
|
122
|
+
}
|
|
123
|
+
// Store in history for debugging
|
|
124
|
+
this.addToEventHistory(event);
|
|
125
|
+
// Get subscriptions for this event type
|
|
126
|
+
const subscriptions = this.subscriptions.get(event.type) || [];
|
|
127
|
+
if (subscriptions.length === 0) {
|
|
128
|
+
if (this.debugMode) {
|
|
129
|
+
console.log(`ContentProtectionMediator: No subscribers for ${event.type}`);
|
|
130
|
+
}
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (this.debugMode) {
|
|
134
|
+
console.log(`ContentProtectionMediator: Publishing ${event.type} from ${event.source}`, event.data ? `with data: ${JSON.stringify(event.data)}` : '');
|
|
135
|
+
}
|
|
136
|
+
// Call each handler
|
|
137
|
+
for (const subscription of subscriptions) {
|
|
138
|
+
try {
|
|
139
|
+
// Apply filter if provided
|
|
140
|
+
if (subscription.filter && !subscription.filter(event)) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
// Call the handler
|
|
144
|
+
subscription.handler(event);
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
console.error(`ContentProtectionMediator: Error in handler for ${event.type}:`, error);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Get all subscriptions for a specific event type
|
|
153
|
+
* @param eventType Type of event to get subscriptions for
|
|
154
|
+
* @returns Array of subscriptions
|
|
155
|
+
*/
|
|
156
|
+
getSubscriptions(eventType) {
|
|
157
|
+
return [...(this.subscriptions.get(eventType) || [])];
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Get all subscriptions
|
|
161
|
+
* @returns Map of event types to subscriptions
|
|
162
|
+
*/
|
|
163
|
+
getAllSubscriptions() {
|
|
164
|
+
// Return a deep copy to prevent external modification
|
|
165
|
+
const result = new Map();
|
|
166
|
+
for (const [eventType, subscriptions] of this.subscriptions.entries()) {
|
|
167
|
+
result.set(eventType, [...subscriptions]);
|
|
168
|
+
}
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Set debug mode
|
|
173
|
+
* @param enabled Whether debug mode should be enabled
|
|
174
|
+
*/
|
|
175
|
+
setDebugMode(enabled) {
|
|
176
|
+
this.debugMode = enabled;
|
|
177
|
+
if (this.debugMode) {
|
|
178
|
+
console.log('ContentProtectionMediator: Debug mode enabled');
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Get debug information about the mediator
|
|
183
|
+
* @returns Object with debug information
|
|
184
|
+
*/
|
|
185
|
+
getDebugInfo() {
|
|
186
|
+
let totalSubscriptions = 0;
|
|
187
|
+
for (const subscriptions of this.subscriptions.values()) {
|
|
188
|
+
totalSubscriptions += subscriptions.length;
|
|
189
|
+
}
|
|
190
|
+
return {
|
|
191
|
+
subscriptionCount: totalSubscriptions,
|
|
192
|
+
eventTypeCount: this.subscriptions.size,
|
|
193
|
+
eventTypes: Array.from(this.subscriptions.keys()),
|
|
194
|
+
recentEvents: [...this.eventHistory]
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Add an event to the history
|
|
199
|
+
* @param event Event to add
|
|
200
|
+
*/
|
|
201
|
+
addToEventHistory(event) {
|
|
202
|
+
this.eventHistory.unshift(event);
|
|
203
|
+
// Limit history size
|
|
204
|
+
if (this.eventHistory.length > this.MAX_HISTORY_SIZE) {
|
|
205
|
+
this.eventHistory.pop();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Clear all subscriptions
|
|
210
|
+
* @returns Number of subscriptions cleared
|
|
211
|
+
*/
|
|
212
|
+
clearAllSubscriptions() {
|
|
213
|
+
let count = 0;
|
|
214
|
+
for (const subscriptions of this.subscriptions.values()) {
|
|
215
|
+
count += subscriptions.length;
|
|
216
|
+
}
|
|
217
|
+
this.subscriptions.clear();
|
|
218
|
+
if (this.debugMode) {
|
|
219
|
+
console.log(`ContentProtectionMediator: Cleared ${count} subscriptions`);
|
|
220
|
+
}
|
|
221
|
+
return count;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Helper method to create and publish an event
|
|
225
|
+
* @param type Event type
|
|
226
|
+
* @param source Source of the event
|
|
227
|
+
* @param data Additional data
|
|
228
|
+
*/
|
|
229
|
+
createAndPublishEvent(type, source, data) {
|
|
230
|
+
const event = {
|
|
231
|
+
type,
|
|
232
|
+
source,
|
|
233
|
+
timestamp: Date.now(),
|
|
234
|
+
data
|
|
235
|
+
};
|
|
236
|
+
this.publish(event);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { OverlayOptions } from "@/utils/securityOverlayManager";
|
|
2
|
+
import { ProtectionEventType } from "./protection-event";
|
|
3
|
+
import { PlaceholderOptions } from "@/utils/protectedContentManager";
|
|
4
|
+
import { ExtensionConfig } from "@/types";
|
|
5
|
+
/**
|
|
6
|
+
* Interface defining the data structure for each event type.
|
|
7
|
+
*
|
|
8
|
+
* Keep this in sync with the {@link ProtectionEventType} enum — every value
|
|
9
|
+
* in the enum should have a matching `[ProtectionEventType.X]: { ... }` entry
|
|
10
|
+
* here. The trailing index signature catches any drift (and lets ad-hoc
|
|
11
|
+
* subscribers attach handlers for not-yet-typed events without compile errors).
|
|
12
|
+
*/
|
|
13
|
+
export interface EventDataMap {
|
|
14
|
+
[ProtectionEventType.DEVTOOLS_STATE_CHANGE]: {
|
|
15
|
+
isOpen: boolean;
|
|
16
|
+
target?: HTMLElement | null;
|
|
17
|
+
showOverlay?: boolean | null;
|
|
18
|
+
overlayOptions?: OverlayOptions | null;
|
|
19
|
+
hideContent?: boolean | null;
|
|
20
|
+
};
|
|
21
|
+
[ProtectionEventType.EXTENSION_DETECTED]: {
|
|
22
|
+
extension: ExtensionConfig | null;
|
|
23
|
+
showOverlay?: boolean | null;
|
|
24
|
+
overlayOptions?: OverlayOptions | null;
|
|
25
|
+
hideContent?: boolean | null;
|
|
26
|
+
priority: number;
|
|
27
|
+
reason: string;
|
|
28
|
+
target?: HTMLElement | null;
|
|
29
|
+
};
|
|
30
|
+
[ProtectionEventType.SCREENSHOT_ATTEMPT]: {
|
|
31
|
+
showOverlay?: boolean | null;
|
|
32
|
+
hideContent?: boolean | null;
|
|
33
|
+
overlayOptions?: OverlayOptions | null;
|
|
34
|
+
duration?: number | null;
|
|
35
|
+
priority: number;
|
|
36
|
+
reason: string;
|
|
37
|
+
target?: HTMLElement | null;
|
|
38
|
+
};
|
|
39
|
+
[ProtectionEventType.FRAME_EMBEDDING_DETECTED]: {
|
|
40
|
+
isEmbedded: boolean;
|
|
41
|
+
isExternalFrame: boolean;
|
|
42
|
+
parentDomain?: string;
|
|
43
|
+
targetElement?: HTMLElement | null;
|
|
44
|
+
showOverlay?: boolean | null;
|
|
45
|
+
hideContent?: boolean | null;
|
|
46
|
+
overlayOptions?: OverlayOptions | null;
|
|
47
|
+
};
|
|
48
|
+
[ProtectionEventType.CONTENT_HIDDEN]: {
|
|
49
|
+
strategyName: string;
|
|
50
|
+
reason: string;
|
|
51
|
+
options: PlaceholderOptions;
|
|
52
|
+
targetElement?: HTMLElement | null;
|
|
53
|
+
priority: number;
|
|
54
|
+
};
|
|
55
|
+
[ProtectionEventType.CONTENT_RESTORED]: {
|
|
56
|
+
strategyName: string;
|
|
57
|
+
targetElement?: HTMLElement | null;
|
|
58
|
+
};
|
|
59
|
+
[ProtectionEventType.OVERLAY_SHOWN]: {
|
|
60
|
+
strategyName: string;
|
|
61
|
+
overlayType: string;
|
|
62
|
+
options: OverlayOptions;
|
|
63
|
+
priority: number;
|
|
64
|
+
duration?: number;
|
|
65
|
+
};
|
|
66
|
+
[ProtectionEventType.OVERLAY_REMOVED]: {
|
|
67
|
+
strategyName: string;
|
|
68
|
+
overlayType: string;
|
|
69
|
+
reason?: string;
|
|
70
|
+
};
|
|
71
|
+
[ProtectionEventType.OVERLAY_RESTORED]: {
|
|
72
|
+
strategyName: string;
|
|
73
|
+
overlayType: string;
|
|
74
|
+
reason?: string;
|
|
75
|
+
};
|
|
76
|
+
[ProtectionEventType.STRATEGY_REMOVED]: {
|
|
77
|
+
strategyName: string;
|
|
78
|
+
reason?: string;
|
|
79
|
+
};
|
|
80
|
+
[key: string]: Record<string, unknown>;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Type-safe typed event interface
|
|
84
|
+
* Use this instead of unsafe `as` assertions
|
|
85
|
+
*/
|
|
86
|
+
export interface TypedProtectionEvent<T extends ProtectionEventType> {
|
|
87
|
+
type: T;
|
|
88
|
+
source: string;
|
|
89
|
+
timestamp: number;
|
|
90
|
+
data: EventDataMap[T];
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Type guard to check if an event has a specific type
|
|
94
|
+
* @param event The event to check
|
|
95
|
+
* @param eventType The expected event type
|
|
96
|
+
* @returns True if the event matches the type
|
|
97
|
+
*/
|
|
98
|
+
export declare function isEventType<T extends ProtectionEventType>(event: {
|
|
99
|
+
type: ProtectionEventType;
|
|
100
|
+
data?: unknown;
|
|
101
|
+
}, eventType: T): event is TypedProtectionEvent<T>;
|
|
102
|
+
/**
|
|
103
|
+
* Get typed event data with type safety
|
|
104
|
+
* Returns undefined if the event type doesn't match
|
|
105
|
+
* @param event The protection event
|
|
106
|
+
* @param eventType The expected event type
|
|
107
|
+
* @returns The typed data or undefined
|
|
108
|
+
*/
|
|
109
|
+
export declare function getTypedEventData<T extends ProtectionEventType>(event: {
|
|
110
|
+
type: ProtectionEventType;
|
|
111
|
+
data?: unknown;
|
|
112
|
+
}, eventType: T): EventDataMap[T] | undefined;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ProtectionEventType } from "./protection-event";
|
|
2
|
+
/**
|
|
3
|
+
* Type guard to check if an event has a specific type
|
|
4
|
+
* @param event The event to check
|
|
5
|
+
* @param eventType The expected event type
|
|
6
|
+
* @returns True if the event matches the type
|
|
7
|
+
*/
|
|
8
|
+
export function isEventType(event, eventType) {
|
|
9
|
+
return event.type === eventType;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Get typed event data with type safety
|
|
13
|
+
* Returns undefined if the event type doesn't match
|
|
14
|
+
* @param event The protection event
|
|
15
|
+
* @param eventType The expected event type
|
|
16
|
+
* @returns The typed data or undefined
|
|
17
|
+
*/
|
|
18
|
+
export function getTypedEventData(event, eventType) {
|
|
19
|
+
if (event.type === eventType) {
|
|
20
|
+
return event.data;
|
|
21
|
+
}
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ProtectionMediator } from "../types";
|
|
2
|
+
import type { ProtectionEvent, ProtectionEventType } from "../protection-event";
|
|
3
|
+
import { LoggableComponent } from "../../../utils/base/LoggableComponent";
|
|
4
|
+
/**
|
|
5
|
+
* Abstract base class for all event handlers
|
|
6
|
+
*/
|
|
7
|
+
export declare abstract class AbstractEventHandler extends LoggableComponent {
|
|
8
|
+
protected mediator: ProtectionMediator;
|
|
9
|
+
protected subscriptionIds: string[];
|
|
10
|
+
/**
|
|
11
|
+
* Create a new event handler
|
|
12
|
+
* @param mediator The protection mediator
|
|
13
|
+
* @param componentName The name of the component
|
|
14
|
+
* @param debugMode Enable debug mode for troubleshooting
|
|
15
|
+
*/
|
|
16
|
+
constructor(mediator: ProtectionMediator, componentName: string, debugMode?: boolean);
|
|
17
|
+
/**
|
|
18
|
+
* Initialize the handler and subscribe to events
|
|
19
|
+
* This method should be implemented by subclasses
|
|
20
|
+
*/
|
|
21
|
+
protected abstract initialize(): void;
|
|
22
|
+
/**
|
|
23
|
+
* Subscribe to an event and track the subscription ID
|
|
24
|
+
* @param eventType The event type to subscribe to
|
|
25
|
+
* @param handler The event handler function
|
|
26
|
+
* @param options Optional subscription options
|
|
27
|
+
* @returns The subscription ID
|
|
28
|
+
*/
|
|
29
|
+
protected subscribe(eventType: ProtectionEventType, handler: (event: ProtectionEvent) => void, options?: {
|
|
30
|
+
context?: string;
|
|
31
|
+
}): string;
|
|
32
|
+
/**
|
|
33
|
+
* Unsubscribe from all events and clean up
|
|
34
|
+
*/
|
|
35
|
+
dispose(): void;
|
|
36
|
+
/**
|
|
37
|
+
* Additional cleanup to be performed on disposal
|
|
38
|
+
* This method can be overridden by subclasses
|
|
39
|
+
*/
|
|
40
|
+
protected onDispose(): void;
|
|
41
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { LoggableComponent } from "../../../utils/base/LoggableComponent";
|
|
2
|
+
/**
|
|
3
|
+
* Abstract base class for all event handlers
|
|
4
|
+
*/
|
|
5
|
+
export class AbstractEventHandler extends LoggableComponent {
|
|
6
|
+
/**
|
|
7
|
+
* Create a new event handler
|
|
8
|
+
* @param mediator The protection mediator
|
|
9
|
+
* @param componentName The name of the component
|
|
10
|
+
* @param debugMode Enable debug mode for troubleshooting
|
|
11
|
+
*/
|
|
12
|
+
constructor(mediator, componentName, debugMode = false) {
|
|
13
|
+
super(componentName, debugMode);
|
|
14
|
+
this.subscriptionIds = [];
|
|
15
|
+
this.mediator = mediator;
|
|
16
|
+
this.initialize();
|
|
17
|
+
// `log` is debug-gated by the logger; the outer check is unnecessary.
|
|
18
|
+
this.log("Initialized and subscribed to events");
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Subscribe to an event and track the subscription ID
|
|
22
|
+
* @param eventType The event type to subscribe to
|
|
23
|
+
* @param handler The event handler function
|
|
24
|
+
* @param options Optional subscription options
|
|
25
|
+
* @returns The subscription ID
|
|
26
|
+
*/
|
|
27
|
+
subscribe(eventType, handler, options) {
|
|
28
|
+
const subId = this.mediator.subscribe(eventType, handler, {
|
|
29
|
+
...options,
|
|
30
|
+
context: options?.context || this.COMPONENT_NAME,
|
|
31
|
+
});
|
|
32
|
+
this.subscriptionIds.push(subId);
|
|
33
|
+
if (this.debugMode) {
|
|
34
|
+
this.log(`Subscribed to ${eventType} with ID ${subId}`);
|
|
35
|
+
}
|
|
36
|
+
return subId;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Unsubscribe from all events and clean up
|
|
40
|
+
*/
|
|
41
|
+
dispose() {
|
|
42
|
+
if (this.debugMode) {
|
|
43
|
+
this.log(`Disposing handler, unsubscribing from ${this.subscriptionIds.length} events`);
|
|
44
|
+
}
|
|
45
|
+
// Unsubscribe from all events
|
|
46
|
+
for (const id of this.subscriptionIds) {
|
|
47
|
+
this.mediator.unsubscribe(id);
|
|
48
|
+
}
|
|
49
|
+
this.subscriptionIds = [];
|
|
50
|
+
this.onDispose();
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Additional cleanup to be performed on disposal
|
|
54
|
+
* This method can be overridden by subclasses
|
|
55
|
+
*/
|
|
56
|
+
onDispose() {
|
|
57
|
+
// Default implementation does nothing
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ProtectionMediator } from "../types";
|
|
2
|
+
import { AbstractEventHandler } from "./abstractEventHandler";
|
|
3
|
+
export declare class DevToolsEventHandler extends AbstractEventHandler {
|
|
4
|
+
constructor(mediator: ProtectionMediator, debugMode?: boolean);
|
|
5
|
+
protected initialize(): void;
|
|
6
|
+
private handleDevToolsStateChange;
|
|
7
|
+
private handleDevToolsOpened;
|
|
8
|
+
private handleDevToolsClosed;
|
|
9
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { ProtectionEventType } from "../protection-event";
|
|
2
|
+
import { isEventType } from "../eventDataTypes";
|
|
3
|
+
import { AbstractEventHandler } from "./abstractEventHandler";
|
|
4
|
+
export class DevToolsEventHandler extends AbstractEventHandler {
|
|
5
|
+
constructor(mediator, debugMode = false) {
|
|
6
|
+
super(mediator, "DevToolsEventHandler", debugMode);
|
|
7
|
+
}
|
|
8
|
+
initialize() {
|
|
9
|
+
this.subscribe(ProtectionEventType.DEVTOOLS_STATE_CHANGE, this.handleDevToolsStateChange.bind(this));
|
|
10
|
+
}
|
|
11
|
+
handleDevToolsStateChange(event) {
|
|
12
|
+
// Use type guard for type-safe access
|
|
13
|
+
if (!isEventType(event, ProtectionEventType.DEVTOOLS_STATE_CHANGE)) {
|
|
14
|
+
this.error("Received invalid event type");
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
this.log(`Handling DevTools state change, isOpen=${event.data.isOpen}`);
|
|
18
|
+
if (event.data.isOpen) {
|
|
19
|
+
this.handleDevToolsOpened(event);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
this.handleDevToolsClosed(event);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
handleDevToolsOpened(event) {
|
|
26
|
+
const { data } = event;
|
|
27
|
+
// Show overlay if showOverlay is enabled (defaults to true)
|
|
28
|
+
if (data.showOverlay !== false) {
|
|
29
|
+
this.mediator.publish({
|
|
30
|
+
type: ProtectionEventType.OVERLAY_SHOWN,
|
|
31
|
+
source: this.COMPONENT_NAME,
|
|
32
|
+
timestamp: Date.now(),
|
|
33
|
+
data: {
|
|
34
|
+
strategyName: this.COMPONENT_NAME,
|
|
35
|
+
overlayType: "devtools",
|
|
36
|
+
options: {
|
|
37
|
+
...data.overlayOptions,
|
|
38
|
+
blockEvents: true,
|
|
39
|
+
autoRestore: true,
|
|
40
|
+
},
|
|
41
|
+
priority: 10,
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
// Only hide content if hideContent option is explicitly enabled
|
|
46
|
+
if (data.hideContent === true) {
|
|
47
|
+
this.mediator.publish({
|
|
48
|
+
type: ProtectionEventType.CONTENT_HIDDEN,
|
|
49
|
+
source: this.COMPONENT_NAME,
|
|
50
|
+
timestamp: Date.now(),
|
|
51
|
+
data: {
|
|
52
|
+
strategyName: this.COMPONENT_NAME,
|
|
53
|
+
reason: "devtools_opened",
|
|
54
|
+
options: {
|
|
55
|
+
title: data.overlayOptions?.title,
|
|
56
|
+
message: data.overlayOptions?.message,
|
|
57
|
+
secondaryMessage: data.overlayOptions?.secondaryMessage,
|
|
58
|
+
textColor: 'black',
|
|
59
|
+
backgroundColor: data.overlayOptions?.backgroundColor,
|
|
60
|
+
},
|
|
61
|
+
targetElement: data.target,
|
|
62
|
+
priority: 10,
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
handleDevToolsClosed(event) {
|
|
68
|
+
const { data } = event;
|
|
69
|
+
// Remove overlay if showOverlay was enabled
|
|
70
|
+
if (data.showOverlay !== false) {
|
|
71
|
+
this.mediator.publish({
|
|
72
|
+
type: ProtectionEventType.OVERLAY_REMOVED,
|
|
73
|
+
source: this.COMPONENT_NAME,
|
|
74
|
+
timestamp: Date.now(),
|
|
75
|
+
data: {
|
|
76
|
+
strategyName: this.COMPONENT_NAME,
|
|
77
|
+
overlayType: "devtools",
|
|
78
|
+
reason: "devtools_closed",
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
// Only restore content if hideContent was explicitly enabled
|
|
83
|
+
if (data.hideContent === true) {
|
|
84
|
+
this.mediator.publish({
|
|
85
|
+
type: ProtectionEventType.CONTENT_RESTORED,
|
|
86
|
+
source: this.COMPONENT_NAME,
|
|
87
|
+
timestamp: Date.now(),
|
|
88
|
+
data: {
|
|
89
|
+
strategyName: this.COMPONENT_NAME,
|
|
90
|
+
targetElement: data.target,
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ProtectionMediator } from "../types";
|
|
2
|
+
export declare class HandlerRegistry {
|
|
3
|
+
private handlers;
|
|
4
|
+
private mediator;
|
|
5
|
+
private debugMode;
|
|
6
|
+
constructor(mediator: ProtectionMediator, debugMode?: boolean);
|
|
7
|
+
private initializeHandlers;
|
|
8
|
+
setDebugMode(enabled: boolean): void;
|
|
9
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { DevToolsEventHandler } from "./devToolsEventHandler";
|
|
2
|
+
import { BrowserExtensionEventHandler } from "./extensionEventHandlers";
|
|
3
|
+
import { FrameEmbeddingEventHandler } from "./iFrameEventHandlers";
|
|
4
|
+
import { ScreenshotEventHandler } from "./screenShotEventHandlers";
|
|
5
|
+
export class HandlerRegistry {
|
|
6
|
+
constructor(mediator, debugMode = false) {
|
|
7
|
+
this.handlers = {};
|
|
8
|
+
this.mediator = mediator;
|
|
9
|
+
this.debugMode = debugMode;
|
|
10
|
+
this.initializeHandlers();
|
|
11
|
+
}
|
|
12
|
+
initializeHandlers() {
|
|
13
|
+
// Initialize all handlers
|
|
14
|
+
this.handlers.devTools = new DevToolsEventHandler(this.mediator, this.debugMode);
|
|
15
|
+
this.handlers.extension = new BrowserExtensionEventHandler(this.mediator, this.debugMode);
|
|
16
|
+
this.handlers.screenshot = new ScreenshotEventHandler(this.mediator, this.debugMode);
|
|
17
|
+
this.handlers.iFrame = new FrameEmbeddingEventHandler(this.mediator, this.debugMode);
|
|
18
|
+
// Add more handlers as you implement them
|
|
19
|
+
// this.handlers.screenshot = new ScreenshotEventHandler(this.mediator, this.debugMode);
|
|
20
|
+
// this.handlers.frameEmbedding = new FrameEmbeddingEventHandler(this.mediator, this.debugMode);
|
|
21
|
+
if (this.debugMode) {
|
|
22
|
+
console.log("HandlerRegistry: Initialized all event handlers");
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
setDebugMode(enabled) {
|
|
26
|
+
this.debugMode = enabled;
|
|
27
|
+
// Update debug mode for all handlers
|
|
28
|
+
Object.values(this.handlers).forEach(handler => {
|
|
29
|
+
if (typeof handler.setDebugMode === 'function') {
|
|
30
|
+
handler.setDebugMode(enabled);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ProtectionMediator } from "../types";
|
|
2
|
+
import { ProtectionEvent, ProtectionEventType } from "../protection-event";
|
|
3
|
+
import { TypedProtectionEvent } from "../eventDataTypes";
|
|
4
|
+
import { AbstractEventHandler } from "./abstractEventHandler";
|
|
5
|
+
type ExtensionDetectedEvent = TypedProtectionEvent<typeof ProtectionEventType.EXTENSION_DETECTED>;
|
|
6
|
+
/**
|
|
7
|
+
* Handler for browser extension detection events
|
|
8
|
+
*/
|
|
9
|
+
export declare class BrowserExtensionEventHandler extends AbstractEventHandler {
|
|
10
|
+
/**
|
|
11
|
+
* Create a new BrowserExtensionEventHandler
|
|
12
|
+
* @param mediator The protection mediator
|
|
13
|
+
* @param debugMode Enable debug mode for troubleshooting
|
|
14
|
+
*/
|
|
15
|
+
constructor(mediator: ProtectionMediator, debugMode?: boolean);
|
|
16
|
+
/**
|
|
17
|
+
* Initialize the handler and subscribe to events
|
|
18
|
+
*/
|
|
19
|
+
protected initialize(): void;
|
|
20
|
+
/**
|
|
21
|
+
* Handle extension detection
|
|
22
|
+
* @param event the ProtectionEvent
|
|
23
|
+
*/
|
|
24
|
+
handleExtensionDetected(event: ProtectionEvent): void;
|
|
25
|
+
/**
|
|
26
|
+
* Handle extension detection
|
|
27
|
+
* @param event the typed ExtensionDetectedEvent
|
|
28
|
+
*/
|
|
29
|
+
applyExtensionProtection(event: ExtensionDetectedEvent): void;
|
|
30
|
+
/**
|
|
31
|
+
* Remove extension protection
|
|
32
|
+
* @param event The typed ExtensionDetectedEvent
|
|
33
|
+
*/
|
|
34
|
+
removeExtensionProtection(event: ExtensionDetectedEvent): void;
|
|
35
|
+
/**
|
|
36
|
+
* Additional cleanup to be performed on disposal
|
|
37
|
+
*/
|
|
38
|
+
protected onDispose(): void;
|
|
39
|
+
}
|
|
40
|
+
export {};
|