content-security-toolkit 1.0.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.
Files changed (162) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +171 -0
  3. package/dist/config/default-extensions-config.json +103 -0
  4. package/dist/core/ContentProtector.d.ts +63 -0
  5. package/dist/core/ContentProtector.js +279 -0
  6. package/dist/core/index.d.ts +1 -0
  7. package/dist/core/index.js +2 -0
  8. package/dist/core/mediator/ContentProtectionMediator.d.ts +86 -0
  9. package/dist/core/mediator/ContentProtectionMediator.js +238 -0
  10. package/dist/core/mediator/eventDataTypes.d.ts +205 -0
  11. package/dist/core/mediator/eventDataTypes.js +23 -0
  12. package/dist/core/mediator/handlers/abstractEventHandler.d.ts +67 -0
  13. package/dist/core/mediator/handlers/abstractEventHandler.js +106 -0
  14. package/dist/core/mediator/handlers/baseEventHandler.d.ts +65 -0
  15. package/dist/core/mediator/handlers/baseEventHandler.js +99 -0
  16. package/dist/core/mediator/handlers/devToolsEventHandler.d.ts +9 -0
  17. package/dist/core/mediator/handlers/devToolsEventHandler.js +95 -0
  18. package/dist/core/mediator/handlers/eventHandlerRegistry.d.ts +9 -0
  19. package/dist/core/mediator/handlers/eventHandlerRegistry.js +34 -0
  20. package/dist/core/mediator/handlers/extensionEventHandlers.d.ts +40 -0
  21. package/dist/core/mediator/handlers/extensionEventHandlers.js +140 -0
  22. package/dist/core/mediator/handlers/iFrameEventHandlers.d.ts +27 -0
  23. package/dist/core/mediator/handlers/iFrameEventHandlers.js +93 -0
  24. package/dist/core/mediator/handlers/index.d.ts +9 -0
  25. package/dist/core/mediator/handlers/index.js +34 -0
  26. package/dist/core/mediator/handlers/screenShotEventHandlers.d.ts +34 -0
  27. package/dist/core/mediator/handlers/screenShotEventHandlers.js +111 -0
  28. package/dist/core/mediator/protection-event.d.ts +94 -0
  29. package/dist/core/mediator/protection-event.js +43 -0
  30. package/dist/core/mediator/types.d.ts +105 -0
  31. package/dist/core/mediator/types.js +1 -0
  32. package/dist/index.d.ts +4 -0
  33. package/dist/index.js +5 -0
  34. package/dist/strategies/AbstractStrategy.d.ts +152 -0
  35. package/dist/strategies/AbstractStrategy.js +296 -0
  36. package/dist/strategies/AbstractStrategy.mediator.d.ts +162 -0
  37. package/dist/strategies/AbstractStrategy.mediator.js +349 -0
  38. package/dist/strategies/ClipboardStrategy.d.ts +67 -0
  39. package/dist/strategies/ClipboardStrategy.js +291 -0
  40. package/dist/strategies/ContextMenuStrategy.d.ts +60 -0
  41. package/dist/strategies/ContextMenuStrategy.js +454 -0
  42. package/dist/strategies/DevToolsStrategy copy.d.ts +85 -0
  43. package/dist/strategies/DevToolsStrategy copy.js +362 -0
  44. package/dist/strategies/DevToolsStrategy-detectorManager.d.ts +70 -0
  45. package/dist/strategies/DevToolsStrategy-detectorManager.js +309 -0
  46. package/dist/strategies/DevToolsStrategy-simple.d.ts +75 -0
  47. package/dist/strategies/DevToolsStrategy-simple.js +366 -0
  48. package/dist/strategies/DevToolsStrategy.d.ts +55 -0
  49. package/dist/strategies/DevToolsStrategy.js +314 -0
  50. package/dist/strategies/ExtensionStrategy.d.ts +66 -0
  51. package/dist/strategies/ExtensionStrategy.js +486 -0
  52. package/dist/strategies/IFrameStrategy.d.ts +49 -0
  53. package/dist/strategies/IFrameStrategy.js +255 -0
  54. package/dist/strategies/KeyboardStrategy.d.ts +35 -0
  55. package/dist/strategies/KeyboardStrategy.js +130 -0
  56. package/dist/strategies/PrintStrategy.d.ts +47 -0
  57. package/dist/strategies/PrintStrategy.js +201 -0
  58. package/dist/strategies/ScreenshotStrategy.d.ts +90 -0
  59. package/dist/strategies/ScreenshotStrategy.js +488 -0
  60. package/dist/strategies/SelectionStrategy.d.ts +49 -0
  61. package/dist/strategies/SelectionStrategy.js +216 -0
  62. package/dist/strategies/StrategyRegistry.d.ts +133 -0
  63. package/dist/strategies/StrategyRegistry.js +379 -0
  64. package/dist/strategies/WatermarkStrategy.d.ts +47 -0
  65. package/dist/strategies/WatermarkStrategy.js +273 -0
  66. package/dist/strategies/index.d.ts +9 -0
  67. package/dist/strategies/index.js +10 -0
  68. package/dist/types/index.d.ts +271 -0
  69. package/dist/types/index.js +16 -0
  70. package/dist/utils/DOMObserver.d.ts +68 -0
  71. package/dist/utils/DOMObserver.js +134 -0
  72. package/dist/utils/base/LoggableComponent.d.ts +62 -0
  73. package/dist/utils/base/LoggableComponent.js +95 -0
  74. package/dist/utils/debuggerDetector/debuggerDetectionWorker.d.ts +6 -0
  75. package/dist/utils/debuggerDetector/debuggerDetectionWorker.js +24 -0
  76. package/dist/utils/debuggerDetector/debuggerDetector.d.ts +55 -0
  77. package/dist/utils/debuggerDetector/debuggerDetector.js +158 -0
  78. package/dist/utils/debuggerDetector/firefoxDetector.d.ts +8 -0
  79. package/dist/utils/debuggerDetector/firefoxDetector.js +64 -0
  80. package/dist/utils/detection.d.ts +29 -0
  81. package/dist/utils/detection.js +267 -0
  82. package/dist/utils/detectors/AbstractDevToolsDetector.d.ts +105 -0
  83. package/dist/utils/detectors/AbstractDevToolsDetector.js +136 -0
  84. package/dist/utils/detectors/dateToStringDetector.d.ts +43 -0
  85. package/dist/utils/detectors/dateToStringDetector.js +96 -0
  86. package/dist/utils/detectors/debugLibDetector.d.ts +64 -0
  87. package/dist/utils/detectors/debugLibDetector.js +195 -0
  88. package/dist/utils/detectors/debuggerDetectionWorker.d.ts +6 -0
  89. package/dist/utils/detectors/debuggerDetectionWorker.js +24 -0
  90. package/dist/utils/detectors/debuggerDetector.d.ts +51 -0
  91. package/dist/utils/detectors/debuggerDetector.js +211 -0
  92. package/dist/utils/detectors/defineGetterDetector.d.ts +48 -0
  93. package/dist/utils/detectors/defineGetterDetector.js +150 -0
  94. package/dist/utils/detectors/detectorInterface.d.ts +36 -0
  95. package/dist/utils/detectors/detectorInterface.js +1 -0
  96. package/dist/utils/detectors/devToolsDetectorManager.d.ts +88 -0
  97. package/dist/utils/detectors/devToolsDetectorManager.js +246 -0
  98. package/dist/utils/detectors/firefoxDetector.d.ts +8 -0
  99. package/dist/utils/detectors/firefoxDetector.js +64 -0
  100. package/dist/utils/detectors/funcToStringDetector.d.ts +43 -0
  101. package/dist/utils/detectors/funcToStringDetector.js +90 -0
  102. package/dist/utils/detectors/regToStringDetector.d.ts +43 -0
  103. package/dist/utils/detectors/regToStringDetector.js +129 -0
  104. package/dist/utils/detectors/sizeDetector.d.ts +54 -0
  105. package/dist/utils/detectors/sizeDetector.js +134 -0
  106. package/dist/utils/detectors/timingDetector.d.ts +55 -0
  107. package/dist/utils/detectors/timingDetector.js +143 -0
  108. package/dist/utils/dom.d.ts +20 -0
  109. package/dist/utils/dom.js +83 -0
  110. package/dist/utils/environment.d.ts +29 -0
  111. package/dist/utils/environment.js +267 -0
  112. package/dist/utils/eventManager.d.ts +167 -0
  113. package/dist/utils/eventManager.js +556 -0
  114. package/dist/utils/index.d.ts +2 -0
  115. package/dist/utils/index.js +3 -0
  116. package/dist/utils/intervalManager.d.ts +96 -0
  117. package/dist/utils/intervalManager.js +229 -0
  118. package/dist/utils/keyboardShortcutManager/keyboardShortcutManager.d.ts +41 -0
  119. package/dist/utils/keyboardShortcutManager/keyboardShortcutManager.js +135 -0
  120. package/dist/utils/keyboardShortcutManager/keyboardShortcuts.d.ts +18 -0
  121. package/dist/utils/keyboardShortcutManager/keyboardShortcuts.js +195 -0
  122. package/dist/utils/logging/LogLevel.d.ts +21 -0
  123. package/dist/utils/logging/LogLevel.js +46 -0
  124. package/dist/utils/logging/LoggingConfig.d.ts +68 -0
  125. package/dist/utils/logging/LoggingConfig.js +64 -0
  126. package/dist/utils/logging/LoggingFactory.d.ts +22 -0
  127. package/dist/utils/logging/LoggingFactory.js +61 -0
  128. package/dist/utils/logging/LoggingService.d.ts +235 -0
  129. package/dist/utils/logging/LoggingService.js +385 -0
  130. package/dist/utils/logging/SimpleLoggingService.d.ts +39 -0
  131. package/dist/utils/logging/SimpleLoggingService.js +58 -0
  132. package/dist/utils/logging/advanced/LogLevel.d.ts +21 -0
  133. package/dist/utils/logging/advanced/LogLevel.js +46 -0
  134. package/dist/utils/logging/advanced/LoggingConfig.d.ts +68 -0
  135. package/dist/utils/logging/advanced/LoggingConfig.js +64 -0
  136. package/dist/utils/logging/advanced/LoggingFactory.d.ts +22 -0
  137. package/dist/utils/logging/advanced/LoggingFactory.js +61 -0
  138. package/dist/utils/logging/advanced/LoggingService.d.ts +235 -0
  139. package/dist/utils/logging/advanced/LoggingService.js +385 -0
  140. package/dist/utils/logging/simple/Loggable.d.ts +33 -0
  141. package/dist/utils/logging/simple/Loggable.js +1 -0
  142. package/dist/utils/logging/simple/LoggingDelegate.d.ts +42 -0
  143. package/dist/utils/logging/simple/LoggingDelegate.js +53 -0
  144. package/dist/utils/logging/simple/SimpleLoggingService.d.ts +39 -0
  145. package/dist/utils/logging/simple/SimpleLoggingService.js +58 -0
  146. package/dist/utils/orientation.d.ts +15 -0
  147. package/dist/utils/orientation.js +32 -0
  148. package/dist/utils/protectedContentManager-simple.d.ts +86 -0
  149. package/dist/utils/protectedContentManager-simple.js +180 -0
  150. package/dist/utils/protectedContentManager.d.ts +162 -0
  151. package/dist/utils/protectedContentManager.js +427 -0
  152. package/dist/utils/screenshotDetector.d.ts +72 -0
  153. package/dist/utils/screenshotDetector.js +179 -0
  154. package/dist/utils/securityOverlayManager-observer-pause.d.ts +283 -0
  155. package/dist/utils/securityOverlayManager-observer-pause.js +878 -0
  156. package/dist/utils/securityOverlayManager-simple.d.ts +197 -0
  157. package/dist/utils/securityOverlayManager-simple.js +552 -0
  158. package/dist/utils/securityOverlayManager.d.ts +260 -0
  159. package/dist/utils/securityOverlayManager.js +774 -0
  160. package/dist/utils/timeoutManager.d.ts +55 -0
  161. package/dist/utils/timeoutManager.js +121 -0
  162. package/package.json +54 -0
