@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,90 @@
|
|
|
1
|
+
import type { CustomEventHandlers, ScreenshotOptions } from "../types";
|
|
2
|
+
import { AbstractStrategy } from "./AbstractStrategy";
|
|
3
|
+
/**
|
|
4
|
+
* Strategy for preventing screenshots and screen captures
|
|
5
|
+
*/
|
|
6
|
+
export declare class ScreenshotStrategy extends AbstractStrategy {
|
|
7
|
+
private keydownHandler;
|
|
8
|
+
private keyupHandler;
|
|
9
|
+
private blurHandler;
|
|
10
|
+
private focusHandler;
|
|
11
|
+
private visibilityHandler;
|
|
12
|
+
private printHandler;
|
|
13
|
+
private beforePrintHandler;
|
|
14
|
+
private fullscreenChangeHandler;
|
|
15
|
+
private lastKeyEvent;
|
|
16
|
+
private options;
|
|
17
|
+
private customHandler?;
|
|
18
|
+
private categories;
|
|
19
|
+
private targetElement;
|
|
20
|
+
private shortcutManager;
|
|
21
|
+
/**
|
|
22
|
+
* Create a new ScreenshotStrategy
|
|
23
|
+
* @param options Options for customizing the screenshot protection
|
|
24
|
+
* @param customHandler Optional custom handler for screenshot attempts
|
|
25
|
+
* @param debugMode Enable debug mode for troubleshooting
|
|
26
|
+
*/
|
|
27
|
+
constructor(options?: ScreenshotOptions, targetElement?: HTMLElement | null, customHandler?: CustomEventHandlers["onScreenshotAttempt"], debugMode?: boolean);
|
|
28
|
+
/**
|
|
29
|
+
* Handle keydown events to detect screenshot attempts
|
|
30
|
+
*/
|
|
31
|
+
private handleKeyDown;
|
|
32
|
+
/**
|
|
33
|
+
* Handle keyup events - specifically for PrintScreen which may only trigger on keyup
|
|
34
|
+
*/
|
|
35
|
+
private handleKeyUp;
|
|
36
|
+
/**
|
|
37
|
+
* Handle window blur event - might indicate screenshot tool activation
|
|
38
|
+
*/
|
|
39
|
+
private handleWindowBlur;
|
|
40
|
+
/**
|
|
41
|
+
* Handle window focus event - remove overlay when focus returns
|
|
42
|
+
*/
|
|
43
|
+
private handleWindowFocus;
|
|
44
|
+
/**
|
|
45
|
+
* Handle visibility change event - might indicate screenshot tool activation
|
|
46
|
+
*/
|
|
47
|
+
private handleVisibilityChange;
|
|
48
|
+
/**
|
|
49
|
+
* Handle print event - might be used for screenshots
|
|
50
|
+
*/
|
|
51
|
+
private handlePrint;
|
|
52
|
+
/**
|
|
53
|
+
* Handle beforeprint event - prepare for print/screenshot
|
|
54
|
+
*/
|
|
55
|
+
private handleBeforePrint;
|
|
56
|
+
/**
|
|
57
|
+
* Handle fullscreen change event - detect entering fullscreen
|
|
58
|
+
*/
|
|
59
|
+
private handleFullscreenChange;
|
|
60
|
+
/**
|
|
61
|
+
* Exit fullscreen mode if currently in fullscreen
|
|
62
|
+
*/
|
|
63
|
+
private exitFullscreen;
|
|
64
|
+
/**
|
|
65
|
+
* Show a notification that fullscreen mode is disabled
|
|
66
|
+
*/
|
|
67
|
+
private showScreenshotNotification;
|
|
68
|
+
/**
|
|
69
|
+
* Show a notification that fullscreen mode is disabled
|
|
70
|
+
*/
|
|
71
|
+
private showFullscreenNotification;
|
|
72
|
+
/**
|
|
73
|
+
* Apply screenshot protection
|
|
74
|
+
*/
|
|
75
|
+
apply(): void;
|
|
76
|
+
/**
|
|
77
|
+
* Remove screenshot protection
|
|
78
|
+
*/
|
|
79
|
+
remove(): void;
|
|
80
|
+
/**
|
|
81
|
+
* Update screenshot options
|
|
82
|
+
* @param options New screenshot options
|
|
83
|
+
*/
|
|
84
|
+
updateOptions(options: Record<string, unknown>): void;
|
|
85
|
+
/**
|
|
86
|
+
* Set debug mode
|
|
87
|
+
* @param enabled Whether debug mode should be enabled
|
|
88
|
+
*/
|
|
89
|
+
setDebugMode(enabled: boolean): void;
|
|
90
|
+
}
|
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
import { isBrowser, isPrintSupported, isBeforePrintSupported } from "../utils/environment";
|
|
2
|
+
import { AbstractStrategy, StrategyErrorType } from "./AbstractStrategy";
|
|
3
|
+
import { ShortcutCategory } from "../utils/keyboardShortcutManager/keyboardShortcuts";
|
|
4
|
+
import { KeyboardShortcutManager } from "../utils/keyboardShortcutManager/keyboardShortcutManager";
|
|
5
|
+
import { ProtectionEventType } from "../core/mediator/protection-event";
|
|
6
|
+
import { timeoutManager } from "../utils/timeoutManager";
|
|
7
|
+
/**
|
|
8
|
+
* Strategy for preventing screenshots and screen captures
|
|
9
|
+
*/
|
|
10
|
+
export class ScreenshotStrategy extends AbstractStrategy {
|
|
11
|
+
/**
|
|
12
|
+
* Create a new ScreenshotStrategy
|
|
13
|
+
* @param options Options for customizing the screenshot protection
|
|
14
|
+
* @param customHandler Optional custom handler for screenshot attempts
|
|
15
|
+
* @param debugMode Enable debug mode for troubleshooting
|
|
16
|
+
*/
|
|
17
|
+
constructor(options, targetElement = null, customHandler, debugMode = false) {
|
|
18
|
+
super("ScreenshotStrategy", debugMode);
|
|
19
|
+
this.lastKeyEvent = 0;
|
|
20
|
+
this.categories = [ShortcutCategory.SCREENSHOT, ShortcutCategory.FULLSCREEN];
|
|
21
|
+
this.targetElement = null;
|
|
22
|
+
this.options = {
|
|
23
|
+
showOverlay: true,
|
|
24
|
+
overlayOptions: {
|
|
25
|
+
title: "SCREENSHOT PROTECTED",
|
|
26
|
+
textColor: "white",
|
|
27
|
+
backgroundColor: "rgba(255, 0, 0, 0.7)",
|
|
28
|
+
fontSize: "48px",
|
|
29
|
+
duration: 1000,
|
|
30
|
+
},
|
|
31
|
+
hideContent: true,
|
|
32
|
+
preventFullscreen: true,
|
|
33
|
+
fullscreenMessage: "Fullscreen mode is disabled for security reasons",
|
|
34
|
+
...options,
|
|
35
|
+
};
|
|
36
|
+
this.customHandler = customHandler;
|
|
37
|
+
this.targetElement = targetElement;
|
|
38
|
+
this.keydownHandler = this.handleKeyDown.bind(this);
|
|
39
|
+
this.keyupHandler = this.handleKeyUp.bind(this);
|
|
40
|
+
this.blurHandler = this.handleWindowBlur.bind(this);
|
|
41
|
+
this.focusHandler = this.handleWindowFocus.bind(this);
|
|
42
|
+
this.visibilityHandler = this.handleVisibilityChange.bind(this);
|
|
43
|
+
this.printHandler = this.handlePrint.bind(this);
|
|
44
|
+
this.beforePrintHandler = this.handleBeforePrint.bind(this);
|
|
45
|
+
this.fullscreenChangeHandler = this.handleFullscreenChange.bind(this);
|
|
46
|
+
// Initialize shortcut manager
|
|
47
|
+
this.shortcutManager = KeyboardShortcutManager.getInstance();
|
|
48
|
+
// Set timeoutManager debug mode to match this strategy's debug mode
|
|
49
|
+
timeoutManager.setDebugMode(this.debugMode);
|
|
50
|
+
this.log("Initialized with options:", {
|
|
51
|
+
preventFullscreen: this.options.preventFullscreen,
|
|
52
|
+
duration: this.options.overlayOptions?.duration,
|
|
53
|
+
hideContent: this.options.hideContent,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Handle keydown events to detect screenshot attempts
|
|
58
|
+
*/
|
|
59
|
+
handleKeyDown(e) {
|
|
60
|
+
return this.safeExecute("handleKeyDown", StrategyErrorType.EVENT_HANDLING, () => {
|
|
61
|
+
if (e.key === "F12") {
|
|
62
|
+
return; // Allow F12 to pass through
|
|
63
|
+
}
|
|
64
|
+
this.lastKeyEvent = Date.now();
|
|
65
|
+
// Use the shortcut manager to detect shortcuts
|
|
66
|
+
const shortcut = this.shortcutManager.matchesShortcut(e, this.categories);
|
|
67
|
+
if (shortcut) {
|
|
68
|
+
// Prevent the default action
|
|
69
|
+
e.preventDefault();
|
|
70
|
+
e.stopPropagation();
|
|
71
|
+
this.log(`Detected keyboard shortcut: ${shortcut.id} (${this.shortcutManager.getShortcutDescription(shortcut)})`);
|
|
72
|
+
// Handle based on category
|
|
73
|
+
if (shortcut.category === ShortcutCategory.SCREENSHOT) {
|
|
74
|
+
// Publish screenshot attempt event
|
|
75
|
+
if (this.mediator) {
|
|
76
|
+
this.showScreenshotNotification();
|
|
77
|
+
}
|
|
78
|
+
// Call custom handler if provided
|
|
79
|
+
if (this.customHandler) {
|
|
80
|
+
this.customHandler(e);
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
else if (shortcut.category === ShortcutCategory.FULLSCREEN && this.options.preventFullscreen) {
|
|
85
|
+
// Exit fullscreen if currently in fullscreen mode
|
|
86
|
+
this.exitFullscreen();
|
|
87
|
+
// Show anti-fullscreen notification
|
|
88
|
+
this.showFullscreenNotification();
|
|
89
|
+
// Call custom handler if provided
|
|
90
|
+
if (this.customHandler) {
|
|
91
|
+
this.customHandler(e);
|
|
92
|
+
}
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Handle keyup events - specifically for PrintScreen which may only trigger on keyup
|
|
100
|
+
*/
|
|
101
|
+
handleKeyUp(e) {
|
|
102
|
+
return this.safeExecute("handleKeyUp", StrategyErrorType.EVENT_HANDLING, () => {
|
|
103
|
+
// Check for PrintScreen key
|
|
104
|
+
const isPrintScreen = e.key === "PrintScreen" ||
|
|
105
|
+
e.key === "PrtScn" ||
|
|
106
|
+
e.key === "Print" ||
|
|
107
|
+
e.key === "PrtSc" ||
|
|
108
|
+
e.code === "PrintScreen" ||
|
|
109
|
+
e.keyCode === 44;
|
|
110
|
+
// Check for SysRq key
|
|
111
|
+
const isSysRq = e.key === "SysRq" || e.code === "SysRq";
|
|
112
|
+
if (isPrintScreen || isSysRq) {
|
|
113
|
+
this.log("PrintScreen/SysRq key detected on keyup");
|
|
114
|
+
e.preventDefault();
|
|
115
|
+
e.stopPropagation();
|
|
116
|
+
// Publish screenshot attempt event
|
|
117
|
+
if (this.mediator) {
|
|
118
|
+
this.showScreenshotNotification();
|
|
119
|
+
}
|
|
120
|
+
// Call custom handler if provided
|
|
121
|
+
if (this.customHandler) {
|
|
122
|
+
this.customHandler(e);
|
|
123
|
+
}
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Handle window blur event - might indicate screenshot tool activation
|
|
130
|
+
*/
|
|
131
|
+
handleWindowBlur() {
|
|
132
|
+
return this.safeExecute("handleWindowBlur", StrategyErrorType.EVENT_HANDLING, () => {
|
|
133
|
+
const timeSinceLastKey = Date.now() - this.lastKeyEvent;
|
|
134
|
+
// If window loses focus within 500ms of a key event, it might be a screenshot tool
|
|
135
|
+
if (timeSinceLastKey < 500) {
|
|
136
|
+
this.log("Window blur detected shortly after key event, possible screenshot attempt");
|
|
137
|
+
// Publish screenshot attempt event
|
|
138
|
+
if (this.mediator) {
|
|
139
|
+
this.showScreenshotNotification();
|
|
140
|
+
}
|
|
141
|
+
if (this.customHandler) {
|
|
142
|
+
this.customHandler(new Event("blur"));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Handle window focus event - remove overlay when focus returns
|
|
149
|
+
*/
|
|
150
|
+
handleWindowFocus() {
|
|
151
|
+
return this.safeExecute("handleWindowFocus", StrategyErrorType.EVENT_HANDLING, () => {
|
|
152
|
+
// No need to do anything here - the handler will manage timeouts
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Handle visibility change event - might indicate screenshot tool activation
|
|
157
|
+
*/
|
|
158
|
+
handleVisibilityChange() {
|
|
159
|
+
return this.safeExecute("handleVisibilityChange", StrategyErrorType.EVENT_HANDLING, () => {
|
|
160
|
+
if (document.visibilityState === "hidden") {
|
|
161
|
+
const timeSinceLastKey = Date.now() - this.lastKeyEvent;
|
|
162
|
+
// If document becomes hidden within 500ms of a key event, it might be a screenshot tool
|
|
163
|
+
if (timeSinceLastKey < 500) {
|
|
164
|
+
this.log("Visibility change detected shortly after key event, possible screenshot attempt");
|
|
165
|
+
// Publish screenshot attempt event
|
|
166
|
+
if (this.mediator) {
|
|
167
|
+
this.showScreenshotNotification();
|
|
168
|
+
}
|
|
169
|
+
if (this.customHandler) {
|
|
170
|
+
this.customHandler(new Event("visibilitychange"));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Handle print event - might be used for screenshots
|
|
178
|
+
*/
|
|
179
|
+
handlePrint() {
|
|
180
|
+
return this.safeExecute("handlePrint", StrategyErrorType.EVENT_HANDLING, () => {
|
|
181
|
+
this.log("Print event detected, possible screenshot attempt");
|
|
182
|
+
// Publish screenshot attempt event
|
|
183
|
+
if (this.mediator) {
|
|
184
|
+
this.showScreenshotNotification();
|
|
185
|
+
}
|
|
186
|
+
if (this.customHandler) {
|
|
187
|
+
this.customHandler(new Event("afterprint"));
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Handle beforeprint event - prepare for print/screenshot
|
|
193
|
+
*/
|
|
194
|
+
handleBeforePrint() {
|
|
195
|
+
return this.safeExecute("handleBeforePrint", StrategyErrorType.EVENT_HANDLING, () => {
|
|
196
|
+
this.log("BeforePrint event detected, possible screenshot attempt");
|
|
197
|
+
// Publish screenshot attempt event
|
|
198
|
+
if (this.mediator) {
|
|
199
|
+
this.showScreenshotNotification();
|
|
200
|
+
}
|
|
201
|
+
if (this.customHandler) {
|
|
202
|
+
this.customHandler(new Event("beforeprint"));
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Handle fullscreen change event - detect entering fullscreen
|
|
208
|
+
*/
|
|
209
|
+
handleFullscreenChange() {
|
|
210
|
+
return this.safeExecute("handleFullscreenChange", StrategyErrorType.EVENT_HANDLING, () => {
|
|
211
|
+
if (!this.options.preventFullscreen)
|
|
212
|
+
return;
|
|
213
|
+
// Check if we're in fullscreen mode
|
|
214
|
+
const doc = document; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
215
|
+
const isInFullscreen = doc.fullscreenElement ||
|
|
216
|
+
doc.webkitFullscreenElement ||
|
|
217
|
+
doc.mozFullScreenElement ||
|
|
218
|
+
doc.msFullscreenElement;
|
|
219
|
+
if (isInFullscreen) {
|
|
220
|
+
this.log("Fullscreen mode detected, exiting fullscreen");
|
|
221
|
+
// Exit fullscreen mode
|
|
222
|
+
this.exitFullscreen();
|
|
223
|
+
// Show notification
|
|
224
|
+
this.showFullscreenNotification();
|
|
225
|
+
// Call custom handler if provided
|
|
226
|
+
if (this.customHandler) {
|
|
227
|
+
this.customHandler(new Event("fullscreenchange"));
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Exit fullscreen mode if currently in fullscreen
|
|
234
|
+
*/
|
|
235
|
+
exitFullscreen() {
|
|
236
|
+
return this.safeExecute("exitFullscreen", StrategyErrorType.APPLICATION, () => {
|
|
237
|
+
if (!isBrowser())
|
|
238
|
+
return;
|
|
239
|
+
// Check if document is active and has a fullscreen element before attempting to exit
|
|
240
|
+
try {
|
|
241
|
+
// Only attempt to exit if we're actually in fullscreen mode
|
|
242
|
+
const doc = document; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
243
|
+
const isInFullscreen = doc.fullscreenElement ||
|
|
244
|
+
doc.webkitFullscreenElement ||
|
|
245
|
+
doc.mozFullScreenElement ||
|
|
246
|
+
doc.msFullscreenElement;
|
|
247
|
+
if (isInFullscreen) {
|
|
248
|
+
if (document.exitFullscreen) {
|
|
249
|
+
document.exitFullscreen().catch((err) => {
|
|
250
|
+
// Silently catch errors during exit fullscreen
|
|
251
|
+
this.log("Error exiting fullscreen:", err.message);
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
else if (doc.webkitExitFullscreen) {
|
|
255
|
+
doc.webkitExitFullscreen();
|
|
256
|
+
}
|
|
257
|
+
else if (doc.mozCancelFullScreen) {
|
|
258
|
+
doc.mozCancelFullScreen();
|
|
259
|
+
}
|
|
260
|
+
else if (doc.msExitFullscreen) {
|
|
261
|
+
doc.msExitFullscreen();
|
|
262
|
+
}
|
|
263
|
+
this.log("Exited fullscreen mode");
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
this.log("Not in fullscreen mode, no need to exit");
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
catch (error) {
|
|
270
|
+
this.error("Error checking fullscreen state:", error);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Show a notification that fullscreen mode is disabled
|
|
276
|
+
*/
|
|
277
|
+
showScreenshotNotification() {
|
|
278
|
+
return this.safeExecute("showScreenshotNotification", StrategyErrorType.APPLICATION, () => {
|
|
279
|
+
if (!isBrowser() || !this.mediator)
|
|
280
|
+
return;
|
|
281
|
+
// Publish overlay shown event through the mediator
|
|
282
|
+
this.mediator.publish({
|
|
283
|
+
type: ProtectionEventType.OVERLAY_SHOWN,
|
|
284
|
+
source: this.STRATEGY_NAME,
|
|
285
|
+
timestamp: Date.now(),
|
|
286
|
+
data: {
|
|
287
|
+
strategyName: this.STRATEGY_NAME,
|
|
288
|
+
overlayType: "screenshot_notification",
|
|
289
|
+
options: {
|
|
290
|
+
...this.options.overlayOptions,
|
|
291
|
+
blockEvents: false, // Don't block events, just show a notification
|
|
292
|
+
autoRestore: true,
|
|
293
|
+
},
|
|
294
|
+
priority: 5,
|
|
295
|
+
duration: 3000, // Show for 3 seconds
|
|
296
|
+
},
|
|
297
|
+
});
|
|
298
|
+
this.log("Showed screenshot disabled notification");
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Show a notification that fullscreen mode is disabled
|
|
303
|
+
*/
|
|
304
|
+
showFullscreenNotification() {
|
|
305
|
+
return this.safeExecute("showFullscreenNotification", StrategyErrorType.APPLICATION, () => {
|
|
306
|
+
if (!isBrowser() || !this.mediator)
|
|
307
|
+
return;
|
|
308
|
+
// Publish overlay shown event through the mediator
|
|
309
|
+
this.mediator.publish({
|
|
310
|
+
type: ProtectionEventType.OVERLAY_SHOWN,
|
|
311
|
+
source: this.STRATEGY_NAME,
|
|
312
|
+
timestamp: Date.now(),
|
|
313
|
+
data: {
|
|
314
|
+
strategyName: this.STRATEGY_NAME,
|
|
315
|
+
overlayType: "fullscreen_notification",
|
|
316
|
+
options: {
|
|
317
|
+
...this.options.overlayOptions,
|
|
318
|
+
title: "Fullscreen Disabled",
|
|
319
|
+
message: this.options.fullscreenMessage || "Fullscreen mode is disabled for security reasons",
|
|
320
|
+
blockEvents: false, // Don't block events, just show a notification
|
|
321
|
+
autoRestore: true,
|
|
322
|
+
},
|
|
323
|
+
priority: 5,
|
|
324
|
+
duration: 3000, // Show for 3 seconds
|
|
325
|
+
},
|
|
326
|
+
});
|
|
327
|
+
this.log("Showed fullscreen disabled notification");
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Apply screenshot protection
|
|
332
|
+
*/
|
|
333
|
+
apply() {
|
|
334
|
+
return this.safeExecute("apply", StrategyErrorType.APPLICATION, () => {
|
|
335
|
+
if (this.isAppliedFlag) {
|
|
336
|
+
this.log("Protection already applied");
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (isBrowser()) {
|
|
340
|
+
// Use both keydown and keyup to ensure we catch all PrintScreen events
|
|
341
|
+
this.registerEvent(document, "keydown", this.keydownHandler, {
|
|
342
|
+
capture: true,
|
|
343
|
+
priority: 10,
|
|
344
|
+
});
|
|
345
|
+
this.registerEvent(document, "keyup", this.keyupHandler, {
|
|
346
|
+
capture: true,
|
|
347
|
+
priority: 10,
|
|
348
|
+
});
|
|
349
|
+
// Add window blur/focus detection
|
|
350
|
+
this.registerEvent(window, "blur", this.blurHandler, { priority: 10 });
|
|
351
|
+
this.registerEvent(window, "focus", this.focusHandler, { priority: 10 });
|
|
352
|
+
// Add visibility change detection
|
|
353
|
+
this.registerEvent(document, "visibilitychange", this.visibilityHandler, { priority: 10 });
|
|
354
|
+
// Add print event detection if supported
|
|
355
|
+
if (isPrintSupported()) {
|
|
356
|
+
this.registerEvent(window, "afterprint", this.printHandler, { priority: 10 });
|
|
357
|
+
}
|
|
358
|
+
// Add beforeprint event detection if supported
|
|
359
|
+
if (isBeforePrintSupported()) {
|
|
360
|
+
this.registerEvent(window, "beforeprint", this.beforePrintHandler, { priority: 10 });
|
|
361
|
+
}
|
|
362
|
+
// Add fullscreen change event listeners if preventing fullscreen
|
|
363
|
+
if (this.options.preventFullscreen) {
|
|
364
|
+
this.registerEvent(document, "fullscreenchange", this.fullscreenChangeHandler, {
|
|
365
|
+
priority: 10,
|
|
366
|
+
});
|
|
367
|
+
this.registerEvent(document, "webkitfullscreenchange", this.fullscreenChangeHandler, {
|
|
368
|
+
priority: 10,
|
|
369
|
+
});
|
|
370
|
+
this.registerEvent(document, "mozfullscreenchange", this.fullscreenChangeHandler, {
|
|
371
|
+
priority: 10,
|
|
372
|
+
});
|
|
373
|
+
this.registerEvent(document, "MSFullscreenChange", this.fullscreenChangeHandler, {
|
|
374
|
+
priority: 10,
|
|
375
|
+
});
|
|
376
|
+
// Use a longer delay to ensure the document is fully active
|
|
377
|
+
setTimeout(() => {
|
|
378
|
+
// Use setTimeout to ensure the document is ready and active
|
|
379
|
+
this.exitFullscreen();
|
|
380
|
+
}, 500); // Increased from 0 to 500ms
|
|
381
|
+
}
|
|
382
|
+
this.isAppliedFlag = true;
|
|
383
|
+
this.log("Screenshot protection applied", {
|
|
384
|
+
printSupported: isPrintSupported(),
|
|
385
|
+
beforePrintSupported: isBeforePrintSupported(),
|
|
386
|
+
preventFullscreen: this.options.preventFullscreen,
|
|
387
|
+
registeredEvents: this.eventIds.length,
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Remove screenshot protection
|
|
394
|
+
*/
|
|
395
|
+
remove() {
|
|
396
|
+
return this.safeExecute("remove", StrategyErrorType.REMOVAL, () => {
|
|
397
|
+
if (!this.isAppliedFlag) {
|
|
398
|
+
this.log("Protection not applied");
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
if (isBrowser()) {
|
|
402
|
+
// First attempt - remove by owner using the parent class method
|
|
403
|
+
this.removeEventsByOwner();
|
|
404
|
+
// Second attempt - try direct DOM removal as fallback
|
|
405
|
+
try {
|
|
406
|
+
document.removeEventListener("keydown", this.keydownHandler, { capture: true });
|
|
407
|
+
document.removeEventListener("keyup", this.keyupHandler, { capture: true });
|
|
408
|
+
window.removeEventListener("blur", this.blurHandler);
|
|
409
|
+
window.removeEventListener("focus", this.focusHandler);
|
|
410
|
+
document.removeEventListener("visibilitychange", this.visibilityHandler);
|
|
411
|
+
if (isPrintSupported()) {
|
|
412
|
+
window.removeEventListener("afterprint", this.printHandler);
|
|
413
|
+
}
|
|
414
|
+
if (isBeforePrintSupported()) {
|
|
415
|
+
window.removeEventListener("beforeprint", this.beforePrintHandler);
|
|
416
|
+
}
|
|
417
|
+
if (this.options.preventFullscreen) {
|
|
418
|
+
document.removeEventListener("fullscreenchange", this.fullscreenChangeHandler);
|
|
419
|
+
document.removeEventListener("webkitfullscreenchange", this.fullscreenChangeHandler);
|
|
420
|
+
document.removeEventListener("mozfullscreenchange", this.fullscreenChangeHandler);
|
|
421
|
+
document.removeEventListener("MSFullscreenChange", this.fullscreenChangeHandler);
|
|
422
|
+
}
|
|
423
|
+
this.log("Removed events via direct DOM API");
|
|
424
|
+
}
|
|
425
|
+
catch (domError) {
|
|
426
|
+
// Ignore errors in direct DOM removal
|
|
427
|
+
this.error("Error in fallback DOM removal:", domError);
|
|
428
|
+
}
|
|
429
|
+
// Clear any pending timeout using timeoutManager
|
|
430
|
+
timeoutManager.clearTimeout(`${this.STRATEGY_NAME}-overlay`);
|
|
431
|
+
// Clear tracked event IDs (parent class method will do this too, but being explicit)
|
|
432
|
+
this.eventIds = [];
|
|
433
|
+
this.isAppliedFlag = false;
|
|
434
|
+
this.log("Screenshot protection removed");
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Update screenshot options
|
|
440
|
+
* @param options New screenshot options
|
|
441
|
+
*/
|
|
442
|
+
updateOptions(options) {
|
|
443
|
+
return this.safeExecute("updateOptions", StrategyErrorType.OPTION_UPDATE, () => {
|
|
444
|
+
this.log("Updating options", options);
|
|
445
|
+
const typedOptions = options;
|
|
446
|
+
const wasPreventingFullscreen = this.options.preventFullscreen;
|
|
447
|
+
this.options = {
|
|
448
|
+
...this.options,
|
|
449
|
+
...typedOptions,
|
|
450
|
+
};
|
|
451
|
+
// If fullscreen prevention was toggled, we need to update event listeners
|
|
452
|
+
if (this.isAppliedFlag && wasPreventingFullscreen !== this.options.preventFullscreen) {
|
|
453
|
+
if (this.options.preventFullscreen) {
|
|
454
|
+
// Add fullscreen event listeners
|
|
455
|
+
this.registerEvent(document, "fullscreenchange", this.fullscreenChangeHandler, {
|
|
456
|
+
priority: 10,
|
|
457
|
+
});
|
|
458
|
+
this.registerEvent(document, "webkitfullscreenchange", this.fullscreenChangeHandler, {
|
|
459
|
+
priority: 10,
|
|
460
|
+
});
|
|
461
|
+
this.registerEvent(document, "mozfullscreenchange", this.fullscreenChangeHandler, {
|
|
462
|
+
priority: 10,
|
|
463
|
+
});
|
|
464
|
+
this.registerEvent(document, "MSFullscreenChange", this.fullscreenChangeHandler, {
|
|
465
|
+
priority: 10,
|
|
466
|
+
});
|
|
467
|
+
// Exit fullscreen if already in fullscreen mode
|
|
468
|
+
this.exitFullscreen();
|
|
469
|
+
this.log("Added fullscreen protection");
|
|
470
|
+
}
|
|
471
|
+
else {
|
|
472
|
+
// If we're removing fullscreen prevention while protection is active,
|
|
473
|
+
// it's safest to remove all event listeners and reapply the ones we still need
|
|
474
|
+
const wasApplied = this.isAppliedFlag;
|
|
475
|
+
this.remove();
|
|
476
|
+
if (wasApplied) {
|
|
477
|
+
this.apply();
|
|
478
|
+
}
|
|
479
|
+
this.log("Removed fullscreen protection and reapplied other protections");
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
// Handle categories if present
|
|
483
|
+
if (options.categories !== undefined && Array.isArray(options.categories)) {
|
|
484
|
+
this.categories = options.categories;
|
|
485
|
+
this.log("Updated shortcut categories:", this.categories);
|
|
486
|
+
}
|
|
487
|
+
// Update timeoutManager debug mode if our debug mode changed
|
|
488
|
+
if (options.debugMode !== undefined) {
|
|
489
|
+
timeoutManager.setDebugMode(!!options.debugMode);
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Set debug mode
|
|
495
|
+
* @param enabled Whether debug mode should be enabled
|
|
496
|
+
*/
|
|
497
|
+
setDebugMode(enabled) {
|
|
498
|
+
super.setDebugMode(enabled);
|
|
499
|
+
// Update timeoutManager debug mode
|
|
500
|
+
timeoutManager.setDebugMode(enabled);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { CustomEventHandlers } from "../types";
|
|
2
|
+
import { AbstractStrategy } from "./AbstractStrategy";
|
|
3
|
+
/**
|
|
4
|
+
* Strategy for preventing text selection
|
|
5
|
+
*/
|
|
6
|
+
export declare class SelectionStrategy extends AbstractStrategy {
|
|
7
|
+
private targetElement;
|
|
8
|
+
private styleElement;
|
|
9
|
+
private selectionHandler;
|
|
10
|
+
private dragHandler;
|
|
11
|
+
private customHandler?;
|
|
12
|
+
private preventDrag;
|
|
13
|
+
/**
|
|
14
|
+
* Create a new SelectionStrategy
|
|
15
|
+
* @param targetElement Element to protect (defaults to document.body)
|
|
16
|
+
* @param customHandler Optional custom handler for selection attempts
|
|
17
|
+
* @param debugMode Enable debug mode for troubleshooting
|
|
18
|
+
*/
|
|
19
|
+
constructor(targetElement?: HTMLElement | null, customHandler?: CustomEventHandlers["onSelectionAttempt"], debugMode?: boolean);
|
|
20
|
+
/**
|
|
21
|
+
* Handle selection event
|
|
22
|
+
*/
|
|
23
|
+
private handleSelection;
|
|
24
|
+
/**
|
|
25
|
+
* Handle drag event
|
|
26
|
+
*/
|
|
27
|
+
private handleDrag;
|
|
28
|
+
/**
|
|
29
|
+
* Inject CSS to prevent selection
|
|
30
|
+
*/
|
|
31
|
+
private injectSelectionStyles;
|
|
32
|
+
/**
|
|
33
|
+
* Remove selection-blocking CSS
|
|
34
|
+
*/
|
|
35
|
+
private removeSelectionStyles;
|
|
36
|
+
/**
|
|
37
|
+
* Apply selection protection
|
|
38
|
+
*/
|
|
39
|
+
apply(): void;
|
|
40
|
+
/**
|
|
41
|
+
* Remove selection protection
|
|
42
|
+
*/
|
|
43
|
+
remove(): void;
|
|
44
|
+
/**
|
|
45
|
+
* Update selection protection options
|
|
46
|
+
* @param options New options for selection protection
|
|
47
|
+
*/
|
|
48
|
+
updateOptions(options: Record<string, unknown>): void;
|
|
49
|
+
}
|