@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.
Files changed (118) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +357 -0
  3. package/dist/assess.d.ts +16 -0
  4. package/dist/assess.js +220 -0
  5. package/dist/config/default-extensions-config.json +103 -0
  6. package/dist/core/ContentProtector.d.ts +63 -0
  7. package/dist/core/ContentProtector.js +281 -0
  8. package/dist/core/index.d.ts +1 -0
  9. package/dist/core/index.js +2 -0
  10. package/dist/core/mediator/ContentProtectionMediator.d.ts +86 -0
  11. package/dist/core/mediator/ContentProtectionMediator.js +238 -0
  12. package/dist/core/mediator/eventDataTypes.d.ts +112 -0
  13. package/dist/core/mediator/eventDataTypes.js +23 -0
  14. package/dist/core/mediator/handlers/abstractEventHandler.d.ts +41 -0
  15. package/dist/core/mediator/handlers/abstractEventHandler.js +59 -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/screenShotEventHandlers.d.ts +34 -0
  25. package/dist/core/mediator/handlers/screenShotEventHandlers.js +111 -0
  26. package/dist/core/mediator/protection-event.d.ts +77 -0
  27. package/dist/core/mediator/protection-event.js +32 -0
  28. package/dist/core/mediator/types.d.ts +105 -0
  29. package/dist/core/mediator/types.js +1 -0
  30. package/dist/index.d.ts +10 -0
  31. package/dist/index.js +7 -0
  32. package/dist/otel.d.ts +24 -0
  33. package/dist/otel.js +83 -0
  34. package/dist/policy.d.ts +98 -0
  35. package/dist/policy.js +97 -0
  36. package/dist/strategies/AbstractStrategy.d.ts +124 -0
  37. package/dist/strategies/AbstractStrategy.js +256 -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.d.ts +55 -0
  43. package/dist/strategies/DevToolsStrategy.js +314 -0
  44. package/dist/strategies/ExtensionStrategy.d.ts +66 -0
  45. package/dist/strategies/ExtensionStrategy.js +486 -0
  46. package/dist/strategies/IFrameStrategy.d.ts +49 -0
  47. package/dist/strategies/IFrameStrategy.js +255 -0
  48. package/dist/strategies/KeyboardStrategy.d.ts +35 -0
  49. package/dist/strategies/KeyboardStrategy.js +130 -0
  50. package/dist/strategies/PrintStrategy.d.ts +47 -0
  51. package/dist/strategies/PrintStrategy.js +201 -0
  52. package/dist/strategies/ScreenshotStrategy.d.ts +90 -0
  53. package/dist/strategies/ScreenshotStrategy.js +502 -0
  54. package/dist/strategies/SelectionStrategy.d.ts +49 -0
  55. package/dist/strategies/SelectionStrategy.js +216 -0
  56. package/dist/strategies/WatermarkStrategy.d.ts +56 -0
  57. package/dist/strategies/WatermarkStrategy.js +287 -0
  58. package/dist/strategies/index.d.ts +10 -0
  59. package/dist/strategies/index.js +11 -0
  60. package/dist/types/assessment.d.ts +62 -0
  61. package/dist/types/assessment.js +1 -0
  62. package/dist/types/index.d.ts +278 -0
  63. package/dist/types/index.js +17 -0
  64. package/dist/utils/DOMObserver.d.ts +68 -0
  65. package/dist/utils/DOMObserver.js +134 -0
  66. package/dist/utils/base/LoggableComponent.d.ts +44 -0
  67. package/dist/utils/base/LoggableComponent.js +56 -0
  68. package/dist/utils/detectors/AbstractDevToolsDetector.d.ts +98 -0
  69. package/dist/utils/detectors/AbstractDevToolsDetector.js +127 -0
  70. package/dist/utils/detectors/dateToStringDetector.d.ts +43 -0
  71. package/dist/utils/detectors/dateToStringDetector.js +96 -0
  72. package/dist/utils/detectors/debugLibDetector.d.ts +64 -0
  73. package/dist/utils/detectors/debugLibDetector.js +195 -0
  74. package/dist/utils/detectors/debuggerDetector.d.ts +51 -0
  75. package/dist/utils/detectors/debuggerDetector.js +211 -0
  76. package/dist/utils/detectors/defineGetterDetector.d.ts +48 -0
  77. package/dist/utils/detectors/defineGetterDetector.js +150 -0
  78. package/dist/utils/detectors/detectorInterface.d.ts +36 -0
  79. package/dist/utils/detectors/detectorInterface.js +1 -0
  80. package/dist/utils/detectors/devToolsDetectorManager.d.ts +88 -0
  81. package/dist/utils/detectors/devToolsDetectorManager.js +243 -0
  82. package/dist/utils/detectors/funcToStringDetector.d.ts +43 -0
  83. package/dist/utils/detectors/funcToStringDetector.js +90 -0
  84. package/dist/utils/detectors/regToStringDetector.d.ts +43 -0
  85. package/dist/utils/detectors/regToStringDetector.js +129 -0
  86. package/dist/utils/detectors/sizeDetector.d.ts +54 -0
  87. package/dist/utils/detectors/sizeDetector.js +134 -0
  88. package/dist/utils/detectors/timingDetector.d.ts +55 -0
  89. package/dist/utils/detectors/timingDetector.js +143 -0
  90. package/dist/utils/dom.d.ts +20 -0
  91. package/dist/utils/dom.js +83 -0
  92. package/dist/utils/environment.d.ts +29 -0
  93. package/dist/utils/environment.js +267 -0
  94. package/dist/utils/eventManager.d.ts +162 -0
  95. package/dist/utils/eventManager.js +548 -0
  96. package/dist/utils/index.d.ts +2 -0
  97. package/dist/utils/index.js +3 -0
  98. package/dist/utils/intervalManager.d.ts +91 -0
  99. package/dist/utils/intervalManager.js +221 -0
  100. package/dist/utils/keyboardShortcutManager/keyboardShortcutManager.d.ts +41 -0
  101. package/dist/utils/keyboardShortcutManager/keyboardShortcutManager.js +135 -0
  102. package/dist/utils/keyboardShortcutManager/keyboardShortcuts.d.ts +18 -0
  103. package/dist/utils/keyboardShortcutManager/keyboardShortcuts.js +195 -0
  104. package/dist/utils/logging/simple/Loggable.d.ts +33 -0
  105. package/dist/utils/logging/simple/Loggable.js +1 -0
  106. package/dist/utils/logging/simple/LoggingDelegate.d.ts +42 -0
  107. package/dist/utils/logging/simple/LoggingDelegate.js +53 -0
  108. package/dist/utils/logging/simple/SimpleLoggingService.d.ts +39 -0
  109. package/dist/utils/logging/simple/SimpleLoggingService.js +58 -0
  110. package/dist/utils/orientation.d.ts +15 -0
  111. package/dist/utils/orientation.js +32 -0
  112. package/dist/utils/protectedContentManager.d.ts +155 -0
  113. package/dist/utils/protectedContentManager.js +424 -0
  114. package/dist/utils/securityOverlayManager.d.ts +253 -0
  115. package/dist/utils/securityOverlayManager.js +786 -0
  116. package/dist/utils/timeoutManager.d.ts +50 -0
  117. package/dist/utils/timeoutManager.js +113 -0
  118. package/package.json +61 -0
