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,454 @@
1
+ import { isBrowser, isMobile } from "../utils/environment";
2
+ import { AbstractStrategy, StrategyErrorType } from "./AbstractStrategy";
3
+ import { DomObserver } from "../utils/DOMObserver";
4
+ /**
5
+ * Strategy for preventing context menu (right-click)
6
+ */
7
+ export class ContextMenuStrategy extends AbstractStrategy {
8
+ /**
9
+ * Create a new ContextMenuStrategy
10
+ * @param options Options for customizing the context menu protection
11
+ * @param targetElement Element to protect (defaults to document.body)
12
+ * @param customHandler Optional custom handler for context menu attempts
13
+ * @param debugMode Enable debug mode for troubleshooting
14
+ */
15
+ constructor(options, targetElement, customHandler, debugMode = false) {
16
+ super("ContextMenuStrategy", debugMode);
17
+ this.targetElement = null;
18
+ this.domObserver = null;
19
+ this.options = {
20
+ observeForIframes: false,
21
+ ...options,
22
+ };
23
+ this.targetElement = targetElement || (isBrowser() ? document.body : null);
24
+ this.customHandler = customHandler;
25
+ this.contextMenuHandler = this.handleContextMenu.bind(this);
26
+ this.touchStartHandler = this.handleTouchStart.bind(this);
27
+ this.touchEndHandler = this.handleTouchEnd.bind(this);
28
+ this.log("Initialized with target:", this.targetElement === document.body ? "document.body" : "custom element");
29
+ this.log("Options:", this.options);
30
+ }
31
+ /**
32
+ * Handle context menu event
33
+ */
34
+ handleContextMenu(e) {
35
+ return (this.safeExecute("handleContextMenu", StrategyErrorType.EVENT_HANDLING, () => {
36
+ this.log("Context menu attempt detected", {
37
+ x: e.clientX,
38
+ y: e.clientY,
39
+ target: e.target,
40
+ });
41
+ // Call custom handler if provided
42
+ if (this.customHandler) {
43
+ this.customHandler(e);
44
+ }
45
+ e.preventDefault();
46
+ e.stopPropagation();
47
+ return false;
48
+ }) || false); // Return false as fallback if error occurs
49
+ }
50
+ /**
51
+ * Handle touch start event (for mobile)
52
+ */
53
+ handleTouchStart(e) {
54
+ return this.safeExecute("handleTouchStart", StrategyErrorType.EVENT_HANDLING, () => {
55
+ if (!e || !e.touches)
56
+ return;
57
+ if (e.touches.length > 1) {
58
+ this.log("Multi-touch gesture detected (potential context menu attempt)");
59
+ // Call custom handler if provided (for multi-touch)
60
+ if (this.customHandler) {
61
+ this.customHandler(e);
62
+ }
63
+ e.preventDefault();
64
+ }
65
+ });
66
+ }
67
+ /**
68
+ * Handle touch end event (for mobile)
69
+ */
70
+ handleTouchEnd(e) {
71
+ return this.safeExecute("handleTouchEnd", StrategyErrorType.EVENT_HANDLING, () => {
72
+ if (!e)
73
+ return;
74
+ const now = new Date().getTime();
75
+ const lastTouch = e.timeStamp || 0;
76
+ const timeDiff = now - lastTouch;
77
+ // Detect long press (over 500ms)
78
+ if (timeDiff > 500) {
79
+ this.log("Long press detected (potential context menu attempt)", {
80
+ duration: timeDiff + "ms",
81
+ });
82
+ // Call custom handler if provided (for long press)
83
+ if (this.customHandler) {
84
+ this.customHandler(e);
85
+ }
86
+ e.preventDefault();
87
+ }
88
+ });
89
+ }
90
+ /**
91
+ * Set up DOM observer to watch for new iframes
92
+ */
93
+ setupIframeObserver() {
94
+ return this.safeExecute("setupIframeObserver", StrategyErrorType.APPLICATION, () => {
95
+ if (!isBrowser() || !this.options.observeForIframes)
96
+ return;
97
+ // Disconnect any existing observer
98
+ if (this.domObserver) {
99
+ this.domObserver.stopObserving();
100
+ }
101
+ // Create a new DOM observer
102
+ this.domObserver = new DomObserver({
103
+ targetElement: document.documentElement,
104
+ elementsToWatch: [], // We're not watching for removals
105
+ onElementsAdded: (addedElements) => {
106
+ let newIframesFound = false;
107
+ // Check for added iframes
108
+ addedElements.forEach((element) => {
109
+ if (element.nodeName === "IFRAME") {
110
+ this.protectIframe(element);
111
+ newIframesFound = true;
112
+ }
113
+ });
114
+ if (newIframesFound) {
115
+ this.log("Protected newly added iframes");
116
+ }
117
+ },
118
+ observeSubtree: true,
119
+ debugMode: this.debugMode,
120
+ name: "ContextMenuStrategy-IframeObserver",
121
+ });
122
+ this.domObserver.startObserving();
123
+ this.log("Iframe observer set up");
124
+ });
125
+ }
126
+ /**
127
+ * Protect a specific iframe from context menu
128
+ */
129
+ protectIframe(iframe) {
130
+ return this.safeExecute("protectIframe", StrategyErrorType.APPLICATION, () => {
131
+ // First, protect the iframe element itself immediately
132
+ this.registerEvent(iframe, "contextmenu", this.contextMenuHandler, {
133
+ capture: true,
134
+ priority: 100,
135
+ });
136
+ // For cross-origin iframes, we need to use sandbox attribute detection
137
+ const isCrossOrigin = iframe.src &&
138
+ (iframe.src.startsWith("http") || iframe.src.startsWith("//")) &&
139
+ !iframe.src.includes(window.location.hostname);
140
+ if (isCrossOrigin) {
141
+ this.log(`Detected likely cross-origin iframe: ${iframe.src}`);
142
+ // For cross-origin iframes, we can only protect the iframe element itself
143
+ return;
144
+ }
145
+ // Function to protect the iframe content
146
+ const protectIframeContent = () => {
147
+ // Access the iframe's contentDocument - safeExecute will handle any errors
148
+ const iframeDoc = iframe.contentDocument || iframe.contentWindow?.document;
149
+ if (!iframeDoc) {
150
+ this.log(`Could not access iframe document: ${iframe.src || "unnamed iframe"}`);
151
+ return;
152
+ }
153
+ // Make sure the document is fully loaded
154
+ if (iframeDoc.readyState !== "complete" && iframeDoc.readyState !== "interactive") {
155
+ this.log(`Iframe document not ready yet: ${iframe.src || "unnamed iframe"}, state: ${iframeDoc.readyState}`);
156
+ return;
157
+ }
158
+ // Register context menu handler on the iframe document
159
+ const iframeEventId = this.registerEvent(iframeDoc, "contextmenu", this.contextMenuHandler, {
160
+ capture: true,
161
+ priority: 100,
162
+ });
163
+ // Also register on the iframe document body if available
164
+ if (iframeDoc.body) {
165
+ this.registerEvent(iframeDoc.body, "contextmenu", this.contextMenuHandler, {
166
+ capture: true,
167
+ priority: 100,
168
+ });
169
+ }
170
+ if (iframeEventId) {
171
+ this.log(`Protected iframe content: ${iframe.src || "unnamed iframe"}`);
172
+ }
173
+ // For mobile devices, also protect against touch events
174
+ if (isMobile()) {
175
+ this.registerEvent(iframeDoc, "touchstart", this.touchStartHandler, {
176
+ passive: false,
177
+ capture: true,
178
+ priority: 100,
179
+ });
180
+ this.registerEvent(iframeDoc, "touchend", this.touchEndHandler, {
181
+ passive: false,
182
+ capture: true,
183
+ priority: 100,
184
+ });
185
+ }
186
+ // Add direct event listeners as a backup
187
+ this.safeExecute("addDirectListeners", StrategyErrorType.EVENT_HANDLING, () => {
188
+ iframeDoc.addEventListener("contextmenu", this.contextMenuHandler, {
189
+ capture: true,
190
+ passive: false,
191
+ });
192
+ if (iframeDoc.body) {
193
+ iframeDoc.body.addEventListener("contextmenu", this.contextMenuHandler, {
194
+ capture: true,
195
+ passive: false,
196
+ });
197
+ }
198
+ this.log("Added direct event listeners to iframe document");
199
+ });
200
+ // Try to disable the default context menu using oncontextmenu property
201
+ this.safeExecute("setOnContextMenu", StrategyErrorType.EVENT_HANDLING, () => {
202
+ iframeDoc.oncontextmenu = () => false;
203
+ if (iframeDoc.body) {
204
+ iframeDoc.body.oncontextmenu = () => false;
205
+ }
206
+ this.log("Set oncontextmenu property on iframe document");
207
+ });
208
+ // Also protect any nested iframes
209
+ this.safeExecute("protectNestedIframes", StrategyErrorType.APPLICATION, () => {
210
+ const nestedIframes = iframeDoc.querySelectorAll("iframe");
211
+ if (nestedIframes.length > 0) {
212
+ this.log(`Found ${nestedIframes.length} nested iframes to protect`);
213
+ nestedIframes.forEach((nestedIframe) => {
214
+ this.protectIframe(nestedIframe);
215
+ });
216
+ }
217
+ });
218
+ };
219
+ // Try to protect immediately if possible
220
+ this.safeExecute("immediateProtection", StrategyErrorType.APPLICATION, () => {
221
+ protectIframeContent();
222
+ });
223
+ // Set up multiple attempts to protect the iframe content
224
+ // This helps with dynamically loaded iframes where the content might not be immediately available
225
+ const maxAttempts = 5;
226
+ let attempts = 0;
227
+ const attemptProtection = () => {
228
+ attempts++;
229
+ if (attempts > maxAttempts)
230
+ return;
231
+ this.safeExecute("retryProtection", StrategyErrorType.APPLICATION, () => {
232
+ const iframeDoc = iframe.contentDocument || iframe.contentWindow?.document;
233
+ if (iframeDoc && iframeDoc.readyState === "complete") {
234
+ protectIframeContent();
235
+ }
236
+ else {
237
+ setTimeout(attemptProtection, 200 * attempts); // Increasing delay with each attempt
238
+ }
239
+ });
240
+ };
241
+ // Start the retry process
242
+ setTimeout(attemptProtection, 100);
243
+ // Also set up a load event handler as a fallback
244
+ const loadHandler = () => {
245
+ this.safeExecute("loadHandler", StrategyErrorType.APPLICATION, () => {
246
+ protectIframeContent();
247
+ });
248
+ iframe.removeEventListener("load", loadHandler);
249
+ };
250
+ iframe.addEventListener("load", loadHandler);
251
+ });
252
+ }
253
+ /**
254
+ * Find and protect all existing iframes
255
+ */
256
+ protectExistingIframes() {
257
+ return this.safeExecute("protectExistingIframes", StrategyErrorType.APPLICATION, () => {
258
+ if (!isBrowser())
259
+ return;
260
+ const iframes = document.querySelectorAll("iframe");
261
+ if (iframes.length > 0) {
262
+ this.log(`Found ${iframes.length} existing iframes to protect`);
263
+ iframes.forEach((iframe) => {
264
+ this.protectIframe(iframe);
265
+ });
266
+ }
267
+ });
268
+ }
269
+ /**
270
+ * Apply context menu protection
271
+ */
272
+ apply() {
273
+ return this.safeExecute("apply", StrategyErrorType.APPLICATION, () => {
274
+ if (this.isAppliedFlag || !this.targetElement)
275
+ return;
276
+ this.log("Applying context menu protection", {
277
+ targetElement: this.targetElement === document.body ? "document.body" : "custom element",
278
+ hasCustomHandler: !!this.customHandler,
279
+ observeForIframes: this.options.observeForIframes,
280
+ });
281
+ // Register the context menu event using our registerEvent method
282
+ const contextMenuEventId = this.registerEvent(this.targetElement, "contextmenu", this.contextMenuHandler, { priority: 10 });
283
+ if (!contextMenuEventId) {
284
+ this.handleError(StrategyErrorType.APPLICATION, "Failed to register context menu event handler", new Error("Event registration returned empty ID"));
285
+ }
286
+ // Also prevent other ways to access context menu
287
+ if (isBrowser() && isMobile()) {
288
+ // Register touch events with proper options
289
+ const touchStartId = this.registerEvent(this.targetElement, "touchstart", this.touchStartHandler, {
290
+ passive: false,
291
+ priority: 10,
292
+ });
293
+ const touchEndId = this.registerEvent(this.targetElement, "touchend", this.touchEndHandler, {
294
+ passive: false,
295
+ priority: 10,
296
+ });
297
+ if (!touchStartId || !touchEndId) {
298
+ this.handleError(StrategyErrorType.APPLICATION, "Failed to register touch event handlers", new Error("Touch event registration returned empty ID"));
299
+ }
300
+ this.log("Added mobile-specific event handlers");
301
+ }
302
+ // Always protect existing iframes
303
+ this.protectExistingIframes();
304
+ // Set up observer for future iframes if enabled
305
+ if (this.options.observeForIframes) {
306
+ this.setupIframeObserver();
307
+ }
308
+ this.isAppliedFlag = true;
309
+ this.log(`Protection applied with ${this.eventIds.length} event handlers`);
310
+ });
311
+ }
312
+ /**
313
+ * Remove context menu protection
314
+ * Override the base implementation to handle the complex removal logic
315
+ */
316
+ remove() {
317
+ return this.safeExecute("remove", StrategyErrorType.REMOVAL, () => {
318
+ if (!this.isAppliedFlag)
319
+ return;
320
+ this.log("Removing protection");
321
+ // Disconnect DOM observer if it exists
322
+ if (this.domObserver) {
323
+ this.domObserver.stopObserving();
324
+ this.domObserver = null;
325
+ this.log("Disconnected iframe observer");
326
+ }
327
+ // For Vue components, we need a more robust removal approach
328
+ // Try multiple removal strategies to ensure cleanup
329
+ // 1. Direct removal via target if available
330
+ if (this.targetElement) {
331
+ const removedCount = this.removeAllEventsForTarget(this.targetElement);
332
+ this.log(`Attempted direct removal via target element: ${removedCount} events removed`);
333
+ }
334
+ // 2. Remove by owner ID - this should work for document/window events
335
+ const removedCount = this.removeEventsByOwner();
336
+ this.log(`Removed ${removedCount} events by owner ID`);
337
+ // 3. Use a more comprehensive selector approach for component elements
338
+ if (this.targetElement && this.targetElement !== document.body) {
339
+ let selectorRemoved = 0;
340
+ // Try element tagName first (will be broader but more reliable)
341
+ const tagName = this.targetElement.tagName.toLowerCase();
342
+ selectorRemoved += this.removeEventsBySelector(tagName, "contextmenu");
343
+ // Then try more specific approach with class or ID if available
344
+ const id = this.targetElement.id ? `#${this.targetElement.id}` : "";
345
+ if (id) {
346
+ selectorRemoved += this.removeEventsBySelector(id, "contextmenu");
347
+ }
348
+ // Try removing from elements with same class - but handle string vs DOMTokenList
349
+ let classSelector = "";
350
+ if (this.targetElement.className) {
351
+ if (typeof this.targetElement.className === "string") {
352
+ // Handle string className
353
+ const classes = this.targetElement.className.split(" ").filter((c) => c.trim().length > 0);
354
+ if (classes.length > 0) {
355
+ // Just use the first class for better matching
356
+ classSelector = `.${classes[0]}`;
357
+ }
358
+ }
359
+ else if (this.targetElement.classList && this.targetElement.classList.length > 0) {
360
+ // Handle DOMTokenList
361
+ classSelector = `.${this.targetElement.classList[0]}`;
362
+ }
363
+ }
364
+ if (classSelector) {
365
+ selectorRemoved += this.removeEventsBySelector(classSelector, "contextmenu");
366
+ }
367
+ // Also try finding parent containers with common classes
368
+ selectorRemoved += this.removeEventsBySelector(".content-container", "contextmenu");
369
+ selectorRemoved += this.removeEventsBySelector(".protected-content", "contextmenu");
370
+ if (selectorRemoved > 0) {
371
+ this.log(`Removed ${selectorRemoved} events via selectors`);
372
+ }
373
+ }
374
+ // 4. As a last resort, try to find and remove any contextmenu events from common container elements
375
+ const containerSelectors = ["main", ".app", "#app", ".content", "div.protected-content"];
376
+ let fallbackRemoved = 0;
377
+ for (const selector of containerSelectors) {
378
+ this.safeExecute(`removeSelector-${selector}`, StrategyErrorType.REMOVAL, () => {
379
+ fallbackRemoved += this.removeEventsBySelector(selector, "contextmenu");
380
+ });
381
+ }
382
+ if (fallbackRemoved > 0) {
383
+ this.log(`Removed ${fallbackRemoved} events via fallback selectors`);
384
+ }
385
+ // Clean up mobile-specific handlers if needed
386
+ if (isMobile()) {
387
+ if (this.targetElement) {
388
+ // Try to remove touch events
389
+ this.removeEventsBySelector(this.targetElement.tagName.toLowerCase(), "touchstart");
390
+ this.removeEventsBySelector(this.targetElement.tagName.toLowerCase(), "touchend");
391
+ }
392
+ // Also try global touch event removal
393
+ this.removeEventsBySelector("body", "touchstart");
394
+ this.removeEventsBySelector("body", "touchend");
395
+ }
396
+ // Always clear the event IDs array and reset state
397
+ this.eventIds = [];
398
+ this.isAppliedFlag = false;
399
+ this.log("Protection removal complete");
400
+ });
401
+ }
402
+ /**
403
+ * Update strategy options
404
+ * @param options Options to update
405
+ */
406
+ updateOptions(options) {
407
+ return this.safeExecute("updateOptions", StrategyErrorType.OPTION_UPDATE, () => {
408
+ this.log("Updating options", options);
409
+ // Handle debug mode if present
410
+ if (options.debugMode !== undefined) {
411
+ this.setDebugMode(!!options.debugMode);
412
+ }
413
+ // Handle iframe observation option
414
+ if (options.observeForIframes !== undefined) {
415
+ this.options.observeForIframes = !!options.observeForIframes;
416
+ // If we're already applied and changing the observer setting
417
+ if (this.isAppliedFlag) {
418
+ if (this.options.observeForIframes) {
419
+ this.setupIframeObserver();
420
+ }
421
+ else if (this.domObserver) {
422
+ this.domObserver.stopObserving();
423
+ this.domObserver = null;
424
+ }
425
+ }
426
+ this.log(`Iframe observation ${this.options.observeForIframes ? "enabled" : "disabled"}`);
427
+ }
428
+ // If we need to update the target element
429
+ if (options.targetElement) {
430
+ if (options.targetElement instanceof HTMLElement) {
431
+ const newTarget = options.targetElement;
432
+ if (this.targetElement !== newTarget) {
433
+ // If already applied, remove and reapply with new target
434
+ const wasApplied = this.isAppliedFlag;
435
+ if (wasApplied) {
436
+ this.remove();
437
+ }
438
+ this.targetElement = newTarget;
439
+ if (wasApplied) {
440
+ this.apply();
441
+ }
442
+ this.log("Target element updated and protection reapplied");
443
+ }
444
+ else {
445
+ this.log("Target element unchanged, no update needed");
446
+ }
447
+ }
448
+ else {
449
+ this.handleError(StrategyErrorType.OPTION_UPDATE, "Invalid targetElement option", new Error("targetElement must be an HTMLElement"));
450
+ }
451
+ }
452
+ });
453
+ }
454
+ }
@@ -0,0 +1,85 @@
1
+ import type { CustomEventHandlers, DevToolsOptions } from "../types";
2
+ import { AbstractStrategy } from "./AbstractStrategy";
3
+ /**
4
+ * Strategy for detecting and responding to DevTools usage
5
+ */
6
+ export declare class DevToolsStrategy extends AbstractStrategy {
7
+ private intervalId;
8
+ private taskId;
9
+ private customHandler?;
10
+ private isDevToolsOpen;
11
+ private targetElement;
12
+ private options;
13
+ private contentManager;
14
+ private overlayManager;
15
+ private resizeEventId;
16
+ private originalWidth;
17
+ private originalHeight;
18
+ private resizeHandler;
19
+ private previousOrientation;
20
+ /**
21
+ * Create a new DevToolsStrategy
22
+ * @param options Options for customizing the DevTools protection
23
+ * @param targetElement Element containing sensitive content
24
+ * @param customHandler Optional custom handler for DevTools detection
25
+ * @param debugMode Enable debug mode for troubleshooting
26
+ */
27
+ constructor(options?: DevToolsOptions, targetElement?: HTMLElement | null, customHandler?: CustomEventHandlers["onDevToolsOpen"], debugMode?: boolean);
28
+ /**
29
+ * Detect if DevTools is open based on window dimensions
30
+ * @returns True if DevTools is likely open, false otherwise
31
+ */
32
+ private detectDevTools;
33
+ /**
34
+ * Handle window resize events to detect DevTools
35
+ */
36
+ private handleResize;
37
+ /**
38
+ * Start monitoring for DevTools usage
39
+ */
40
+ private startMonitoring;
41
+ /**
42
+ * Apply DevTools protection by creating overlay and event blocker
43
+ */
44
+ private applyDevToolsProtection;
45
+ /**
46
+ * Hide sensitive content when DevTools is open
47
+ */
48
+ private hideSensitiveContent;
49
+ /**
50
+ * Restore sensitive content when DevTools is closed
51
+ */
52
+ private restoreSensitiveContent;
53
+ /**
54
+ * Remove DevTools protection
55
+ */
56
+ private removeDevToolsProtection;
57
+ /**
58
+ * Apply the protection strategy
59
+ */
60
+ apply(): void;
61
+ /**
62
+ * Remove the protection strategy
63
+ * Override the base implementation to handle additional cleanup
64
+ */
65
+ remove(): void;
66
+ /**
67
+ * Update DevTools protection options
68
+ * @param options New options
69
+ */
70
+ updateOptions(options: Record<string, unknown>): void;
71
+ /**
72
+ * Set debug mode
73
+ * @param enabled Whether debug mode should be enabled
74
+ */
75
+ setDebugMode(enabled: boolean): void;
76
+ /**
77
+ * Check if strategy is applied
78
+ */
79
+ isApplied(): boolean;
80
+ /**
81
+ * Check if debug mode is enabled
82
+ * @returns True if debug mode is enabled
83
+ */
84
+ isDebugEnabled(): boolean;
85
+ }