@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,211 @@
|
|
|
1
|
+
import { getBrowser, isBrowser, isMobile } from "../environment";
|
|
2
|
+
import { AbstractDevToolsDetector, DetectorErrorType } from "./AbstractDevToolsDetector";
|
|
3
|
+
export class DebuggerDetector extends AbstractDevToolsDetector {
|
|
4
|
+
/**
|
|
5
|
+
* Create a new DebuggerDetector
|
|
6
|
+
* @param options Configuration options
|
|
7
|
+
*/
|
|
8
|
+
constructor(options = {}) {
|
|
9
|
+
super("DebuggerDetector", options);
|
|
10
|
+
this.worker = null;
|
|
11
|
+
this.timeoutId = null;
|
|
12
|
+
this.isInitialized = false;
|
|
13
|
+
this.initializationAttempts = 0;
|
|
14
|
+
this.maxInitAttempts = 3;
|
|
15
|
+
this.timeoutDuration = options.timeoutDuration || 500;
|
|
16
|
+
// Delay worker initialization to avoid issues during page load
|
|
17
|
+
setTimeout(() => {
|
|
18
|
+
this.initWorker();
|
|
19
|
+
}, 100);
|
|
20
|
+
this.logger.log(`Initialized with timeout duration: ${this.timeoutDuration}ms`);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Initialize the worker for DevTools detection
|
|
24
|
+
*/
|
|
25
|
+
initWorker() {
|
|
26
|
+
if (this.isInitialized || this.initializationAttempts >= this.maxInitAttempts)
|
|
27
|
+
return;
|
|
28
|
+
this.initializationAttempts++;
|
|
29
|
+
this.safeExecute("initWorker", DetectorErrorType.INITIALIZATION_ERROR, () => {
|
|
30
|
+
// Create a blob URL for the worker script instead of using a file URL
|
|
31
|
+
// This avoids MIME type issues with server responses
|
|
32
|
+
const workerScript = `
|
|
33
|
+
// Worker script for DevTools detection
|
|
34
|
+
self.onmessage = function(e) {
|
|
35
|
+
if (e.data === "checkDevTools") {
|
|
36
|
+
// Send immediate response (opening heartbeat)
|
|
37
|
+
self.postMessage("heartbeatStart");
|
|
38
|
+
|
|
39
|
+
// This will pause execution if DevTools is open
|
|
40
|
+
debugger;
|
|
41
|
+
|
|
42
|
+
// If we reach here without significant delay, DevTools is closed
|
|
43
|
+
self.postMessage("heartbeatEnd");
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
`;
|
|
47
|
+
// Create a blob URL from the script
|
|
48
|
+
const blob = new Blob([workerScript], { type: "application/javascript" });
|
|
49
|
+
const workerUrl = URL.createObjectURL(blob);
|
|
50
|
+
// Create the worker using the blob URL
|
|
51
|
+
this.worker = new Worker(workerUrl);
|
|
52
|
+
this.worker.onmessage = (e) => {
|
|
53
|
+
if (e.data === "heartbeatStart") {
|
|
54
|
+
// First heartbeat received, now waiting for second
|
|
55
|
+
this.logger.log("Received opening heartbeat");
|
|
56
|
+
}
|
|
57
|
+
else if (e.data === "heartbeatEnd") {
|
|
58
|
+
// Second heartbeat received, DevTools is closed
|
|
59
|
+
if (this.timeoutId !== null) {
|
|
60
|
+
clearTimeout(this.timeoutId);
|
|
61
|
+
this.timeoutId = null;
|
|
62
|
+
}
|
|
63
|
+
this.isChecking = false;
|
|
64
|
+
this.updateDevToolsState(false);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
this.worker.onerror = (error) => {
|
|
68
|
+
this.logger.error("Worker error:", error);
|
|
69
|
+
this.isChecking = false;
|
|
70
|
+
// Try to recreate the worker
|
|
71
|
+
this.terminateWorker();
|
|
72
|
+
// Retry initialization with exponential backoff
|
|
73
|
+
setTimeout(() => {
|
|
74
|
+
this.initWorker();
|
|
75
|
+
}, Math.min(1000 * Math.pow(2, this.initializationAttempts - 1), 10000));
|
|
76
|
+
};
|
|
77
|
+
// Mark as initialized
|
|
78
|
+
this.isInitialized = true;
|
|
79
|
+
// Perform a test check to ensure everything is working
|
|
80
|
+
setTimeout(() => {
|
|
81
|
+
this.performTestCheck();
|
|
82
|
+
}, 200);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Perform a test check to ensure the worker is functioning correctly
|
|
87
|
+
*/
|
|
88
|
+
performTestCheck() {
|
|
89
|
+
if (!this.worker || !this.isInitialized)
|
|
90
|
+
return;
|
|
91
|
+
this.safeExecute("performTestCheck", DetectorErrorType.DETECTION_ERROR, () => {
|
|
92
|
+
let testTimeoutId = null;
|
|
93
|
+
let testCompleted = false;
|
|
94
|
+
// Send message to worker
|
|
95
|
+
this.worker.postMessage("checkDevTools");
|
|
96
|
+
// Set a short timeout
|
|
97
|
+
testTimeoutId = window.setTimeout(() => {
|
|
98
|
+
// If we reach here and test isn't completed, something might be wrong
|
|
99
|
+
if (!testCompleted) {
|
|
100
|
+
this.logger.log("Test check timed out, worker might not be responding correctly");
|
|
101
|
+
}
|
|
102
|
+
testTimeoutId = null;
|
|
103
|
+
}, 100);
|
|
104
|
+
// Set up a one-time message handler for the test
|
|
105
|
+
const originalOnMessage = this.worker.onmessage;
|
|
106
|
+
this.worker.onmessage = (e) => {
|
|
107
|
+
if (e.data === "heartbeatEnd") {
|
|
108
|
+
testCompleted = true;
|
|
109
|
+
if (testTimeoutId !== null) {
|
|
110
|
+
clearTimeout(testTimeoutId);
|
|
111
|
+
testTimeoutId = null;
|
|
112
|
+
}
|
|
113
|
+
this.logger.log("Test check completed successfully");
|
|
114
|
+
// Restore original handler
|
|
115
|
+
this.worker.onmessage = originalOnMessage;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
// Pass through to original handler
|
|
119
|
+
if (originalOnMessage) {
|
|
120
|
+
originalOnMessage.call(this.worker, e);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Check if DevTools is open
|
|
128
|
+
*/
|
|
129
|
+
checkDevTools() {
|
|
130
|
+
if (this.isChecking || !this.worker || !this.isInitialized)
|
|
131
|
+
return;
|
|
132
|
+
this.isChecking = true;
|
|
133
|
+
this.safeExecute("checkDevTools", DetectorErrorType.DETECTION_ERROR, () => {
|
|
134
|
+
// Send message to worker
|
|
135
|
+
this.worker.postMessage("checkDevTools");
|
|
136
|
+
// Set timeout for missing second heartbeat
|
|
137
|
+
this.timeoutId = window.setTimeout(() => {
|
|
138
|
+
// If we reach here, the worker is paused at debugger
|
|
139
|
+
// which means DevTools is open
|
|
140
|
+
this.isChecking = false;
|
|
141
|
+
this.updateDevToolsState(true);
|
|
142
|
+
}, this.timeoutDuration);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Terminate the worker and clean up resources
|
|
147
|
+
*/
|
|
148
|
+
terminateWorker() {
|
|
149
|
+
this.safeExecute("terminateWorker", DetectorErrorType.DISPOSAL_ERROR, () => {
|
|
150
|
+
if (this.worker) {
|
|
151
|
+
this.worker.terminate();
|
|
152
|
+
this.worker = null;
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Clean up resources
|
|
158
|
+
*/
|
|
159
|
+
dispose() {
|
|
160
|
+
this.safeExecute("dispose", DetectorErrorType.DISPOSAL_ERROR, () => {
|
|
161
|
+
if (this.timeoutId !== null) {
|
|
162
|
+
clearTimeout(this.timeoutId);
|
|
163
|
+
this.timeoutId = null;
|
|
164
|
+
}
|
|
165
|
+
this.terminateWorker();
|
|
166
|
+
this.isInitialized = false;
|
|
167
|
+
this.logger.log("Disposed");
|
|
168
|
+
});
|
|
169
|
+
super.dispose();
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Check if this detector is supported in the current browser
|
|
173
|
+
* @returns True if supported
|
|
174
|
+
*/
|
|
175
|
+
static isSupported() {
|
|
176
|
+
if (!isBrowser())
|
|
177
|
+
return false;
|
|
178
|
+
// Check if Web Workers are supported
|
|
179
|
+
if (typeof Worker === "undefined") {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
// Check if Blob URLs are supported (needed for creating worker scripts)
|
|
183
|
+
if (typeof Blob === "undefined" || typeof URL === "undefined" || typeof URL.createObjectURL !== "function") {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
// Check if we can create a worker from a blob URL
|
|
187
|
+
// Some environments restrict this for security reasons
|
|
188
|
+
try {
|
|
189
|
+
const blob = new Blob([""], { type: "application/javascript" });
|
|
190
|
+
const url = URL.createObjectURL(blob);
|
|
191
|
+
const worker = new Worker(url);
|
|
192
|
+
worker.terminate();
|
|
193
|
+
URL.revokeObjectURL(url);
|
|
194
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
// If we can't create a worker, this detector won't work
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
// Get browser info
|
|
201
|
+
const browser = getBrowser();
|
|
202
|
+
const isMobileDevice = isMobile();
|
|
203
|
+
// This detector works best in Chromium-based browsers
|
|
204
|
+
// It's generally reliable across most desktop browsers
|
|
205
|
+
// Some older iOS browsers have issues with the debugger statement
|
|
206
|
+
if (isMobileDevice && browser.name === "safari" && Number.parseInt(browser.version, 10) < 14) {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { AbstractDevToolsDetector } from "./AbstractDevToolsDetector";
|
|
2
|
+
import { DevToolsDetectorOptions } from "./detectorInterface";
|
|
3
|
+
/**
|
|
4
|
+
* Options for the DefineGetterDetector
|
|
5
|
+
*/
|
|
6
|
+
export interface DefineGetterDetectorOptions extends DevToolsDetectorOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Check interval in milliseconds
|
|
9
|
+
*/
|
|
10
|
+
checkInterval?: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Detector that uses property getter access to detect DevTools
|
|
14
|
+
* This works by creating an element with a getter that triggers when DevTools inspects it
|
|
15
|
+
*/
|
|
16
|
+
export declare class DefineGetterDetector extends AbstractDevToolsDetector {
|
|
17
|
+
private checkInterval;
|
|
18
|
+
private div;
|
|
19
|
+
private detectionCount;
|
|
20
|
+
private lastDetectionTime;
|
|
21
|
+
/**
|
|
22
|
+
* Create a new DefineGetterDetector
|
|
23
|
+
* @param options Configuration options
|
|
24
|
+
*/
|
|
25
|
+
constructor(options?: DefineGetterDetectorOptions);
|
|
26
|
+
/**
|
|
27
|
+
* Initialize the div element with custom getter for detection
|
|
28
|
+
*/
|
|
29
|
+
private initDetectionElement;
|
|
30
|
+
/**
|
|
31
|
+
* Handle getter access - called when the 'id' property is accessed
|
|
32
|
+
*/
|
|
33
|
+
private handleGetterAccess;
|
|
34
|
+
/**
|
|
35
|
+
* Check if DevTools is open by logging the div element
|
|
36
|
+
* When the element is logged, DevTools will access its properties including 'id'
|
|
37
|
+
*/
|
|
38
|
+
checkDevTools(): void;
|
|
39
|
+
/**
|
|
40
|
+
* Clean up resources
|
|
41
|
+
*/
|
|
42
|
+
dispose(): void;
|
|
43
|
+
/**
|
|
44
|
+
* Check if this detector is supported in the current browser
|
|
45
|
+
* @returns True if supported
|
|
46
|
+
*/
|
|
47
|
+
static isSupported(): boolean;
|
|
48
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { isBrowser } from "../environment";
|
|
2
|
+
import { AbstractDevToolsDetector, DetectorErrorType } from "./AbstractDevToolsDetector";
|
|
3
|
+
/**
|
|
4
|
+
* Detector that uses property getter access to detect DevTools
|
|
5
|
+
* This works by creating an element with a getter that triggers when DevTools inspects it
|
|
6
|
+
*/
|
|
7
|
+
export class DefineGetterDetector extends AbstractDevToolsDetector {
|
|
8
|
+
/**
|
|
9
|
+
* Create a new DefineGetterDetector
|
|
10
|
+
* @param options Configuration options
|
|
11
|
+
*/
|
|
12
|
+
constructor(options = {}) {
|
|
13
|
+
super("DefineGetterDetector", options);
|
|
14
|
+
this.div = null;
|
|
15
|
+
this.detectionCount = 0;
|
|
16
|
+
this.lastDetectionTime = 0;
|
|
17
|
+
this.checkInterval = options.checkInterval || 1000;
|
|
18
|
+
// Initialize the div element with custom getter
|
|
19
|
+
this.initDetectionElement();
|
|
20
|
+
this.logger.log("Initialized with interval:", this.checkInterval);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Initialize the div element with custom getter for detection
|
|
24
|
+
*/
|
|
25
|
+
initDetectionElement() {
|
|
26
|
+
this.safeExecute("initDetectionElement", DetectorErrorType.INITIALIZATION_ERROR, () => {
|
|
27
|
+
if (!isBrowser())
|
|
28
|
+
return;
|
|
29
|
+
this.div = document.createElement("div");
|
|
30
|
+
// Define a getter for the 'id' property that will be triggered when DevTools inspects the element
|
|
31
|
+
// Using both __defineGetter__ (for older browsers) and Object.defineProperty
|
|
32
|
+
try {
|
|
33
|
+
// This is a non-standard method but works in some browsers
|
|
34
|
+
;
|
|
35
|
+
this.div.__defineGetter__("id", () => {
|
|
36
|
+
this.handleGetterAccess();
|
|
37
|
+
return "";
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
// Ignore errors for browsers that don't support __defineGetter__
|
|
42
|
+
this.logger.log("__defineGetter__ not supported, using Object.defineProperty", e);
|
|
43
|
+
}
|
|
44
|
+
// Standard way using Object.defineProperty
|
|
45
|
+
Object.defineProperty(this.div, "id", {
|
|
46
|
+
get: () => {
|
|
47
|
+
this.handleGetterAccess();
|
|
48
|
+
return "";
|
|
49
|
+
},
|
|
50
|
+
configurable: true,
|
|
51
|
+
});
|
|
52
|
+
this.logger.log("Detection element initialized");
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Handle getter access - called when the 'id' property is accessed
|
|
57
|
+
*/
|
|
58
|
+
handleGetterAccess() {
|
|
59
|
+
this.safeExecute("handleGetterAccess", DetectorErrorType.DETECTION_ERROR, () => {
|
|
60
|
+
const now = Date.now();
|
|
61
|
+
this.detectionCount++;
|
|
62
|
+
// Avoid triggering too many times in a short period
|
|
63
|
+
if (now - this.lastDetectionTime < 100) {
|
|
64
|
+
this.detectionCount++;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
this.detectionCount = 1;
|
|
68
|
+
}
|
|
69
|
+
this.lastDetectionTime = now;
|
|
70
|
+
// If the getter is accessed multiple times in a short period, DevTools is likely open
|
|
71
|
+
if (this.detectionCount >= 2 && !this.isDevToolsOpen) {
|
|
72
|
+
this.updateDevToolsState(true);
|
|
73
|
+
this.logger.log("DevTools opened (getter accessed multiple times)");
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Check if DevTools is open by logging the div element
|
|
79
|
+
* When the element is logged, DevTools will access its properties including 'id'
|
|
80
|
+
*/
|
|
81
|
+
checkDevTools() {
|
|
82
|
+
if (this.isChecking || !isBrowser() || !this.div)
|
|
83
|
+
return;
|
|
84
|
+
this.isChecking = true;
|
|
85
|
+
this.safeExecute("checkDevTools", DetectorErrorType.DETECTION_ERROR, () => {
|
|
86
|
+
// Log the div element - this will trigger property access in DevTools
|
|
87
|
+
console.log(this.div);
|
|
88
|
+
// Clear console to avoid clutter
|
|
89
|
+
if (!this.debugMode) {
|
|
90
|
+
console.clear();
|
|
91
|
+
}
|
|
92
|
+
// If no detection occurred within a short time after logging,
|
|
93
|
+
// DevTools is likely closed
|
|
94
|
+
setTimeout(() => {
|
|
95
|
+
this.safeExecute("checkDevToolsTimeout", DetectorErrorType.DETECTION_ERROR, () => {
|
|
96
|
+
const now = Date.now();
|
|
97
|
+
if (now - this.lastDetectionTime > 500 && this.isDevToolsOpen) {
|
|
98
|
+
this.updateDevToolsState(false);
|
|
99
|
+
this.logger.log("DevTools closed (no recent getter access)");
|
|
100
|
+
}
|
|
101
|
+
this.isChecking = false;
|
|
102
|
+
});
|
|
103
|
+
}, 100);
|
|
104
|
+
});
|
|
105
|
+
// Ensure isChecking is reset if an error occurs
|
|
106
|
+
if (this.isChecking) {
|
|
107
|
+
setTimeout(() => {
|
|
108
|
+
this.isChecking = false;
|
|
109
|
+
}, 200);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Clean up resources
|
|
114
|
+
*/
|
|
115
|
+
dispose() {
|
|
116
|
+
this.safeExecute("dispose", DetectorErrorType.DISPOSAL_ERROR, () => {
|
|
117
|
+
this.div = null;
|
|
118
|
+
this.logger.log("Disposed");
|
|
119
|
+
});
|
|
120
|
+
super.dispose();
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Check if this detector is supported in the current browser
|
|
124
|
+
* @returns True if supported
|
|
125
|
+
*/
|
|
126
|
+
static isSupported() {
|
|
127
|
+
if (!isBrowser())
|
|
128
|
+
return false;
|
|
129
|
+
try {
|
|
130
|
+
// Test if we can define getters on an element
|
|
131
|
+
const testDiv = document.createElement("div");
|
|
132
|
+
let testValue = false;
|
|
133
|
+
Object.defineProperty(testDiv, "testProp", {
|
|
134
|
+
get: () => {
|
|
135
|
+
testValue = true;
|
|
136
|
+
return "";
|
|
137
|
+
},
|
|
138
|
+
configurable: true,
|
|
139
|
+
});
|
|
140
|
+
// Access the property to see if the getter works
|
|
141
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
142
|
+
const value = testDiv.testProp;
|
|
143
|
+
return testValue;
|
|
144
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
145
|
+
}
|
|
146
|
+
catch (e) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interface for all DevTools detector implementations
|
|
3
|
+
*/
|
|
4
|
+
export interface DevToolsDetector {
|
|
5
|
+
/**
|
|
6
|
+
* Check if DevTools is currently open
|
|
7
|
+
*/
|
|
8
|
+
checkDevTools(): void;
|
|
9
|
+
/**
|
|
10
|
+
* Get the current DevTools state
|
|
11
|
+
* @returns True if DevTools is open
|
|
12
|
+
*/
|
|
13
|
+
isOpen(): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Clean up resources
|
|
16
|
+
*/
|
|
17
|
+
dispose(): void;
|
|
18
|
+
/**
|
|
19
|
+
* Set debug mode
|
|
20
|
+
* @param enabled Whether debug mode should be enabled
|
|
21
|
+
*/
|
|
22
|
+
setDebugMode(enabled: boolean): void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Common options for all DevTools detectors
|
|
26
|
+
*/
|
|
27
|
+
export interface DevToolsDetectorOptions {
|
|
28
|
+
/**
|
|
29
|
+
* Callback function when DevTools state changes
|
|
30
|
+
*/
|
|
31
|
+
onDevToolsChange?: (isOpen: boolean) => void;
|
|
32
|
+
/**
|
|
33
|
+
* Enable debug mode for troubleshooting
|
|
34
|
+
*/
|
|
35
|
+
debugMode?: boolean;
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { DevToolsDetectorOptions } from "./detectorInterface";
|
|
2
|
+
import { AbstractDevToolsDetector } from "./AbstractDevToolsDetector";
|
|
3
|
+
/**
|
|
4
|
+
* Type for detector types
|
|
5
|
+
*/
|
|
6
|
+
export type DetectorType = "debugger" | "timing" | "dateToString" | "funcToString" | "regToString" | "defineGetter" | "debugLib" | "size";
|
|
7
|
+
/**
|
|
8
|
+
* Options for the DevTools detector manager
|
|
9
|
+
*/
|
|
10
|
+
export interface DetectorManagerOptions extends DevToolsDetectorOptions {
|
|
11
|
+
/**
|
|
12
|
+
* Enable specific detection methods
|
|
13
|
+
*/
|
|
14
|
+
enabledDetectors?: DetectorType[];
|
|
15
|
+
/**
|
|
16
|
+
* Timeout duration for the debugger detector
|
|
17
|
+
*/
|
|
18
|
+
debuggerTimeoutDuration?: number;
|
|
19
|
+
/**
|
|
20
|
+
* Check interval in milliseconds
|
|
21
|
+
*/
|
|
22
|
+
checkInterval?: number;
|
|
23
|
+
/**
|
|
24
|
+
* Delay initial check to avoid false positives during page load
|
|
25
|
+
* @default true
|
|
26
|
+
*/
|
|
27
|
+
delayInitialCheck?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Initial check delay in milliseconds
|
|
30
|
+
* @default 1000
|
|
31
|
+
*/
|
|
32
|
+
initialCheckDelay?: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Manager class that coordinates multiple DevTools detection methods
|
|
36
|
+
* **Chrome, Edge, Opera**: Primarily uses `DebuggerDetector` with `DefineGetterDetector` as backup
|
|
37
|
+
* **Firefox**: Uses `TimingDetector` and `RegToStringDetector` for reliable detection
|
|
38
|
+
* **Safari**: Primarily uses `DateToStringDetector` with `DefineGetterDetector` as backup
|
|
39
|
+
* **QQ Browser**: Uses `RegToStringDetector` which works particularly well for this browser
|
|
40
|
+
* **Mobile Browsers**: Always includes `DebugLibDetector` to catch third-party debugging tools
|
|
41
|
+
*/
|
|
42
|
+
export declare class DevToolsDetectorManager extends AbstractDevToolsDetector {
|
|
43
|
+
private detectors;
|
|
44
|
+
private activeDetectors;
|
|
45
|
+
private browserInfo;
|
|
46
|
+
private isMobileDevice;
|
|
47
|
+
private isInitialCheckDone;
|
|
48
|
+
private initialCheckTimeout;
|
|
49
|
+
private delayInitialCheck;
|
|
50
|
+
private initialCheckDelay;
|
|
51
|
+
private static readonly BROWSER_DETECTOR_MAP;
|
|
52
|
+
private static readonly MOBILE_DETECTOR_MAP;
|
|
53
|
+
/**
|
|
54
|
+
* Create a new DevToolsDetectorManager
|
|
55
|
+
* @param options Configuration options
|
|
56
|
+
*/
|
|
57
|
+
constructor(options?: DetectorManagerOptions);
|
|
58
|
+
/**
|
|
59
|
+
* Initialize all available detectors
|
|
60
|
+
*/
|
|
61
|
+
private initializeAllDetectors;
|
|
62
|
+
/**
|
|
63
|
+
* Select which detectors to activate based on browser
|
|
64
|
+
*/
|
|
65
|
+
private selectActiveDetectors;
|
|
66
|
+
/**
|
|
67
|
+
* Handle state change from any detector
|
|
68
|
+
*/
|
|
69
|
+
private handleDetectorChange;
|
|
70
|
+
/**
|
|
71
|
+
* Check if DevTools is open using all active detectors
|
|
72
|
+
*/
|
|
73
|
+
checkDevTools(): void;
|
|
74
|
+
/**
|
|
75
|
+
* Get the current DevTools state
|
|
76
|
+
* @returns True if DevTools is open
|
|
77
|
+
*/
|
|
78
|
+
isOpen(): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Clean up resources for all detectors
|
|
81
|
+
*/
|
|
82
|
+
dispose(): void;
|
|
83
|
+
/**
|
|
84
|
+
* Set debug mode for all detectors
|
|
85
|
+
* @param enabled Whether debug mode should be enabled
|
|
86
|
+
*/
|
|
87
|
+
setDebugMode(enabled: boolean): void;
|
|
88
|
+
}
|