@@ -0,0 +1,486 @@
1
+ import { isBrowser } from "../utils/environment";
2
+ import defaultConfig from "../config/default-extensions-config.json";
3
+ import { intervalManager } from "../utils/intervalManager";
4
+ import { AbstractStrategy, StrategyErrorType } from "./AbstractStrategy";
5
+ import { ProtectionEventType } from "../core/mediator/protection-event";
6
+ /**
7
+ * Strategy for detecting and responding to browser extensions that might scrape content
8
+ */
9
+ export class BrowserExtensionDetectionStrategy extends AbstractStrategy {
10
+ /**
11
+ * Create a new BrowserExtensionDetectionStrategy
12
+ * @param options Configuration options
13
+ * @param targetElement Element containing sensitive content to protect
14
+ * @param customHandler Optional custom handler for extension detection
15
+ * @param debugMode Enable debug mode for troubleshooting
16
+ */
17
+ constructor(options, targetElement = null, customHandler, debugMode = false) {
18
+ super("BrowserExtensionDetectionStrategy", debugMode);
19
+ this.intervalId = null;
20
+ this.taskId = null;
21
+ this.extensionsConfig = {};
22
+ this.detectedExtensions = new Set();
23
+ this.targetElement = null;
24
+ this.configLoaded = false;
25
+ this.configLoadPromise = null;
26
+ this.defaultConfigPath = "/src/data/default-extensions-config.json";
27
+ this.options = {
28
+ showOverlay: true,
29
+ detectionInterval: 5000, // Check every 5 seconds
30
+ overlayOptions: {
31
+ title: "Content Protection Active",
32
+ message: "Please disable content scraping extensions to view this content.",
33
+ backgroundColor: "rgba(220, 38, 38, 0.9)", // Red with opacity
34
+ textColor: "white",
35
+ },
36
+ hideContent: true,
37
+ ...options,
38
+ };
39
+ this.targetElement = targetElement;
40
+ this.customHandler = customHandler;
41
+ this.log("Initialized with detection interval:", this.options.detectionInterval);
42
+ // If inline config is provided, use it immediately
43
+ if (this.options.extensionsConfig) {
44
+ this.extensionsConfig = this.options.extensionsConfig;
45
+ this.configLoaded = true;
46
+ this.log("Using provided inline extensions config");
47
+ }
48
+ }
49
+ /**
50
+ * Load extension configuration from a JSON file
51
+ */
52
+ async loadConfiguration() {
53
+ return this.safeExecuteAsync("loadConfiguration", StrategyErrorType.INITIALIZATION, async () => {
54
+ if (this.configLoaded)
55
+ return;
56
+ // If we're already loading, wait for that to complete
57
+ if (this.configLoadPromise) {
58
+ return this.configLoadPromise;
59
+ }
60
+ // eslint-disable-next-line no-async-promise-executor
61
+ this.configLoadPromise = new Promise(async (resolve) => {
62
+ // If a custom config path is provided, use it
63
+ if (this.options.configPath) {
64
+ this.log(`Loading configuration from ${this.options.configPath}`);
65
+ try {
66
+ const response = await fetch(this.options.configPath);
67
+ if (!response.ok) {
68
+ throw new Error(`Failed to load configuration: ${response.statusText}`);
69
+ }
70
+ const config = await response.json();
71
+ // Validate the configuration
72
+ if (!config.extensions || typeof config.extensions !== "object") {
73
+ throw new Error('Invalid configuration format: missing or invalid "extensions" object');
74
+ }
75
+ this.extensionsConfig = config.extensions;
76
+ this.configLoaded = true;
77
+ this.log(`Loaded configuration with ${Object.keys(this.extensionsConfig).length} extensions`);
78
+ }
79
+ catch (fetchError) {
80
+ this.handleError(StrategyErrorType.INITIALIZATION, "Error fetching configuration", fetchError);
81
+ // Fall back to default configuration
82
+ this.extensionsConfig = defaultConfig.extensions;
83
+ this.configLoaded = true;
84
+ this.log("Falling back to default configuration after fetch error");
85
+ }
86
+ }
87
+ else {
88
+ // Use the imported default configuration
89
+ this.extensionsConfig = defaultConfig.extensions;
90
+ this.configLoaded = true;
91
+ this.log(`Using default configuration with ${Object.keys(this.extensionsConfig).length} extensions`);
92
+ }
93
+ resolve();
94
+ });
95
+ return this.configLoadPromise;
96
+ });
97
+ }
98
+ /**
99
+ * Apply the protection strategy
100
+ */
101
+ async apply() {
102
+ return this.safeExecuteAsync("apply", StrategyErrorType.APPLICATION, async () => {
103
+ if (this.isAppliedFlag) {
104
+ this.log("Protection already applied");
105
+ return;
106
+ }
107
+ // Load configuration if not already loaded
108
+ await this.loadConfiguration();
109
+ // Run initial detection
110
+ await this.runAllDetectionMethods();
111
+ // Register with IntervalManager
112
+ try {
113
+ this.taskId = intervalManager.registerTask("extension-detection", async () => {
114
+ await this.runAllDetectionMethods();
115
+ // Always check overlay state if we have detected extensions
116
+ if (this.detectedExtensions.size > 0 && this.mediator) {
117
+ // Publish event to check overlay state
118
+ this.mediator.publish({
119
+ type: ProtectionEventType.OVERLAY_RESTORED,
120
+ source: this.STRATEGY_NAME,
121
+ timestamp: Date.now(),
122
+ data: {
123
+ strategyName: this.STRATEGY_NAME,
124
+ overlayType: "extension",
125
+ reason: "periodic_check",
126
+ },
127
+ });
128
+ }
129
+ }, this.options.detectionInterval);
130
+ if (!this.taskId) {
131
+ this.handleError(StrategyErrorType.APPLICATION, "Failed to register interval task", new Error("Task registration returned empty ID"));
132
+ }
133
+ }
134
+ catch (intervalError) {
135
+ this.handleError(StrategyErrorType.APPLICATION, "Error registering interval task", intervalError);
136
+ // Try to use a fallback interval method if IntervalManager fails
137
+ this.intervalId = window.setInterval(async () => {
138
+ await this.safeExecuteAsync("fallbackInterval", StrategyErrorType.APPLICATION, async () => {
139
+ await this.runAllDetectionMethods();
140
+ // Always check overlay state if we have detected extensions
141
+ if (this.detectedExtensions.size > 0 && this.mediator) {
142
+ // Publish event to check overlay state
143
+ this.mediator.publish({
144
+ type: ProtectionEventType.OVERLAY_RESTORED,
145
+ source: this.STRATEGY_NAME,
146
+ timestamp: Date.now(),
147
+ data: {
148
+ strategyName: this.STRATEGY_NAME,
149
+ overlayType: "extension",
150
+ reason: "periodic_check",
151
+ },
152
+ });
153
+ }
154
+ });
155
+ }, this.options.detectionInterval);
156
+ this.log("Using fallback interval method after IntervalManager failure");
157
+ }
158
+ this.isAppliedFlag = true;
159
+ this.log("Protection applied with detection interval:", this.options.detectionInterval);
160
+ });
161
+ }
162
+ /**
163
+ * Remove the protection strategy
164
+ */
165
+ remove() {
166
+ return this.safeExecute("remove", StrategyErrorType.REMOVAL, () => {
167
+ if (!this.isAppliedFlag) {
168
+ this.log("Protection not applied");
169
+ return;
170
+ }
171
+ // Clear interval via IntervalManager
172
+ if (this.taskId !== null) {
173
+ intervalManager.unregisterTask(this.taskId);
174
+ this.taskId = null;
175
+ this.log("Interval task unregistered");
176
+ }
177
+ // Also clear the old interval for backwards compatibility or if both are used
178
+ if (this.intervalId !== null) {
179
+ window.clearInterval(this.intervalId);
180
+ this.intervalId = null;
181
+ this.log("Legacy interval cleared");
182
+ }
183
+ // Call parent class remove method to handle event cleanup
184
+ super.remove();
185
+ // If extensions are currently detected, publish events to restore content and remove overlay
186
+ if (this.detectedExtensions.size > 0 && this.mediator) {
187
+ if (this.options.hideContent) {
188
+ this.mediator.publish({
189
+ type: ProtectionEventType.CONTENT_RESTORED,
190
+ source: this.STRATEGY_NAME,
191
+ timestamp: Date.now(),
192
+ data: {
193
+ strategyName: this.STRATEGY_NAME,
194
+ targetElement: this.targetElement,
195
+ },
196
+ });
197
+ }
198
+ if (this.options.showOverlay) {
199
+ this.mediator.publish({
200
+ type: ProtectionEventType.OVERLAY_REMOVED,
201
+ source: this.STRATEGY_NAME,
202
+ timestamp: Date.now(),
203
+ data: {
204
+ strategyName: this.STRATEGY_NAME,
205
+ overlayType: "devtools",
206
+ reason: "strategy_removed",
207
+ },
208
+ });
209
+ }
210
+ }
211
+ // Always reset state
212
+ this.isAppliedFlag = false;
213
+ this.detectedExtensions.clear();
214
+ this.log("Protection removed");
215
+ });
216
+ }
217
+ /**
218
+ * Run all detection methods to check for extensions
219
+ */
220
+ async runAllDetectionMethods() {
221
+ return this.safeExecuteAsync("runAllDetectionMethods", StrategyErrorType.APPLICATION, async () => {
222
+ this.log("Running detection methods");
223
+ if (!this.configLoaded) {
224
+ await this.loadConfiguration();
225
+ }
226
+ // Run each detection method
227
+ await this.safeExecuteAsync("detectDOMInjections", StrategyErrorType.APPLICATION, async () => {
228
+ await this.detectDOMInjections();
229
+ });
230
+ await this.safeExecute("checkJavaScriptSignatures", StrategyErrorType.APPLICATION, () => {
231
+ this.checkJavaScriptSignatures();
232
+ });
233
+ });
234
+ }
235
+ /**
236
+ * Detect DOM elements injected by extensions
237
+ */
238
+ async detectDOMInjections() {
239
+ if (!isBrowser() || !document)
240
+ return;
241
+ if (!this.extensionsConfig || Object.keys(this.extensionsConfig).length === 0) {
242
+ this.log("No extension configuration available for DOM injection detection");
243
+ return;
244
+ }
245
+ for (const [extensionId, config] of Object.entries(this.extensionsConfig)) {
246
+ if (!config ||
247
+ !config.detectionMethods ||
248
+ !config.detectionMethods.domSelectors ||
249
+ config.detectionMethods.domSelectors.length === 0) {
250
+ continue;
251
+ }
252
+ // Create a combined selector from all selectors for this extension
253
+ const selector = config.detectionMethods.domSelectors.join(", ");
254
+ try {
255
+ const elements = document.querySelectorAll(selector);
256
+ if (elements.length > 0) {
257
+ this.handleDetection(extensionId, "dom-injection", {
258
+ selector,
259
+ count: elements.length,
260
+ elements: Array.from(elements).map((el) => ({
261
+ tagName: el.tagName,
262
+ id: el.id || "",
263
+ className: el.className || "",
264
+ })),
265
+ });
266
+ }
267
+ }
268
+ catch (queryError) {
269
+ this.handleError(StrategyErrorType.APPLICATION, `Error querying DOM for extension ${extensionId} with selector "${selector}"`, queryError);
270
+ }
271
+ }
272
+ }
273
+ /**
274
+ * Check for JavaScript signatures injected by extensions
275
+ */
276
+ checkJavaScriptSignatures() {
277
+ if (!isBrowser() || !window)
278
+ return;
279
+ if (!this.extensionsConfig || Object.keys(this.extensionsConfig).length === 0) {
280
+ this.log("No extension configuration available for JavaScript signature detection");
281
+ return;
282
+ }
283
+ for (const [extensionId, config] of Object.entries(this.extensionsConfig)) {
284
+ if (!config ||
285
+ !config.detectionMethods ||
286
+ !config.detectionMethods.jsSignatures ||
287
+ config.detectionMethods.jsSignatures.length === 0) {
288
+ continue;
289
+ }
290
+ for (const signature of config.detectionMethods.jsSignatures) {
291
+ if (!signature)
292
+ continue;
293
+ // Check if the signature exists in the global scope
294
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
295
+ const exists = this.checkPropertyExists(window, signature);
296
+ if (exists) {
297
+ this.handleDetection(extensionId, "js-signature", {
298
+ signature,
299
+ });
300
+ }
301
+ }
302
+ }
303
+ }
304
+ /**
305
+ * Check if a property exists in an object, supporting dot notation
306
+ */
307
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
308
+ checkPropertyExists(obj, path) {
309
+ return (this.safeExecute("checkPropertyExists", StrategyErrorType.APPLICATION, () => {
310
+ if (!obj || !path)
311
+ return false;
312
+ const parts = path.split(".");
313
+ let current = obj;
314
+ for (const part of parts) {
315
+ if (!part)
316
+ continue;
317
+ if (current === undefined || current === null) {
318
+ return false;
319
+ }
320
+ try {
321
+ current = current[part];
322
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
323
+ }
324
+ catch (e) {
325
+ return false;
326
+ }
327
+ }
328
+ return current !== undefined;
329
+ }) || false);
330
+ }
331
+ /**
332
+ * Handle detection of an extension
333
+ */
334
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
335
+ handleDetection(extensionId, detectionType, details) {
336
+ return this.safeExecute("handleDetection", StrategyErrorType.APPLICATION, () => {
337
+ if (!extensionId) {
338
+ this.handleError(StrategyErrorType.APPLICATION, "Invalid extension ID in handleDetection", new Error("Extension ID is empty or undefined"));
339
+ return;
340
+ }
341
+ // Skip if already detected
342
+ if (this.detectedExtensions.has(extensionId))
343
+ return;
344
+ // Add to detected extensions
345
+ this.detectedExtensions.add(extensionId);
346
+ const extensionConfig = this.extensionsConfig[extensionId] || {
347
+ name: "Unknown Extension",
348
+ risk: "medium",
349
+ };
350
+ this.log(`Detected extension ${extensionConfig.name} (${extensionId}) via ${detectionType}`, details);
351
+ // Call custom handler if provided
352
+ if (this.customHandler) {
353
+ this.safeExecute("customHandler", StrategyErrorType.APPLICATION, () => {
354
+ if (this.customHandler) {
355
+ this.customHandler(extensionId, extensionConfig.name, extensionConfig.risk);
356
+ }
357
+ });
358
+ }
359
+ // PUBLISH DETECTION EVENT
360
+ if (this.mediator) {
361
+ this.mediator.publish({
362
+ type: ProtectionEventType.EXTENSION_DETECTED,
363
+ source: this.STRATEGY_NAME,
364
+ timestamp: Date.now(),
365
+ data: {
366
+ extension: extensionConfig,
367
+ hideContent: this.options.hideContent,
368
+ showOverlay: this.options.showOverlay,
369
+ overlayOptions: this.options.overlayOptions,
370
+ target: this.targetElement,
371
+ priority: 8,
372
+ reason: "extension_detected",
373
+ },
374
+ });
375
+ this.log(`Published EXTENSION_DETECTED event for ${extensionConfig.name}`);
376
+ }
377
+ });
378
+ }
379
+ /**
380
+ * Update options for the strategy
381
+ */
382
+ updateOptions(options) {
383
+ return this.safeExecute("updateOptions", StrategyErrorType.OPTION_UPDATE, () => {
384
+ if (!options) {
385
+ this.handleError(StrategyErrorType.OPTION_UPDATE, "Invalid options in updateOptions", new Error("Options is null or undefined"));
386
+ return;
387
+ }
388
+ const typedOptions = options;
389
+ this.log("Updating options", typedOptions);
390
+ // Store previous options for comparison
391
+ const previousOptions = { ...this.options };
392
+ // Update options
393
+ this.options = {
394
+ ...this.options,
395
+ ...typedOptions,
396
+ };
397
+ // Handle extension config updates
398
+ if (typedOptions.extensionsConfig) {
399
+ this.extensionsConfig = typedOptions.extensionsConfig;
400
+ this.configLoaded = true;
401
+ this.log("Updated to use new inline extensions config");
402
+ }
403
+ // Handle config path updates
404
+ if (typedOptions.configPath && this.isAppliedFlag) {
405
+ // Check if the path actually changed
406
+ if (typedOptions.configPath !== previousOptions.configPath) {
407
+ // Reload configuration
408
+ this.configLoaded = false;
409
+ this.loadConfiguration()
410
+ .then(() => {
411
+ // Re-run detection with new configuration
412
+ this.runAllDetectionMethods();
413
+ })
414
+ .catch((detectionError) => {
415
+ this.handleError(StrategyErrorType.OPTION_UPDATE, "Error running detection after config update", detectionError);
416
+ });
417
+ this.log("Reloading configuration from new path:", typedOptions.configPath);
418
+ }
419
+ }
420
+ // Update detection interval if it changed and we're using the IntervalManager
421
+ if (typedOptions.detectionInterval &&
422
+ this.taskId !== null &&
423
+ this.isAppliedFlag &&
424
+ typedOptions.detectionInterval !== previousOptions.detectionInterval) {
425
+ // Unregister and re-register with new frequency
426
+ intervalManager.unregisterTask(this.taskId);
427
+ this.taskId = intervalManager.registerTask("extension-detection", async () => {
428
+ await this.runAllDetectionMethods();
429
+ // Always check overlay state if we have detected extensions
430
+ if (this.detectedExtensions.size > 0 && this.mediator) {
431
+ // Publish event to check overlay state
432
+ this.mediator.publish({
433
+ type: ProtectionEventType.OVERLAY_RESTORED,
434
+ source: this.STRATEGY_NAME,
435
+ timestamp: Date.now(),
436
+ data: {
437
+ strategyName: this.STRATEGY_NAME,
438
+ overlayType: "extension",
439
+ reason: "periodic_check",
440
+ },
441
+ });
442
+ }
443
+ }, typedOptions.detectionInterval);
444
+ this.log(`Detection interval updated to ${typedOptions.detectionInterval}ms`);
445
+ }
446
+ // If protection is already applied and we have detected extensions, update the overlay if needed
447
+ if (this.isAppliedFlag && this.detectedExtensions.size > 0 && this.mediator) {
448
+ // Check if any visual options changed
449
+ const visualOptionsChanged = previousOptions.overlayOptions?.title !== this.options.overlayOptions?.title ||
450
+ previousOptions.overlayOptions?.message !== this.options.overlayOptions?.message ||
451
+ previousOptions.overlayOptions?.backgroundColor !== this.options.overlayOptions?.backgroundColor ||
452
+ previousOptions.overlayOptions?.textColor !== this.options.overlayOptions?.textColor;
453
+ if (visualOptionsChanged) {
454
+ // For each detected extension, republish the event with updated options
455
+ for (const extensionId of this.detectedExtensions) {
456
+ const extensionConfig = this.extensionsConfig[extensionId];
457
+ if (extensionConfig && extensionConfig.risk === "high") {
458
+ this.mediator.publish({
459
+ type: ProtectionEventType.EXTENSION_DETECTED,
460
+ source: this.STRATEGY_NAME,
461
+ timestamp: Date.now(),
462
+ data: {
463
+ extension: extensionConfig,
464
+ hideContent: this.options.hideContent,
465
+ showOverlay: this.options.showOverlay,
466
+ overlayOptions: this.options.overlayOptions,
467
+ target: this.targetElement,
468
+ priority: 8,
469
+ reason: "options_updated",
470
+ },
471
+ });
472
+ break; // Just need to update once
473
+ }
474
+ }
475
+ }
476
+ }
477
+ });
478
+ }
479
+ /**
480
+ * Set debug mode
481
+ * @param enabled Whether debug mode should be enabled
482
+ */
483
+ setDebugMode(enabled) {
484
+ super.setDebugMode(enabled);
485
+ }
486
+ }
@@ -0,0 +1,49 @@
1
+ import type { FrameEmbeddingOptions, CustomEventHandlers } from "../types";
2
+ import { AbstractStrategy } from "./AbstractStrategy";
3
+ /**
4
+ * Strategy for preventing content from being embedded in external iframes
5
+ */
6
+ export declare class FrameEmbeddingProtectionStrategy extends AbstractStrategy {
7
+ private options;
8
+ private targetElement;
9
+ private customHandler?;
10
+ private isEmbedded;
11
+ private isExternalFrame;
12
+ private intervalId;
13
+ private taskId;
14
+ private parentDomain;
15
+ /**
16
+ * Create a new FrameEmbeddingProtectionStrategy
17
+ * @param options Configuration options
18
+ * @param targetElement Element containing sensitive content to protect
19
+ * @param customHandler Optional custom handler for frame embedding detection
20
+ * @param debugMode Enable debug mode for troubleshooting
21
+ */
22
+ constructor(options?: FrameEmbeddingOptions, targetElement?: HTMLElement | null, customHandler?: CustomEventHandlers["onFrameEmbeddingDetected"], debugMode?: boolean);
23
+ /**
24
+ * Check if the page is embedded in an iframe
25
+ */
26
+ private checkIfEmbedded;
27
+ /**
28
+ * Publish frame embedding detection event
29
+ */
30
+ private publishFrameEmbeddingEvent;
31
+ /**
32
+ * Apply the protection strategy
33
+ */
34
+ apply(): void;
35
+ /**
36
+ * Remove the protection strategy
37
+ */
38
+ remove(): void;
39
+ /**
40
+ * Update strategy options
41
+ * @param options Options to update
42
+ */
43
+ updateOptions(options: Record<string, unknown>): void;
44
+ /**
45
+ * Set debug mode
46
+ * @param enabled Whether debug mode should be enabled
47
+ */
48
+ setDebugMode(enabled: boolean): void;
49
+ }