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,379 @@
1
+ import { SimpleLoggingService } from "../utils/logging/simple/SimpleLoggingService";
2
+ import { ProtectionEventType } from "../core/mediator/protection-event";
3
+ /**
4
+ * Error types for strategy operations
5
+ */
6
+ export var StrategyErrorType;
7
+ (function (StrategyErrorType) {
8
+ StrategyErrorType["REGISTRATION_ERROR"] = "registration_error";
9
+ StrategyErrorType["UNREGISTRATION_ERROR"] = "unregistration_error";
10
+ StrategyErrorType["APPLICATION_ERROR"] = "application_error";
11
+ StrategyErrorType["REMOVAL_ERROR"] = "removal_error";
12
+ StrategyErrorType["INVALID_STRATEGY"] = "invalid_strategy";
13
+ StrategyErrorType["STRATEGY_NOT_FOUND"] = "strategy_not_found";
14
+ StrategyErrorType["STRATEGY_ALREADY_REGISTERED"] = "strategy_already_registered";
15
+ })(StrategyErrorType || (StrategyErrorType = {}));
16
+ /**
17
+ * Custom error class for strategy registry operations
18
+ */
19
+ export class StrategyRegistryError extends Error {
20
+ constructor(errorType, message, strategyId, originalError) {
21
+ super(message);
22
+ this.errorType = errorType;
23
+ this.strategyId = strategyId;
24
+ this.originalError = originalError;
25
+ this.name = "StrategyRegistryError";
26
+ // Maintain the stack trace
27
+ if (Error.captureStackTrace) {
28
+ Error.captureStackTrace(this, StrategyRegistryError);
29
+ }
30
+ }
31
+ }
32
+ /**
33
+ * Registry for managing protection strategies
34
+ * Provides centralized access and lifecycle management
35
+ */
36
+ export class StrategyRegistry {
37
+ constructor(options = {}) {
38
+ this.COMPONENT_NAME = "StrategyRegistry";
39
+ this.strategies = new Map();
40
+ this.mediator = null;
41
+ this.logger = new SimpleLoggingService(this.COMPONENT_NAME, !!options.debugMode);
42
+ this.logger.log("Initialized");
43
+ }
44
+ /**
45
+ * Set the mediator to communicate with other components
46
+ * @param mediator The protection mediator
47
+ */
48
+ setMediator(mediator) {
49
+ this.mediator = mediator;
50
+ this.logger.log("Mediator set");
51
+ }
52
+ /**
53
+ * Register a strategy with the registry
54
+ * @param id Unique identifier for the strategy
55
+ * @param strategy Strategy instance
56
+ * @returns True if registration was successful
57
+ * @throws StrategyRegistryError if registration fails
58
+ */
59
+ register(id, strategy) {
60
+ try {
61
+ // Validate inputs
62
+ if (!id) {
63
+ throw new StrategyRegistryError(StrategyErrorType.INVALID_STRATEGY, "Strategy ID cannot be empty");
64
+ }
65
+ if (!strategy) {
66
+ throw new StrategyRegistryError(StrategyErrorType.INVALID_STRATEGY, `Strategy instance for "${id}" is invalid`, id);
67
+ }
68
+ if (this.strategies.has(id)) {
69
+ throw new StrategyRegistryError(StrategyErrorType.STRATEGY_ALREADY_REGISTERED, `Strategy with ID "${id}" is already registered`, id);
70
+ }
71
+ // Set mediator on the strategy if it's mediator-aware
72
+ if (this.mediator && 'setMediator' in strategy) {
73
+ strategy.setMediator(this.mediator);
74
+ }
75
+ // Register the strategy
76
+ this.strategies.set(id, strategy);
77
+ this.logger.log(`Registered strategy "${id}"`);
78
+ return true;
79
+ }
80
+ catch (error) {
81
+ // Handle errors
82
+ if (error instanceof StrategyRegistryError) {
83
+ this.logger.warn(error.message);
84
+ // Publish error event through mediator
85
+ this.publishErrorEvent(id, error);
86
+ throw error;
87
+ }
88
+ else {
89
+ const registryError = new StrategyRegistryError(StrategyErrorType.REGISTRATION_ERROR, `Failed to register strategy "${id}": ${error instanceof Error ? error.message : String(error)}`, id, error instanceof Error ? error : undefined);
90
+ this.logger.error(registryError.message);
91
+ // Publish error event through mediator
92
+ this.publishErrorEvent(id, registryError);
93
+ throw registryError;
94
+ }
95
+ }
96
+ }
97
+ /**
98
+ * Unregister a strategy from the registry
99
+ * @param id Strategy ID to unregister
100
+ * @returns True if unregistration was successful
101
+ * @throws StrategyRegistryError if unregistration fails
102
+ */
103
+ unregister(id) {
104
+ try {
105
+ if (!id) {
106
+ throw new StrategyRegistryError(StrategyErrorType.INVALID_STRATEGY, "Strategy ID cannot be empty");
107
+ }
108
+ if (!this.strategies.has(id)) {
109
+ throw new StrategyRegistryError(StrategyErrorType.STRATEGY_NOT_FOUND, `Strategy with ID "${id}" is not registered`, id);
110
+ }
111
+ // Get the strategy and remove it if it's applied
112
+ const strategy = this.strategies.get(id);
113
+ if (strategy.isApplied()) {
114
+ try {
115
+ strategy.remove();
116
+ // Publish removal event through mediator
117
+ this.publishStrategyRemovedEvent(id);
118
+ }
119
+ catch (removeError) {
120
+ this.logger.error(`Error removing strategy "${id}" during unregistration:`, removeError);
121
+ // Publish error event through mediator
122
+ this.publishErrorEvent(id, removeError instanceof Error ? removeError : new Error(String(removeError)));
123
+ // Continue with unregistration despite removal error
124
+ }
125
+ }
126
+ // Remove from map
127
+ this.strategies.delete(id);
128
+ this.logger.log(`Unregistered strategy "${id}"`);
129
+ return true;
130
+ }
131
+ catch (error) {
132
+ // Handle errors
133
+ if (error instanceof StrategyRegistryError) {
134
+ this.logger.warn(error.message);
135
+ // Publish error event through mediator
136
+ this.publishErrorEvent(id, error);
137
+ throw error;
138
+ }
139
+ else {
140
+ const registryError = new StrategyRegistryError(StrategyErrorType.UNREGISTRATION_ERROR, `Failed to unregister strategy "${id}": ${error instanceof Error ? error.message : String(error)}`, id, error instanceof Error ? error : undefined);
141
+ this.logger.error(registryError.message);
142
+ // Publish error event through mediator
143
+ this.publishErrorEvent(id, registryError);
144
+ throw registryError;
145
+ }
146
+ }
147
+ }
148
+ /**
149
+ * Get a strategy by ID
150
+ * @param id Strategy ID
151
+ * @returns The strategy instance or undefined if not found
152
+ */
153
+ getStrategy(id) {
154
+ return this.strategies.get(id);
155
+ }
156
+ /**
157
+ * Check if a strategy is registered
158
+ * @param id Strategy ID
159
+ * @returns True if the strategy is registered
160
+ */
161
+ hasStrategy(id) {
162
+ return this.strategies.has(id);
163
+ }
164
+ /**
165
+ * Apply a specific strategy
166
+ * @param id Strategy ID to apply
167
+ * @returns True if the strategy was applied successfully
168
+ * @throws StrategyRegistryError if application fails
169
+ */
170
+ applyStrategy(id) {
171
+ try {
172
+ if (!id) {
173
+ throw new StrategyRegistryError(StrategyErrorType.INVALID_STRATEGY, "Strategy ID cannot be empty");
174
+ }
175
+ if (!this.strategies.has(id)) {
176
+ throw new StrategyRegistryError(StrategyErrorType.STRATEGY_NOT_FOUND, `Cannot apply strategy "${id}" because it is not registered`, id);
177
+ }
178
+ const strategy = this.strategies.get(id);
179
+ // Only apply if not already applied
180
+ if (!strategy.isApplied()) {
181
+ strategy.apply();
182
+ this.logger.log(`Applied strategy "${id}"`);
183
+ // Publish event through mediator
184
+ this.publishStrategyAppliedEvent(id);
185
+ }
186
+ else {
187
+ this.logger.log(`Strategy "${id}" is already applied`);
188
+ }
189
+ return true;
190
+ }
191
+ catch (error) {
192
+ // Handle errors
193
+ const registryError = new StrategyRegistryError(StrategyErrorType.APPLICATION_ERROR, `Error applying strategy "${id}": ${error instanceof Error ? error.message : String(error)}`, id, error instanceof Error ? error : undefined);
194
+ this.logger.error(registryError.message);
195
+ // Publish error event through mediator
196
+ this.publishErrorEvent(id, registryError);
197
+ throw registryError;
198
+ }
199
+ }
200
+ /**
201
+ * Apply all registered strategies
202
+ * @returns Array of strategy IDs that failed to apply
203
+ */
204
+ applyAllStrategies() {
205
+ const failed = [];
206
+ for (const id of this.strategies.keys()) {
207
+ try {
208
+ this.applyStrategy(id);
209
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
210
+ }
211
+ catch (error) {
212
+ failed.push(id);
213
+ // Error already logged and event published in applyStrategy
214
+ }
215
+ }
216
+ return failed;
217
+ }
218
+ /**
219
+ * Remove a specific strategy
220
+ * @param id Strategy ID to remove
221
+ * @returns True if removal was successful
222
+ * @throws StrategyRegistryError if removal fails
223
+ */
224
+ removeStrategy(id) {
225
+ try {
226
+ if (!id) {
227
+ throw new StrategyRegistryError(StrategyErrorType.INVALID_STRATEGY, "Strategy ID cannot be empty");
228
+ }
229
+ if (!this.strategies.has(id)) {
230
+ throw new StrategyRegistryError(StrategyErrorType.STRATEGY_NOT_FOUND, `Cannot remove strategy "${id}" because it is not registered`, id);
231
+ }
232
+ const strategy = this.strategies.get(id);
233
+ // Only remove if applied
234
+ if (strategy.isApplied()) {
235
+ strategy.remove();
236
+ this.logger.log(`Removed strategy "${id}"`);
237
+ // Publish event through mediator
238
+ this.publishStrategyRemovedEvent(id);
239
+ }
240
+ else {
241
+ this.logger.log(`Strategy "${id}" is not applied, nothing to remove`);
242
+ }
243
+ return true;
244
+ }
245
+ catch (error) {
246
+ // Handle errors
247
+ const registryError = new StrategyRegistryError(StrategyErrorType.REMOVAL_ERROR, `Error removing strategy "${id}": ${error instanceof Error ? error.message : String(error)}`, id, error instanceof Error ? error : undefined);
248
+ this.logger.error(registryError.message);
249
+ // Publish error event through mediator
250
+ this.publishErrorEvent(id, registryError);
251
+ throw registryError;
252
+ }
253
+ }
254
+ /**
255
+ * Remove all registered strategies
256
+ * @returns Array of strategy IDs that failed to remove
257
+ */
258
+ removeAllStrategies() {
259
+ const failed = [];
260
+ for (const id of this.strategies.keys()) {
261
+ try {
262
+ this.removeStrategy(id);
263
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
264
+ }
265
+ catch (error) {
266
+ failed.push(id);
267
+ // Error already logged and event published in removeStrategy
268
+ }
269
+ }
270
+ return failed;
271
+ }
272
+ /**
273
+ * Get all registered strategy IDs
274
+ * @returns Array of strategy IDs
275
+ */
276
+ getStrategyIds() {
277
+ return Array.from(this.strategies.keys());
278
+ }
279
+ /**
280
+ * Get all registered strategies
281
+ * @returns Map of strategy IDs to strategy instances
282
+ */
283
+ getAllStrategies() {
284
+ return new Map(this.strategies);
285
+ }
286
+ /**
287
+ * Get all applied strategies
288
+ * @returns Array of strategy IDs that are currently applied
289
+ */
290
+ getAppliedStrategies() {
291
+ const applied = [];
292
+ for (const [id, strategy] of this.strategies.entries()) {
293
+ if (strategy.isApplied()) {
294
+ applied.push(id);
295
+ }
296
+ }
297
+ return applied;
298
+ }
299
+ /**
300
+ * Set debug mode for all strategies that support it
301
+ * @param enabled Whether debug mode should be enabled
302
+ */
303
+ setDebugMode(enabled) {
304
+ this.logger.setDebugMode(enabled);
305
+ for (const [id, strategy] of this.strategies.entries()) {
306
+ try {
307
+ strategy.setDebugMode(enabled);
308
+ }
309
+ catch (error) {
310
+ this.logger.error(`Error setting debug mode for strategy "${id}":`, error);
311
+ }
312
+ }
313
+ this.logger.log(`Set debug mode to ${enabled} for all strategies`);
314
+ }
315
+ /**
316
+ * Clear the registry (remove all strategies first)
317
+ */
318
+ clear() {
319
+ // Remove all strategies first
320
+ this.removeAllStrategies();
321
+ // Clear the map
322
+ this.strategies.clear();
323
+ this.logger.log("Cleared registry");
324
+ }
325
+ /**
326
+ * Publish a strategy applied event through the mediator
327
+ * @param strategyId ID of the strategy that was applied
328
+ */
329
+ publishStrategyAppliedEvent(strategyId) {
330
+ if (!this.mediator)
331
+ return;
332
+ this.mediator.publish({
333
+ type: ProtectionEventType.STRATEGY_APPLIED,
334
+ source: this.COMPONENT_NAME,
335
+ timestamp: Date.now(),
336
+ data: {
337
+ strategyName: strategyId,
338
+ options: {}
339
+ }
340
+ });
341
+ }
342
+ /**
343
+ * Publish a strategy removed event through the mediator
344
+ * @param strategyId ID of the strategy that was removed
345
+ */
346
+ publishStrategyRemovedEvent(strategyId) {
347
+ if (!this.mediator)
348
+ return;
349
+ this.mediator.publish({
350
+ type: ProtectionEventType.STRATEGY_REMOVED,
351
+ source: this.COMPONENT_NAME,
352
+ timestamp: Date.now(),
353
+ data: {
354
+ strategyName: strategyId,
355
+ reason: "registry_operation"
356
+ }
357
+ });
358
+ }
359
+ /**
360
+ * Publish an error event through the mediator
361
+ * @param strategyId ID of the strategy that had an error
362
+ * @param error The error that occurred
363
+ */
364
+ publishErrorEvent(strategyId, error) {
365
+ if (!this.mediator)
366
+ return;
367
+ this.mediator.publish({
368
+ type: ProtectionEventType.ERROR_OCCURRED,
369
+ source: this.COMPONENT_NAME,
370
+ timestamp: Date.now(),
371
+ data: {
372
+ strategyName: strategyId,
373
+ errorType: error instanceof StrategyRegistryError ? error.errorType : "unknown",
374
+ message: error.message,
375
+ stack: error.stack
376
+ }
377
+ });
378
+ }
379
+ }
@@ -0,0 +1,47 @@
1
+ import type { WatermarkOptions } from "../types";
2
+ import { AbstractStrategy } from "./AbstractStrategy";
3
+ /**
4
+ * Strategy for adding watermarks to content
5
+ */
6
+ export declare class WatermarkStrategy extends AbstractStrategy {
7
+ private targetElement;
8
+ private options;
9
+ private watermarkElements;
10
+ private domObserver;
11
+ private isFullPageWatermark;
12
+ private watermarkContainer;
13
+ private osInfo;
14
+ /**
15
+ * Create a new WatermarkStrategy
16
+ * @param targetElement Element to watermark (defaults to document.body)
17
+ * @param options Watermark options
18
+ * @param debugMode Enable debug mode for troubleshooting
19
+ */
20
+ constructor(options?: WatermarkOptions, targetElement?: HTMLElement | null, debugMode?: boolean);
21
+ /**
22
+ * Create watermarks
23
+ */
24
+ private createWatermarks;
25
+ /**
26
+ * Set up DOM observer to detect watermark removal
27
+ */
28
+ private setupObserver;
29
+ /**
30
+ * Remove watermark elements
31
+ */
32
+ private removeWatermarkElements;
33
+ /**
34
+ * Apply watermark protection
35
+ */
36
+ apply(): void;
37
+ /**
38
+ * Remove watermark protection
39
+ * Override the base implementation to handle additional cleanup
40
+ */
41
+ remove(): void;
42
+ /**
43
+ * Update watermark options
44
+ * @param options New watermark options
45
+ */
46
+ updateOptions(options: Record<string, unknown>): void;
47
+ }
@@ -0,0 +1,273 @@
1
+ import { DomObserver } from "../utils/DOMObserver";
2
+ import { isBrowser, getOS } from "../utils/environment";
3
+ import { AbstractStrategy, StrategyErrorType } from "./AbstractStrategy";
4
+ /**
5
+ * Strategy for adding watermarks to content
6
+ */
7
+ export class WatermarkStrategy extends AbstractStrategy {
8
+ /**
9
+ * Create a new WatermarkStrategy
10
+ * @param targetElement Element to watermark (defaults to document.body)
11
+ * @param options Watermark options
12
+ * @param debugMode Enable debug mode for troubleshooting
13
+ */
14
+ constructor(options, targetElement, debugMode = false) {
15
+ super("WatermarkStrategy", debugMode);
16
+ this.targetElement = null;
17
+ this.watermarkElements = [];
18
+ this.domObserver = null;
19
+ this.isFullPageWatermark = false;
20
+ this.watermarkContainer = null;
21
+ this.targetElement = targetElement || (isBrowser() ? document.body : null);
22
+ this.options = {
23
+ text: "CONFIDENTIAL",
24
+ opacity: 0.15,
25
+ density: 3,
26
+ ...options,
27
+ };
28
+ this.osInfo = getOS();
29
+ this.watermarkElements = [];
30
+ // Determine if we're doing a full-page watermark
31
+ if (isBrowser() && this.targetElement === document.body) {
32
+ this.isFullPageWatermark = true;
33
+ }
34
+ this.log("Initialized with OS:", this.osInfo, "Full-page:", this.isFullPageWatermark);
35
+ }
36
+ /**
37
+ * Create watermarks
38
+ */
39
+ createWatermarks() {
40
+ return this.safeExecute("createWatermarks", StrategyErrorType.APPLICATION, () => {
41
+ if (!this.targetElement || !isBrowser())
42
+ return;
43
+ // Check if watermark container already exists
44
+ const existingContainer = document.querySelector(".content-security-watermark-container");
45
+ if (existingContainer) {
46
+ this.log("Watermark container already exists, removing first");
47
+ if (existingContainer.parentNode) {
48
+ existingContainer.parentNode.removeChild(existingContainer);
49
+ }
50
+ }
51
+ // Clear any existing watermarks
52
+ this.removeWatermarkElements();
53
+ // Calculate density
54
+ const density = Math.min(Math.max(this.options.density || 3, 1), 10);
55
+ const rows = density * 3;
56
+ const cols = density * 3;
57
+ // Create watermark container
58
+ const container = document.createElement("div");
59
+ container.className = "content-security-watermark-container";
60
+ container.setAttribute("data-watermark-id", `watermark-${Date.now()}`);
61
+ this.watermarkContainer = container;
62
+ // Set container styles based on whether it's full-page or element-specific
63
+ if (this.isFullPageWatermark) {
64
+ // Full-page watermark (fixed position covering viewport)
65
+ Object.assign(container.style, {
66
+ position: "fixed",
67
+ top: "0",
68
+ left: "0",
69
+ width: "100%",
70
+ height: "100%",
71
+ overflow: "hidden",
72
+ pointerEvents: "none",
73
+ zIndex: "2147483647", // Max z-index
74
+ userSelect: "none",
75
+ });
76
+ }
77
+ else {
78
+ // Element-specific watermark
79
+ // Get the computed style of the target element
80
+ const targetStyle = window.getComputedStyle(this.targetElement);
81
+ const targetPosition = targetStyle.position;
82
+ // If the target element doesn't have a position set, we need to set it to relative
83
+ // so that our absolutely positioned watermark container stays within it
84
+ if (targetPosition === "static") {
85
+ this.targetElement.style.position = "relative";
86
+ }
87
+ Object.assign(container.style, {
88
+ position: "absolute",
89
+ top: "0",
90
+ left: "0",
91
+ width: "100%",
92
+ height: "100%",
93
+ overflow: "hidden",
94
+ pointerEvents: "none",
95
+ zIndex: "999", // High z-index but not max to avoid breaking page layout
96
+ userSelect: "none",
97
+ });
98
+ }
99
+ // Generate timestamp and user info for watermark
100
+ const timestamp = new Date().toISOString();
101
+ const userInfo = this.options.userId ? ` - User: ${this.options.userId}` : "";
102
+ const watermarkText = `${this.options.text}${userInfo} - ${timestamp}`;
103
+ // Create watermark pattern
104
+ for (let i = 0; i < rows; i++) {
105
+ for (let j = 0; j < cols; j++) {
106
+ const watermark = document.createElement("div");
107
+ watermark.className = "content-security-watermark";
108
+ watermark.textContent = watermarkText;
109
+ // Position watermark - use % for element-specific watermarks instead of vh/vw
110
+ const positionUnit = this.isFullPageWatermark ? "vh" : "%";
111
+ const horizontalUnit = this.isFullPageWatermark ? "vw" : "%";
112
+ Object.assign(watermark.style, {
113
+ position: "absolute",
114
+ top: `${(i * 100) / rows}${positionUnit}`,
115
+ left: `${(j * 100) / cols}${horizontalUnit}`,
116
+ transform: "rotate(-45deg) translateX(-30%) translateY(-200%)",
117
+ transformOrigin: "center",
118
+ opacity: String(this.options.opacity || 0.15),
119
+ fontSize: this.isFullPageWatermark ? "16px" : "14px", // Slightly smaller for element watermarks
120
+ color: "rgba(0, 0, 0, 0.7)",
121
+ whiteSpace: "nowrap",
122
+ pointerEvents: "none",
123
+ userSelect: "none",
124
+ ...this.options.style,
125
+ });
126
+ container.appendChild(watermark);
127
+ this.watermarkElements.push(watermark);
128
+ }
129
+ }
130
+ // Add container to target
131
+ this.targetElement.appendChild(container);
132
+ this.watermarkElements.push(container);
133
+ this.log("Created watermark container with", this.watermarkElements.length - 1, "watermarks");
134
+ // Set up observer to detect if watermarks are removed
135
+ this.setupObserver();
136
+ });
137
+ }
138
+ /**
139
+ * Set up DOM observer to detect watermark removal
140
+ */
141
+ setupObserver() {
142
+ return this.safeExecute("setupObserver", StrategyErrorType.APPLICATION, () => {
143
+ if (!this.targetElement || !isBrowser())
144
+ return;
145
+ // Create a handler for element removal
146
+ const handleElementsRemoved = (removedElements) => {
147
+ this.log("Watermark elements removed from DOM", removedElements);
148
+ // Only restore if auto-restore is enabled
149
+ if (this.isAppliedFlag) {
150
+ this.log("Auto-restoring watermarks");
151
+ // Restore the watermarks
152
+ this.createWatermarks();
153
+ }
154
+ };
155
+ // Create a new observer if needed
156
+ if (!this.domObserver) {
157
+ this.domObserver = new DomObserver({
158
+ targetElement: this.targetElement,
159
+ elementsToWatch: this.watermarkContainer ? [this.watermarkContainer] : [],
160
+ onElementsRemoved: handleElementsRemoved,
161
+ observeSubtree: true,
162
+ debugMode: this.debugMode,
163
+ name: "WatermarkStrategy",
164
+ });
165
+ }
166
+ else {
167
+ // Update the elements to watch
168
+ this.domObserver.updateElementsToWatch(this.watermarkContainer ? [this.watermarkContainer] : []);
169
+ }
170
+ // Start observing
171
+ this.domObserver.startObserving();
172
+ this.log("DOM observer set up to detect watermark removal");
173
+ });
174
+ }
175
+ /**
176
+ * Remove watermark elements
177
+ */
178
+ removeWatermarkElements() {
179
+ return this.safeExecute("removeWatermarkElements", StrategyErrorType.REMOVAL, () => {
180
+ if (!isBrowser())
181
+ return;
182
+ // First, try to remove by container reference
183
+ if (this.watermarkContainer && this.watermarkContainer.parentNode) {
184
+ this.watermarkContainer.parentNode.removeChild(this.watermarkContainer);
185
+ this.watermarkContainer = null;
186
+ }
187
+ // Then try to remove individual elements
188
+ for (const element of this.watermarkElements) {
189
+ if (element.parentNode) {
190
+ element.parentNode.removeChild(element);
191
+ }
192
+ }
193
+ // Also try to remove by class name in case references were lost
194
+ const containers = document.querySelectorAll(".content-security-watermark-container");
195
+ containers.forEach((container) => {
196
+ if (container.parentNode) {
197
+ container.parentNode.removeChild(container);
198
+ }
199
+ });
200
+ // If we modified the target element's position, restore it
201
+ if (!this.isFullPageWatermark && this.targetElement) {
202
+ // Only remove the position if we added it
203
+ // This is a simplification - ideally we'd store the original position
204
+ if (this.targetElement.style.position === "relative") {
205
+ this.targetElement.style.position = "";
206
+ }
207
+ }
208
+ this.watermarkElements = [];
209
+ this.log("Removed all watermark elements");
210
+ });
211
+ }
212
+ /**
213
+ * Apply watermark protection
214
+ */
215
+ apply() {
216
+ return this.safeExecute("apply", StrategyErrorType.APPLICATION, () => {
217
+ if (this.isAppliedFlag) {
218
+ this.log("Already applied, skipping");
219
+ return;
220
+ }
221
+ this.log("Applying watermark protection", {
222
+ text: this.options.text,
223
+ opacity: this.options.opacity,
224
+ density: this.options.density,
225
+ userId: this.options.userId,
226
+ isFullPage: this.isFullPageWatermark,
227
+ os: this.osInfo.name,
228
+ });
229
+ this.createWatermarks();
230
+ this.isAppliedFlag = true;
231
+ });
232
+ }
233
+ /**
234
+ * Remove watermark protection
235
+ * Override the base implementation to handle additional cleanup
236
+ */
237
+ remove() {
238
+ return this.safeExecute("remove", StrategyErrorType.REMOVAL, () => {
239
+ if (!this.isAppliedFlag) {
240
+ this.log("Not applied, skipping removal");
241
+ return;
242
+ }
243
+ if (this.domObserver) {
244
+ this.domObserver.stopObserving();
245
+ this.domObserver = null;
246
+ this.log("DOM observer stopped");
247
+ }
248
+ this.removeWatermarkElements();
249
+ this.isAppliedFlag = false;
250
+ this.log("Watermark protection removed");
251
+ });
252
+ }
253
+ /**
254
+ * Update watermark options
255
+ * @param options New watermark options
256
+ */
257
+ updateOptions(options) {
258
+ return this.safeExecute("updateOptions", StrategyErrorType.OPTION_UPDATE, () => {
259
+ const typedOptions = options;
260
+ this.log("Updating options", typedOptions);
261
+ this.options = {
262
+ ...this.options,
263
+ ...typedOptions,
264
+ };
265
+ if (this.isAppliedFlag) {
266
+ // Reapply with new options
267
+ this.remove();
268
+ this.apply();
269
+ this.log("Reapplied watermarks with updated options");
270
+ }
271
+ });
272
+ }
273
+ }