@@ -0,0 +1,243 @@
1
+ import { DebuggerDetector } from "./debuggerDetector";
2
+ import { TimingDetector } from "./timingDetector";
3
+ import { DateToStringDetector } from "./dateToStringDetector";
4
+ import { FuncToStringDetector } from "./funcToStringDetector";
5
+ import { RegToStringDetector } from "./regToStringDetector";
6
+ import { DefineGetterDetector } from "./defineGetterDetector";
7
+ import { DebugLibDetector } from "./debugLibDetector";
8
+ import { SizeDetector } from "./sizeDetector";
9
+ import { getBrowser, isMobile } from "../environment";
10
+ import { AbstractDevToolsDetector } from "./AbstractDevToolsDetector";
11
+ /**
12
+ * Manager class that coordinates multiple DevTools detection methods
13
+ * **Chrome, Edge, Opera**: Primarily uses `DebuggerDetector` with `DefineGetterDetector` as backup
14
+ * **Firefox**: Uses `TimingDetector` and `RegToStringDetector` for reliable detection
15
+ * **Safari**: Primarily uses `DateToStringDetector` with `DefineGetterDetector` as backup
16
+ * **QQ Browser**: Uses `RegToStringDetector` which works particularly well for this browser
17
+ * **Mobile Browsers**: Always includes `DebugLibDetector` to catch third-party debugging tools
18
+ */
19
+ export class DevToolsDetectorManager extends AbstractDevToolsDetector {
20
+ /**
21
+ * Create a new DevToolsDetectorManager
22
+ * @param options Configuration options
23
+ */
24
+ constructor(options = {}) {
25
+ super('DevToolsDetectorManager', { debugMode: !!options.debugMode });
26
+ this.detectors = new Map();
27
+ this.activeDetectors = [];
28
+ this.isInitialCheckDone = false;
29
+ this.initialCheckTimeout = null;
30
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
31
+ this.onDevToolsChange = options.onDevToolsChange || ((isOpen) => { });
32
+ this.browserInfo = getBrowser();
33
+ this.isMobileDevice = isMobile();
34
+ this.delayInitialCheck = options.delayInitialCheck !== false; // Default to true
35
+ this.initialCheckDelay = options.initialCheckDelay || 1000;
36
+ // Initialize all available detectors
37
+ this.initializeAllDetectors(options);
38
+ // Select appropriate detectors based on browser
39
+ this.selectActiveDetectors(options.enabledDetectors);
40
+ this.logger.log("Initialized for browser:", this.browserInfo.name);
41
+ this.logger.log("Mobile device:", this.isMobileDevice);
42
+ this.logger.log("Active detectors:", Array.from(this.activeDetectors).map((d) => d.constructor.name));
43
+ // Schedule initial check with delay to avoid false positives during page load
44
+ if (this.delayInitialCheck) {
45
+ this.logger.log(`Delaying initial check by ${this.initialCheckDelay}ms`);
46
+ this.initialCheckTimeout = window.setTimeout(() => {
47
+ this.isInitialCheckDone = true;
48
+ this.checkDevTools();
49
+ this.initialCheckTimeout = null;
50
+ this.logger.log("Initial check completed");
51
+ }, this.initialCheckDelay);
52
+ }
53
+ else {
54
+ this.isInitialCheckDone = true;
55
+ }
56
+ }
57
+ /**
58
+ * Initialize all available detectors
59
+ */
60
+ initializeAllDetectors(options) {
61
+ const commonOptions = {
62
+ onDevToolsChange: this.handleDetectorChange.bind(this),
63
+ debugMode: this.logger.isDebugEnabled(),
64
+ };
65
+ // Initialize all detectors but don't activate them yet
66
+ this.detectors.set("debugger", new DebuggerDetector({
67
+ ...commonOptions,
68
+ timeoutDuration: options.debuggerTimeoutDuration || 50,
69
+ }));
70
+ this.detectors.set("timing", new TimingDetector({
71
+ ...commonOptions,
72
+ thresholdMultiplier: 3, // Slightly higher for better reliability
73
+ }));
74
+ if (DateToStringDetector.isSupported()) {
75
+ this.detectors.set("dateToString", new DateToStringDetector({
76
+ ...commonOptions,
77
+ threshold: 2,
78
+ }));
79
+ }
80
+ if (FuncToStringDetector.isSupported()) {
81
+ this.detectors.set("funcToString", new FuncToStringDetector({
82
+ ...commonOptions,
83
+ threshold: 2,
84
+ }));
85
+ }
86
+ if (RegToStringDetector.isSupported()) {
87
+ this.detectors.set("regToString", new RegToStringDetector({
88
+ ...commonOptions,
89
+ timeThreshold: 100,
90
+ }));
91
+ }
92
+ this.detectors.set("defineGetter", new DefineGetterDetector({
93
+ ...commonOptions,
94
+ checkInterval: options.checkInterval || 1000,
95
+ }));
96
+ this.detectors.set("debugLib", new DebugLibDetector({
97
+ ...commonOptions,
98
+ checkInterval: options.checkInterval || 1000,
99
+ }));
100
+ if (SizeDetector.isSupported()) {
101
+ this.detectors.set("size", new SizeDetector({
102
+ ...commonOptions,
103
+ widthThreshold: 200,
104
+ heightThreshold: 300,
105
+ }));
106
+ }
107
+ }
108
+ /**
109
+ * Select which detectors to activate based on browser
110
+ */
111
+ selectActiveDetectors(userEnabledDetectors) {
112
+ // If user specified detectors, use those
113
+ if (userEnabledDetectors && userEnabledDetectors.length > 0) {
114
+ this.activeDetectors = userEnabledDetectors
115
+ .filter((type) => this.detectors.has(type))
116
+ .map((type) => this.detectors.get(type))
117
+ .filter(Boolean);
118
+ this.logger.log("Using user-specified detectors");
119
+ return;
120
+ }
121
+ // Get browser-specific detectors
122
+ let detectorTypes = DevToolsDetectorManager.BROWSER_DETECTOR_MAP[this.browserInfo.name] ||
123
+ DevToolsDetectorManager.BROWSER_DETECTOR_MAP.unknown;
124
+ // Apply mobile overrides if on mobile
125
+ if (this.isMobileDevice) {
126
+ const mobileDetectorTypes = DevToolsDetectorManager.MOBILE_DETECTOR_MAP[this.browserInfo.name] ||
127
+ DevToolsDetectorManager.MOBILE_DETECTOR_MAP.default;
128
+ // Combine with browser-specific detectors, prioritizing mobile ones
129
+ detectorTypes = [...mobileDetectorTypes, ...detectorTypes.filter((type) => !mobileDetectorTypes.includes(type))];
130
+ }
131
+ // Activate the selected detectors
132
+ this.activeDetectors = detectorTypes
133
+ .filter((type) => this.detectors.has(type))
134
+ .map((type) => this.detectors.get(type))
135
+ .filter(Boolean);
136
+ // Always start the continuous detectors
137
+ const continuousDetector = this.detectors.get("debugLib");
138
+ if (continuousDetector) {
139
+ continuousDetector.startDetection();
140
+ }
141
+ this.logger.log("Selected detectors for", this.browserInfo.name, ":", detectorTypes);
142
+ }
143
+ /**
144
+ * Handle state change from any detector
145
+ */
146
+ handleDetectorChange(isOpen) {
147
+ // Skip state changes until initial check is done
148
+ if (!this.isInitialCheckDone) {
149
+ this.logger.log("Ignoring state change before initial check");
150
+ return;
151
+ }
152
+ this.updateDevToolsState(isOpen);
153
+ }
154
+ /**
155
+ * Check if DevTools is open using all active detectors
156
+ */
157
+ checkDevTools() {
158
+ // Skip checks until initial check is done
159
+ if (!this.isInitialCheckDone) {
160
+ this.logger.log("Skipping check before initial delay");
161
+ return;
162
+ }
163
+ for (const detector of this.activeDetectors) {
164
+ detector.checkDevTools();
165
+ // If any detector reports DevTools as open, we can stop checking
166
+ if (detector.isOpen()) {
167
+ break;
168
+ }
169
+ }
170
+ }
171
+ /**
172
+ * Get the current DevTools state
173
+ * @returns True if DevTools is open
174
+ */
175
+ isOpen() {
176
+ return this.isDevToolsOpen;
177
+ }
178
+ /**
179
+ * Clean up resources for all detectors
180
+ */
181
+ dispose() {
182
+ // Clear initial check timeout if it exists
183
+ if (this.initialCheckTimeout !== null) {
184
+ clearTimeout(this.initialCheckTimeout);
185
+ this.initialCheckTimeout = null;
186
+ }
187
+ for (const detector of this.detectors.values()) {
188
+ detector.dispose();
189
+ }
190
+ this.detectors.clear();
191
+ this.activeDetectors = [];
192
+ this.logger.log("Disposed all detectors");
193
+ }
194
+ /**
195
+ * Set debug mode for all detectors
196
+ * @param enabled Whether debug mode should be enabled
197
+ */
198
+ setDebugMode(enabled) {
199
+ this.logger.setDebugMode(enabled);
200
+ for (const detector of this.detectors.values()) {
201
+ detector.setDebugMode(enabled);
202
+ }
203
+ }
204
+ }
205
+ // Browser-specific detector mapping
206
+ DevToolsDetectorManager.BROWSER_DETECTOR_MAP = {
207
+ // Chromium-based browsers
208
+ chrome: ["debugger", "defineGetter"],
209
+ edge: ["debugger", "defineGetter"],
210
+ chromium: ["debugger", "defineGetter"],
211
+ opera: ["debugger", "defineGetter"],
212
+ vivaldi: ["debugger", "defineGetter"],
213
+ // Firefox and derivatives
214
+ firefox: ["timing", "regToString"],
215
+ // Safari
216
+ safari: ["dateToString", "defineGetter"],
217
+ // Chinese browsers
218
+ qq: ["regToString", "debugger"],
219
+ uc: ["debugger", "defineGetter"],
220
+ baidu: ["debugger", "defineGetter"],
221
+ mi: ["debugger", "defineGetter"],
222
+ wechat: ["debugger", "defineGetter"],
223
+ // Korean browsers
224
+ samsung: ["debugger", "defineGetter"],
225
+ whale: ["debugger", "defineGetter"],
226
+ // Russian browsers
227
+ yandex: ["debugger", "defineGetter"],
228
+ // Japanese browsers (typically WebKit/Blink based)
229
+ // Fallback for unknown browsers
230
+ unknown: ["debugger", "defineGetter", "dateToString"],
231
+ };
232
+ // Mobile-specific overrides
233
+ DevToolsDetectorManager.MOBILE_DETECTOR_MAP = {
234
+ // iOS Safari needs special handling
235
+ safari: ["dateToString", "debugLib"],
236
+ // Android Chrome/WebView
237
+ chrome: ["debugger", "debugLib"],
238
+ // UC and QQ browsers on mobile
239
+ uc: ["debugger", "debugLib"],
240
+ qq: ["regToString", "debugLib"],
241
+ // Default for other mobile browsers
242
+ default: ["debugger", "debugLib"],
243
+ };
@@ -0,0 +1,43 @@
1
+ import { AbstractDevToolsDetector } from "./AbstractDevToolsDetector";
2
+ import { DevToolsDetectorOptions } from "./detectorInterface";
3
+ /**
4
+ * Options for the FuncToStringDetector
5
+ */
6
+ export interface FuncToStringDetectorOptions extends DevToolsDetectorOptions {
7
+ /**
8
+ * Threshold for detection (number of toString calls)
9
+ */
10
+ threshold?: number;
11
+ }
12
+ /**
13
+ * Utility class for detecting DevTools using Function.toString behavior
14
+ * This approach works by detecting multiple calls to toString when a function is logged
15
+ * and DevTools is open
16
+ */
17
+ export declare class FuncToStringDetector extends AbstractDevToolsDetector {
18
+ private func;
19
+ private count;
20
+ private threshold;
21
+ /**
22
+ * Create a new FuncToStringDetector
23
+ * @param options Configuration options
24
+ */
25
+ constructor(options?: FuncToStringDetectorOptions);
26
+ /**
27
+ * Initialize the function with custom toString method
28
+ */
29
+ private initFunction;
30
+ /**
31
+ * Check if DevTools is open using Function.toString behavior
32
+ */
33
+ checkDevTools(): void;
34
+ /**
35
+ * Clean up resources
36
+ */
37
+ dispose(): void;
38
+ /**
39
+ * Check if this detector is supported in the current browser
40
+ * @returns True if supported
41
+ */
42
+ static isSupported(): boolean;
43
+ }
@@ -0,0 +1,90 @@
1
+ import { isBrowser, getBrowser, isMobile } from "../environment";
2
+ import { AbstractDevToolsDetector, DetectorErrorType } from "./AbstractDevToolsDetector";
3
+ /**
4
+ * Utility class for detecting DevTools using Function.toString behavior
5
+ * This approach works by detecting multiple calls to toString when a function is logged
6
+ * and DevTools is open
7
+ */
8
+ export class FuncToStringDetector extends AbstractDevToolsDetector {
9
+ /**
10
+ * Create a new FuncToStringDetector
11
+ * @param options Configuration options
12
+ */
13
+ constructor(options = {}) {
14
+ super("FuncToStringDetector", options);
15
+ this.func = null;
16
+ this.count = 0;
17
+ this.threshold = options.threshold || 2;
18
+ // Initialize the function with custom toString
19
+ this.initFunction();
20
+ this.logger.log("Initialized with threshold:", this.threshold);
21
+ }
22
+ /**
23
+ * Initialize the function with custom toString method
24
+ */
25
+ initFunction() {
26
+ this.safeExecute("initFunction", DetectorErrorType.INITIALIZATION_ERROR, () => {
27
+ if (!isBrowser())
28
+ return;
29
+ // Create an empty function
30
+ this.func = () => { };
31
+ // Override toString to count calls
32
+ this.func.toString = () => {
33
+ this.count++;
34
+ this.logger.log(`toString called (${this.count} times)`);
35
+ return "";
36
+ };
37
+ this.logger.log("Function initialized with custom toString");
38
+ });
39
+ }
40
+ /**
41
+ * Check if DevTools is open using Function.toString behavior
42
+ */
43
+ checkDevTools() {
44
+ if (this.isChecking || !isBrowser())
45
+ return;
46
+ this.isChecking = true;
47
+ this.safeExecute("checkDevTools", DetectorErrorType.DETECTION_ERROR, () => {
48
+ // Reset counter
49
+ this.count = 0;
50
+ // Log the function - this will trigger toString
51
+ console.log(this.func);
52
+ // Clear console to avoid clutter
53
+ if (!this.debugMode) {
54
+ console.clear();
55
+ }
56
+ // If toString was called multiple times, DevTools is likely open
57
+ const isOpen = this.count >= this.threshold;
58
+ // Update state and notify listeners if changed
59
+ this.updateDevToolsState(isOpen);
60
+ this.logger.log(`toString called ${this.count} times, threshold: ${this.threshold}`);
61
+ });
62
+ // Ensure isChecking is reset even if an error occurs
63
+ this.isChecking = false;
64
+ }
65
+ /**
66
+ * Clean up resources
67
+ */
68
+ dispose() {
69
+ this.safeExecute("dispose", DetectorErrorType.DISPOSAL_ERROR, () => {
70
+ this.func = null;
71
+ this.logger.log("Disposed");
72
+ });
73
+ super.dispose();
74
+ }
75
+ /**
76
+ * Check if this detector is supported in the current browser
77
+ * @returns True if supported
78
+ */
79
+ static isSupported() {
80
+ if (!isBrowser())
81
+ return false;
82
+ const browser = getBrowser();
83
+ const isMobileDevice = isMobile();
84
+ // Not supported in iOS Chrome or iOS Edge
85
+ if (isMobileDevice && (browser.name === "chrome" || browser.name === "edge")) {
86
+ return false;
87
+ }
88
+ return true;
89
+ }
90
+ }
@@ -0,0 +1,43 @@
1
+ import { AbstractDevToolsDetector } from "./AbstractDevToolsDetector";
2
+ import { DevToolsDetectorOptions } from "./detectorInterface";
3
+ /**
4
+ * Options for the RegToStringDetector
5
+ */
6
+ export interface RegToStringDetectorOptions extends DevToolsDetectorOptions {
7
+ /**
8
+ * Time threshold in milliseconds for QQ Browser detection
9
+ * If two toString calls happen within this time, DevTools is considered open
10
+ */
11
+ timeThreshold?: number;
12
+ }
13
+ /**
14
+ * Utility class for detecting DevTools using RegExp.toString behavior
15
+ * This approach works especially well in Firefox and QQ Browser
16
+ */
17
+ export declare class RegToStringDetector extends AbstractDevToolsDetector {
18
+ private reg;
19
+ private lastCallTime;
20
+ private timeThreshold;
21
+ /**
22
+ * Create a new RegToStringDetector
23
+ * @param options Configuration options
24
+ */
25
+ constructor(options?: RegToStringDetectorOptions);
26
+ /**
27
+ * Initialize the RegExp object with custom toString method
28
+ */
29
+ private initRegExpObject;
30
+ /**
31
+ * Check if DevTools is open using RegExp.toString behavior
32
+ */
33
+ checkDevTools(): void;
34
+ /**
35
+ * Clean up resources
36
+ */
37
+ dispose(): void;
38
+ /**
39
+ * Check if this detector is supported in the current browser
40
+ * @returns True if supported
41
+ */
42
+ static isSupported(): boolean;
43
+ }
@@ -0,0 +1,129 @@
1
+ import { isBrowser, getBrowser } from "../environment";
2
+ import { AbstractDevToolsDetector, DetectorErrorType } from "./AbstractDevToolsDetector";
3
+ /**
4
+ * Utility class for detecting DevTools using RegExp.toString behavior
5
+ * This approach works especially well in Firefox and QQ Browser
6
+ */
7
+ export class RegToStringDetector extends AbstractDevToolsDetector {
8
+ /**
9
+ * Create a new RegToStringDetector
10
+ * @param options Configuration options
11
+ */
12
+ constructor(options = {}) {
13
+ super("RegToStringDetector", options);
14
+ this.reg = null;
15
+ this.lastCallTime = 0;
16
+ this.timeThreshold = options.timeThreshold || 100;
17
+ // Initialize the RegExp object with custom toString
18
+ this.initRegExpObject();
19
+ this.logger.log("Initialized with time threshold:", this.timeThreshold);
20
+ }
21
+ /**
22
+ * Initialize the RegExp object with custom toString method
23
+ */
24
+ initRegExpObject() {
25
+ this.safeExecute("initRegExpObject", DetectorErrorType.INITIALIZATION_ERROR, () => {
26
+ if (!isBrowser())
27
+ return;
28
+ this.reg = /./;
29
+ // Override toString to detect DevTools
30
+ this.reg.toString = () => {
31
+ const browser = getBrowser();
32
+ const isQQBrowser = browser.name === "qq" || navigator.userAgent.toLowerCase().includes("qqbrowser");
33
+ const isFirefox = browser.name === "firefox";
34
+ if (isQQBrowser) {
35
+ // For QQ Browser: When DevTools is closed, toString is called once.
36
+ // When DevTools is open, toString is called twice in quick succession.
37
+ // We use this timing difference to detect if DevTools is open.
38
+ const currentTime = Date.now();
39
+ if (this.lastCallTime && currentTime - this.lastCallTime < this.timeThreshold) {
40
+ // Two calls in quick succession - DevTools is likely open
41
+ this.updateDevToolsState(true);
42
+ this.logger.log("DevTools opened (QQ Browser detection)");
43
+ }
44
+ else {
45
+ // First call or calls too far apart
46
+ this.lastCallTime = currentTime;
47
+ }
48
+ }
49
+ else if (isFirefox) {
50
+ // For Firefox: toString is only called when DevTools is open
51
+ this.updateDevToolsState(true);
52
+ this.logger.log("DevTools opened (Firefox detection)");
53
+ }
54
+ return "";
55
+ };
56
+ this.logger.log("RegExp object initialized");
57
+ });
58
+ }
59
+ /**
60
+ * Check if DevTools is open using RegExp.toString behavior
61
+ */
62
+ checkDevTools() {
63
+ if (this.isChecking || !isBrowser() || !this.reg)
64
+ return;
65
+ this.isChecking = true;
66
+ this.safeExecute("checkDevTools", DetectorErrorType.DETECTION_ERROR, () => {
67
+ // Reset detection state for Firefox
68
+ const browser = getBrowser();
69
+ if (browser.name === "firefox" && this.isDevToolsOpen) {
70
+ this.updateDevToolsState(false);
71
+ }
72
+ // Log the RegExp object - this will trigger toString
73
+ console.log(this.reg);
74
+ // Clear console to avoid clutter
75
+ if (!this.debugMode) {
76
+ console.clear();
77
+ }
78
+ // For browsers other than Firefox and QQ Browser, we need to check if
79
+ // DevTools is closed after a short delay
80
+ setTimeout(() => {
81
+ this.safeExecute("checkDevToolsTimeout", DetectorErrorType.DETECTION_ERROR, () => {
82
+ const browser = getBrowser();
83
+ const isQQBrowser = browser.name === "qq" || navigator.userAgent.toLowerCase().includes("qqbrowser");
84
+ const isFirefox = browser.name === "firefox";
85
+ // If we're not in Firefox or QQ Browser and DevTools is open,
86
+ // check if it should be marked as closed
87
+ if (!isFirefox && !isQQBrowser && this.isDevToolsOpen) {
88
+ const currentTime = Date.now();
89
+ // If no recent toString calls, DevTools is likely closed
90
+ if (currentTime - this.lastCallTime > this.timeThreshold * 2) {
91
+ this.updateDevToolsState(false);
92
+ this.logger.log("DevTools closed");
93
+ }
94
+ }
95
+ this.isChecking = false;
96
+ });
97
+ }, this.timeThreshold * 2);
98
+ });
99
+ // Ensure isChecking is reset if an error occurs
100
+ if (this.isChecking) {
101
+ setTimeout(() => {
102
+ this.isChecking = false;
103
+ }, this.timeThreshold * 3);
104
+ }
105
+ }
106
+ /**
107
+ * Clean up resources
108
+ */
109
+ dispose() {
110
+ this.safeExecute("dispose", DetectorErrorType.DISPOSAL_ERROR, () => {
111
+ this.reg = null;
112
+ this.logger.log("Disposed");
113
+ });
114
+ super.dispose();
115
+ }
116
+ /**
117
+ * Check if this detector is supported in the current browser
118
+ * @returns True if supported
119
+ */
120
+ static isSupported() {
121
+ if (!isBrowser())
122
+ return false;
123
+ const browser = getBrowser();
124
+ const isQQBrowser = browser.name === "qq" || navigator.userAgent.toLowerCase().includes("qqbrowser");
125
+ const isFirefox = browser.name === "firefox";
126
+ // This detector works best in Firefox and QQ Browser
127
+ return isFirefox || isQQBrowser;
128
+ }
129
+ }
@@ -0,0 +1,54 @@
1
+ import { AbstractDevToolsDetector } from "./AbstractDevToolsDetector";
2
+ import { DevToolsDetectorOptions } from "./detectorInterface";
3
+ /**
4
+ * Options for the SizeDetector
5
+ */
6
+ export interface SizeDetectorOptions extends DevToolsDetectorOptions {
7
+ /**
8
+ * Width threshold in pixels
9
+ * If the difference between outer and inner width exceeds this, DevTools is considered open
10
+ */
11
+ widthThreshold?: number;
12
+ /**
13
+ * Height threshold in pixels
14
+ * If the difference between outer and inner height exceeds this, DevTools is considered open
15
+ */
16
+ heightThreshold?: number;
17
+ }
18
+ /**
19
+ * Utility class for detecting DevTools using window size differences
20
+ * This approach works by detecting unusual differences between outer and inner window dimensions
21
+ * that typically occur when DevTools are open
22
+ */
23
+ export declare class SizeDetector extends AbstractDevToolsDetector {
24
+ private widthThreshold;
25
+ private heightThreshold;
26
+ private resizeEventId;
27
+ /**
28
+ * Create a new SizeDetector
29
+ * @param options Configuration options
30
+ */
31
+ constructor(options?: SizeDetectorOptions);
32
+ /**
33
+ * Initialize resize event listener
34
+ */
35
+ private initResizeListener;
36
+ /**
37
+ * Calculate screen zoom ratio
38
+ * @returns The screen zoom ratio or false if it can't be determined
39
+ */
40
+ private getScreenZoomRatio;
41
+ /**
42
+ * Check if DevTools is open using window size differences
43
+ */
44
+ checkDevTools(): void;
45
+ /**
46
+ * Clean up resources
47
+ */
48
+ dispose(): void;
49
+ /**
50
+ * Check if this detector is supported in the current browser
51
+ * @returns True if supported
52
+ */
53
+ static isSupported(): boolean;
54
+ }