@readium/navigator-html-injectables 2.2.4 → 2.3.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/dist/ar-DyHX_uy2.js +7 -0
- package/dist/da-Dct0PS3E.js +7 -0
- package/dist/fr-C5HEel98.js +7 -0
- package/dist/index.js +2121 -1352
- package/dist/index.umd.cjs +24 -10
- package/dist/it-DFOBoXGy.js +7 -0
- package/dist/pt_PT-Di3sVjze.js +7 -0
- package/dist/sv-BfzAFsVN.js +7 -0
- package/package.json +4 -2
- package/src/comms/keys.ts +22 -3
- package/src/index.ts +2 -1
- package/src/modules/ModuleLibrary.ts +10 -4
- package/src/modules/Peripherals.ts +458 -13
- package/src/modules/snapper/ColumnSnapper.ts +63 -2
- package/src/modules/snapper/ScrollSnapper.ts +59 -1
- package/src/modules/snapper/WebPubSnapper.ts +57 -3
- package/src/protection/BulkCopyProtector.ts +134 -0
- package/src/protection/KeyCombinationManager.ts +139 -0
- package/src/protection/KeyboardCombinations.ts +60 -0
- package/src/protection/PatternAnalyzer.ts +117 -0
- package/src/protection/PrintProtector.ts +87 -0
- package/src/protection/SelectionAnalyzer.ts +133 -0
- package/src/protection/config.ts +33 -0
- package/src/protection/index.ts +6 -0
- package/types/src/comms/keys.d.ts +3 -2
- package/types/src/index.d.ts +1 -0
- package/types/src/modules/ModuleLibrary.d.ts +1 -1
- package/types/src/modules/Peripherals.d.ts +102 -2
- package/types/src/modules/snapper/ColumnSnapper.d.ts +10 -0
- package/types/src/modules/snapper/ScrollSnapper.d.ts +13 -0
- package/types/src/modules/snapper/WebPubSnapper.d.ts +6 -0
- package/types/src/protection/BulkCopyProtector.d.ts +20 -0
- package/types/src/protection/KeyCombinationManager.d.ts +33 -0
- package/types/src/protection/KeyboardCombinations.d.ts +15 -0
- package/types/src/protection/PatternAnalyzer.d.ts +18 -0
- package/types/src/protection/PrintProtector.d.ts +20 -0
- package/types/src/protection/SelectionAnalyzer.d.ts +20 -0
- package/types/src/protection/config.d.ts +7 -0
- package/types/src/protection/index.d.ts +6 -0
- package/src/types/protection.ts +0 -16
- package/types/src/types/protection.d.ts +0 -7
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Comms } from "../comms";
|
|
2
|
+
import { ReadiumWindow } from "../helpers/dom";
|
|
3
|
+
import { Module } from "../modules/Module";
|
|
4
|
+
import { ModuleName } from "../modules/ModuleLibrary";
|
|
5
|
+
|
|
6
|
+
export interface PrintProtectionConfig {
|
|
7
|
+
disable?: boolean;
|
|
8
|
+
watermark?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class PrintProtector extends Module {
|
|
12
|
+
static readonly moduleName: ModuleName = "print_protection";
|
|
13
|
+
|
|
14
|
+
private comms!: Comms;
|
|
15
|
+
private wnd!: ReadiumWindow;
|
|
16
|
+
private styleElement: HTMLStyleElement | null = null;
|
|
17
|
+
private beforePrintHandler: ((e: Event) => void) | null = null;
|
|
18
|
+
private configApplied = false;
|
|
19
|
+
|
|
20
|
+
private setupPrintProtection(wnd: Window, config: PrintProtectionConfig) {
|
|
21
|
+
if (!config.disable) return;
|
|
22
|
+
|
|
23
|
+
const style = wnd.document.createElement("style");
|
|
24
|
+
style.textContent = `
|
|
25
|
+
@media print {
|
|
26
|
+
body * {
|
|
27
|
+
display: none !important;
|
|
28
|
+
}
|
|
29
|
+
body::after {
|
|
30
|
+
content: "${config.watermark || 'Printing has been disabled'}";
|
|
31
|
+
font-size: 200%;
|
|
32
|
+
display: block;
|
|
33
|
+
text-align: center;
|
|
34
|
+
margin-top: 50vh;
|
|
35
|
+
transform: translateY(-50%);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
`;
|
|
39
|
+
wnd.document.head.appendChild(style);
|
|
40
|
+
this.styleElement = style;
|
|
41
|
+
|
|
42
|
+
this.beforePrintHandler = (e: Event) => {
|
|
43
|
+
e.preventDefault();
|
|
44
|
+
return false;
|
|
45
|
+
};
|
|
46
|
+
wnd.addEventListener("beforeprint", this.beforePrintHandler);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private registerPrintHandlers() {
|
|
50
|
+
this.comms?.register("print_protection", PrintProtector.moduleName, (data: unknown) => {
|
|
51
|
+
const config = data as PrintProtectionConfig;
|
|
52
|
+
|
|
53
|
+
if (!this.configApplied) {
|
|
54
|
+
this.configApplied = true;
|
|
55
|
+
this.setupPrintProtection(this.wnd, config);
|
|
56
|
+
this.comms?.log("Print protection configuration applied");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return true;
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
mount(wnd: ReadiumWindow, comms: Comms): boolean {
|
|
64
|
+
this.wnd = wnd;
|
|
65
|
+
this.comms = comms;
|
|
66
|
+
this.registerPrintHandlers();
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
unmount(wnd: ReadiumWindow, _comms: Comms): boolean {
|
|
71
|
+
if (this.beforePrintHandler) {
|
|
72
|
+
wnd.removeEventListener("beforeprint", this.beforePrintHandler);
|
|
73
|
+
this.beforePrintHandler = null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (this.styleElement?.parentNode) {
|
|
77
|
+
this.styleElement.parentNode.removeChild(this.styleElement);
|
|
78
|
+
this.styleElement = null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Unregister all print protection handlers
|
|
82
|
+
this.comms?.unregisterAll(PrintProtector.moduleName);
|
|
83
|
+
|
|
84
|
+
this.configApplied = false;
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
interface SelectionEvent {
|
|
2
|
+
timestamp: number;
|
|
3
|
+
position: number;
|
|
4
|
+
length: number;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface SelectionAnalyzerOptions {
|
|
8
|
+
maxSelectionsPerSecond: number;
|
|
9
|
+
minVariance: number;
|
|
10
|
+
historySize: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
import { SELECTION_ANALYZER_CONFIG } from './config';
|
|
14
|
+
|
|
15
|
+
export class SelectionAnalyzer {
|
|
16
|
+
private events: SelectionEvent[] = [];
|
|
17
|
+
private selectionStartTime = 0;
|
|
18
|
+
private lastSelectionTime = 0;
|
|
19
|
+
private lastSelectionPosition = 0;
|
|
20
|
+
private selectionPatterns: number[] = [];
|
|
21
|
+
private lastSelectedText = "";
|
|
22
|
+
|
|
23
|
+
constructor(
|
|
24
|
+
private readonly options: SelectionAnalyzerOptions = SELECTION_ANALYZER_CONFIG
|
|
25
|
+
) {}
|
|
26
|
+
|
|
27
|
+
public analyze(selection: Selection | null): boolean {
|
|
28
|
+
if (!selection) {
|
|
29
|
+
this.clear();
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const selectedText = selection.toString();
|
|
34
|
+
if (selectedText.length === 0) {
|
|
35
|
+
this.clear();
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (selection.type !== "Range" || !selection.rangeCount) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const now = Date.now();
|
|
44
|
+
|
|
45
|
+
// Only analyze meaningful selections (not too short, not duplicates)
|
|
46
|
+
if (selectedText.length <= 50 || selectedText === this.lastSelectedText) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Start timing if this is a new selection
|
|
51
|
+
if (this.selectionStartTime === 0) {
|
|
52
|
+
this.selectionStartTime = now;
|
|
53
|
+
this.lastSelectedText = selectedText;
|
|
54
|
+
return false; // Don't analyze first selection, just track it
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Check if enough time has passed for selection to be complete
|
|
58
|
+
const timeSinceStart = now - this.selectionStartTime;
|
|
59
|
+
if (timeSinceStart < 500) {
|
|
60
|
+
return false; // Still selecting, wait longer
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Track selection frequency
|
|
64
|
+
if (now - this.lastSelectionTime > 1000) {
|
|
65
|
+
this.lastSelectionTime = now;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (this.selectionStartTime === 0) {
|
|
69
|
+
this.selectionStartTime = now;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Store current selection for comparison
|
|
73
|
+
this.lastSelectedText = selectedText;
|
|
74
|
+
|
|
75
|
+
// Analyze selection pattern
|
|
76
|
+
const isSuspicious = this.analyzeSelectionPattern(selection, now);
|
|
77
|
+
|
|
78
|
+
// Clean up old events
|
|
79
|
+
this.cleanup(now);
|
|
80
|
+
|
|
81
|
+
return isSuspicious;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private analyzeSelectionPattern(selection: Selection, now: number): boolean {
|
|
85
|
+
if (!selection.rangeCount) return false;
|
|
86
|
+
|
|
87
|
+
const range = selection.getRangeAt(0);
|
|
88
|
+
const text = range.toString();
|
|
89
|
+
|
|
90
|
+
// Calculate selection speed (characters per second)
|
|
91
|
+
const duration = (now - this.selectionStartTime) / 1000; // Convert to seconds
|
|
92
|
+
const speed = text.length / Math.max(1, duration); // Characters per second
|
|
93
|
+
|
|
94
|
+
// Check for unnaturally fast selection
|
|
95
|
+
if (speed > this.options.maxSelectionsPerSecond) return true;
|
|
96
|
+
|
|
97
|
+
// Analyze selection pattern (human selections have more variance)
|
|
98
|
+
const currentPosition = range.startOffset;
|
|
99
|
+
const distance = Math.abs(currentPosition - this.lastSelectionPosition);
|
|
100
|
+
this.selectionPatterns.push(distance);
|
|
101
|
+
|
|
102
|
+
if (this.selectionPatterns.length > this.options.historySize) {
|
|
103
|
+
this.selectionPatterns.shift();
|
|
104
|
+
const variance = this.calculateVariance(this.selectionPatterns);
|
|
105
|
+
if (variance < this.options.minVariance) { // Very consistent patterns suggest automation
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
this.lastSelectionPosition = currentPosition;
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private calculateVariance(numbers: number[]): number {
|
|
115
|
+
if (numbers.length === 0) return 0;
|
|
116
|
+
const mean = numbers.reduce((a, b) => a + b, 0) / numbers.length;
|
|
117
|
+
return numbers.reduce((a, b) => a + Math.pow(b - mean, 2), 0) / numbers.length;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private cleanup(now: number): void {
|
|
121
|
+
// Keep events from the last second
|
|
122
|
+
this.events = this.events.filter(e => now - e.timestamp <= 1000);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
public clear(): void {
|
|
126
|
+
this.events = [];
|
|
127
|
+
this.selectionStartTime = 0;
|
|
128
|
+
this.lastSelectionTime = 0;
|
|
129
|
+
this.lastSelectionPosition = 0;
|
|
130
|
+
this.selectionPatterns = [];
|
|
131
|
+
this.lastSelectedText = "";
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { BulkCopyProtectionOptions } from "./BulkCopyProtector";
|
|
2
|
+
import { PatternAnalyzerOptions } from "./PatternAnalyzer";
|
|
3
|
+
import { SelectionAnalyzerOptions } from "./SelectionAnalyzer";
|
|
4
|
+
|
|
5
|
+
export const DEFAULT_PATTERN_ANALYZER_CONFIG: PatternAnalyzerOptions = {
|
|
6
|
+
maxVelocity: 200, // Reasonable default for human-like scrolling (pixels/ms)
|
|
7
|
+
minVariance: 0.01, // Default variance threshold
|
|
8
|
+
historySize: 20, // Balanced history size for performance
|
|
9
|
+
minDirectionChanges: 0.2, // Reasonable default for detecting patterns
|
|
10
|
+
maxConsistentScrolls: 15 // Balanced threshold for flagging
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const SCROLL_PROTECTION_CONFIG: PatternAnalyzerOptions = {
|
|
14
|
+
maxVelocity: 200, // Extremely fast scrolling (pixels/ms)
|
|
15
|
+
minVariance: 0.00001, // Near-perfect consistency
|
|
16
|
+
historySize: 100, // Large history window
|
|
17
|
+
minDirectionChanges: 0.1, // Only trigger on near-perfect patterns
|
|
18
|
+
maxConsistentScrolls: 20 // Need many consistent scrolls
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const SELECTION_ANALYZER_CONFIG: SelectionAnalyzerOptions = {
|
|
22
|
+
maxSelectionsPerSecond: 500,
|
|
23
|
+
minVariance: 50,
|
|
24
|
+
historySize: 20
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const BULK_COPY_CONFIG: BulkCopyProtectionOptions = {
|
|
28
|
+
enabled: true,
|
|
29
|
+
maxSelectionPercent: 0.1,
|
|
30
|
+
minThreshold: 100,
|
|
31
|
+
absoluteMaxChars: 5000,
|
|
32
|
+
historySize: 20
|
|
33
|
+
}
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export type CommsEventKey = "_pong" | "_unhandled" | "_ack" | "log" | "error" | "click" | "tap" | "tap_more" | "no_more" | "no_less" | "swipe" | "scroll" | "progress" | "first_visible_locator" | "text_selected" | "media_play" | "media_pause";
|
|
2
|
-
export type CommsCommandKey = "_ping" | "go_prev" | "go_next" | "go_id" | "go_text" | "go_end" | "go_start" | "go_progression" | "get_properties" | "update_properties" | "set_property" | "remove_property" | "first_visible_locator" | "decorate" | "protect" | "unprotect" | "unfocus" | "focus" | "activate" | "shake" | "force_webkit_recalc";
|
|
1
|
+
export type CommsEventKey = "_pong" | "_unhandled" | "_ack" | "log" | "error" | "click" | "tap" | "tap_more" | "no_more" | "no_less" | "swipe" | "scroll" | "progress" | "first_visible_locator" | "text_selected" | "context_menu" | "media_play" | "media_pause" | "content_protection" | "keyboard_peripherals";
|
|
2
|
+
export type CommsCommandKey = "_ping" | "go_prev" | "go_next" | "go_id" | "go_text" | "go_end" | "go_start" | "go_progression" | "get_properties" | "update_properties" | "set_property" | "remove_property" | "first_visible_locator" | "decorate" | "protect" | "unprotect" | "unfocus" | "focus" | "activate" | "shake" | "force_webkit_recalc" | "peripherals_protection" | "keyboard_peripherals" | "scroll_protection" | "print_protection";
|
|
3
|
+
export type SuspiciousActivityType = "developer_tools" | "select_all" | "save" | "suspicious_selection" | "bulk_copy" | "suspicious_scrolling" | "suspicious_snapping" | "drag_detected" | "drop_detected" | "print";
|
package/types/src/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ModuleDerived } from "./Module";
|
|
2
|
-
export type ModuleName = "setup" | "snapper" | "column_snapper" | "scroll_snapper" | "webpub_snapper" | "fixed_setup" | "decorator" | "reflowable_setup" | "peripherals" | "webpub_setup";
|
|
2
|
+
export type ModuleName = "setup" | "snapper" | "column_snapper" | "scroll_snapper" | "webpub_snapper" | "fixed_setup" | "decorator" | "reflowable_setup" | "peripherals" | "webpub_setup" | "print_protection";
|
|
3
3
|
export declare const FXLModules: ModuleName[];
|
|
4
4
|
export declare const ReflowableModules: ModuleName[];
|
|
5
5
|
export declare const WebPubModules: ModuleName[];
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { Comms } from "../comms/comms";
|
|
2
2
|
import { Module } from "./Module";
|
|
3
3
|
import { ReadiumWindow } from "../helpers/dom";
|
|
4
|
+
import { BulkCopyProtectionOptions } from "../protection/BulkCopyProtector";
|
|
5
|
+
import { SelectionAnalyzerOptions } from "../protection/SelectionAnalyzer";
|
|
6
|
+
import { SuspiciousActivityType } from "../comms";
|
|
7
|
+
import { KeyboardPeripheralEvent } from "../protection/KeyCombinationManager";
|
|
8
|
+
import { SuspiciousScrollingEvent } from "./snapper/ScrollSnapper";
|
|
9
|
+
import { SuspiciousSnappingEvent } from "./snapper/ColumnSnapper";
|
|
4
10
|
export interface FrameClickEvent {
|
|
5
11
|
defaultPrevented: boolean;
|
|
6
12
|
doNotDisturb: boolean;
|
|
@@ -19,21 +25,115 @@ export interface BasicTextSelection {
|
|
|
19
25
|
height: number;
|
|
20
26
|
targetFrameSrc: string;
|
|
21
27
|
}
|
|
28
|
+
export interface BaseSuspiciousActivityEvent {
|
|
29
|
+
type: SuspiciousActivityType;
|
|
30
|
+
timestamp: number;
|
|
31
|
+
targetFrameSrc: string;
|
|
32
|
+
}
|
|
33
|
+
export interface KeyboardEventData {
|
|
34
|
+
key: string;
|
|
35
|
+
code: string;
|
|
36
|
+
keyCode: number;
|
|
37
|
+
ctrlKey: boolean;
|
|
38
|
+
altKey: boolean;
|
|
39
|
+
shiftKey: boolean;
|
|
40
|
+
metaKey: boolean;
|
|
41
|
+
}
|
|
42
|
+
export interface BaseKeyboardPeripheralEvent {
|
|
43
|
+
type: string;
|
|
44
|
+
timestamp: number;
|
|
45
|
+
targetFrameSrc: string;
|
|
46
|
+
selectedText?: Omit<BasicTextSelection, "targetFrameSrc">;
|
|
47
|
+
}
|
|
48
|
+
export interface DeveloperToolsEvent extends BaseSuspiciousActivityEvent, KeyboardEventData {
|
|
49
|
+
type: "developer_tools";
|
|
50
|
+
}
|
|
51
|
+
export interface SelectAllEvent extends BaseSuspiciousActivityEvent, KeyboardEventData {
|
|
52
|
+
type: "select_all";
|
|
53
|
+
}
|
|
54
|
+
export interface PrintEvent extends BaseSuspiciousActivityEvent, KeyboardEventData {
|
|
55
|
+
type: "print";
|
|
56
|
+
}
|
|
57
|
+
export interface SaveEvent extends BaseSuspiciousActivityEvent, KeyboardEventData {
|
|
58
|
+
type: "save";
|
|
59
|
+
}
|
|
60
|
+
export interface BulkCopyEvent extends BaseSuspiciousActivityEvent {
|
|
61
|
+
type: "bulk_copy";
|
|
62
|
+
clipboardTypes: readonly string[];
|
|
63
|
+
selectedText?: Omit<BasicTextSelection, "targetFrameSrc">;
|
|
64
|
+
selectionLength?: number;
|
|
65
|
+
}
|
|
66
|
+
export interface SuspiciousSelectionEvent extends BaseSuspiciousActivityEvent {
|
|
67
|
+
type: "suspicious_selection";
|
|
68
|
+
selectionLength: number;
|
|
69
|
+
selectedText: Omit<BasicTextSelection, "targetFrameSrc">;
|
|
70
|
+
eventType: string;
|
|
71
|
+
}
|
|
72
|
+
export interface DragDetectedEvent extends BaseSuspiciousActivityEvent {
|
|
73
|
+
type: "drag_detected";
|
|
74
|
+
dataTransferTypes: readonly string[];
|
|
75
|
+
}
|
|
76
|
+
export interface DropDetectedEvent extends BaseSuspiciousActivityEvent {
|
|
77
|
+
type: "drop_detected";
|
|
78
|
+
dataTransferTypes: readonly string[];
|
|
79
|
+
fileCount: number;
|
|
80
|
+
}
|
|
81
|
+
export interface ContextMenuEvent extends Omit<BaseSuspiciousActivityEvent, "type"> {
|
|
82
|
+
clientX: number;
|
|
83
|
+
clientY: number;
|
|
84
|
+
selectedText?: Omit<BasicTextSelection, "targetFrameSrc">;
|
|
85
|
+
}
|
|
86
|
+
export type SuspiciousActivityEvent = DeveloperToolsEvent | SelectAllEvent | BulkCopyEvent | SuspiciousSelectionEvent | DragDetectedEvent | DropDetectedEvent | PrintEvent | SaveEvent | SuspiciousScrollingEvent | SuspiciousSnappingEvent | KeyboardPeripheralEvent;
|
|
87
|
+
export interface ContentProtectionConfig {
|
|
88
|
+
monitorSelection?: boolean | SelectionAnalyzerOptions;
|
|
89
|
+
protectCopy?: boolean | Omit<BulkCopyProtectionOptions, "enabled">;
|
|
90
|
+
disableContextMenu?: boolean;
|
|
91
|
+
disableDragAndDrop?: boolean;
|
|
92
|
+
disableSelectAll?: boolean;
|
|
93
|
+
disableSave?: boolean;
|
|
94
|
+
monitorDevTools?: boolean;
|
|
95
|
+
monitorScrollingExperimental?: boolean;
|
|
96
|
+
}
|
|
22
97
|
export declare class Peripherals extends Module {
|
|
23
98
|
static readonly moduleName = "peripherals";
|
|
24
99
|
private wnd;
|
|
25
100
|
private comms;
|
|
101
|
+
private configApplied;
|
|
102
|
+
private cleanupCallbacks;
|
|
26
103
|
private pointerMoved;
|
|
104
|
+
private isContextMenuEnabled;
|
|
105
|
+
private isDragAndDropEnabled;
|
|
106
|
+
private isSelectionMonitoringEnabled;
|
|
107
|
+
private isBulkCopyProtectionEnabled;
|
|
108
|
+
private selectionAnalyzer;
|
|
109
|
+
private currentSelection;
|
|
110
|
+
private bulkCopyProtector;
|
|
111
|
+
private keyManager;
|
|
112
|
+
private addContextMenuPrevention;
|
|
113
|
+
private removeContextMenuPrevention;
|
|
114
|
+
private addDragAndDropPrevention;
|
|
115
|
+
private removeDragAndDropPrevention;
|
|
116
|
+
private keyDownHandler;
|
|
117
|
+
private enableKeyboardPeripherals;
|
|
118
|
+
private disableKeyboardPeripherals;
|
|
119
|
+
private addBulkCopyProtection;
|
|
120
|
+
private removeBulkCopyProtection;
|
|
121
|
+
private preventBulkCopy;
|
|
122
|
+
private handleSelection;
|
|
123
|
+
private addSelectionMonitoring;
|
|
124
|
+
private removeSelectionMonitoring;
|
|
125
|
+
private onDragStart;
|
|
126
|
+
private onDrop;
|
|
127
|
+
private onContext;
|
|
27
128
|
onPointUp(event: PointerEvent): void;
|
|
28
129
|
private readonly onPointerUp;
|
|
29
130
|
onPointMove(event: PointerEvent): void;
|
|
30
131
|
private readonly onPointerMove;
|
|
31
132
|
onPointDown(): void;
|
|
32
133
|
private readonly onPointerDown;
|
|
33
|
-
onContext(event: MouseEvent): void;
|
|
34
|
-
private readonly onContextMenu;
|
|
35
134
|
onClick(event: MouseEvent): void;
|
|
36
135
|
private readonly onClicker;
|
|
136
|
+
private registerProtectionHandlers;
|
|
37
137
|
mount(wnd: ReadiumWindow, comms: Comms): boolean;
|
|
38
138
|
unmount(wnd: ReadiumWindow, comms: Comms): boolean;
|
|
39
139
|
}
|
|
@@ -2,13 +2,21 @@ import { Comms } from "../../comms";
|
|
|
2
2
|
import { Snapper } from "./Snapper";
|
|
3
3
|
import { ModuleName } from "../ModuleLibrary";
|
|
4
4
|
import { ReadiumWindow } from "../../helpers/dom";
|
|
5
|
+
import { BaseSuspiciousActivityEvent } from "../Peripherals";
|
|
6
|
+
export interface SuspiciousSnappingEvent extends BaseSuspiciousActivityEvent {
|
|
7
|
+
type: "suspicious_snapping";
|
|
8
|
+
event: null;
|
|
9
|
+
}
|
|
5
10
|
/**
|
|
6
11
|
* A {Snapper} for reflowable resources using a column-based layout
|
|
7
12
|
*/
|
|
8
13
|
export declare class ColumnSnapper extends Snapper {
|
|
9
14
|
static readonly moduleName: ModuleName;
|
|
15
|
+
private isSnapProtectionEnabled;
|
|
10
16
|
private resizeObserver;
|
|
11
17
|
private mutationObserver;
|
|
18
|
+
private patternAnalyzer;
|
|
19
|
+
private lastTurnTime;
|
|
12
20
|
private wnd;
|
|
13
21
|
private comms;
|
|
14
22
|
private doc;
|
|
@@ -36,6 +44,8 @@ export declare class ColumnSnapper extends Snapper {
|
|
|
36
44
|
private readonly onWidthChanger;
|
|
37
45
|
onTouchMove(e: TouchEvent): void;
|
|
38
46
|
private readonly onTouchMover;
|
|
47
|
+
private enableSnapProtection;
|
|
48
|
+
private checkSuspiciousSnap;
|
|
39
49
|
mount(wnd: ReadiumWindow, comms: Comms): boolean;
|
|
40
50
|
unmount(wnd: ReadiumWindow, comms: Comms): boolean;
|
|
41
51
|
}
|
|
@@ -2,11 +2,23 @@ import { Comms } from "../../comms";
|
|
|
2
2
|
import { ReadiumWindow } from "../../helpers/dom";
|
|
3
3
|
import { ModuleName } from "../ModuleLibrary";
|
|
4
4
|
import { Snapper } from "./Snapper";
|
|
5
|
+
import { BaseSuspiciousActivityEvent } from "../Peripherals";
|
|
6
|
+
export interface SuspiciousScrollingEvent extends BaseSuspiciousActivityEvent {
|
|
7
|
+
type: "suspicious_scrolling";
|
|
8
|
+
scrollDelta: number;
|
|
9
|
+
scrollDirection: "up" | "down";
|
|
10
|
+
targetElement: {
|
|
11
|
+
tagName: string;
|
|
12
|
+
} | null;
|
|
13
|
+
}
|
|
5
14
|
export declare class ScrollSnapper extends Snapper {
|
|
6
15
|
static readonly moduleName: ModuleName;
|
|
7
16
|
private wnd;
|
|
8
17
|
private comms;
|
|
9
18
|
private resizeObserver;
|
|
19
|
+
private patternAnalyzer;
|
|
20
|
+
private lastScrollTime;
|
|
21
|
+
private isScrollProtectionEnabled;
|
|
10
22
|
private initialScrollHandled;
|
|
11
23
|
private isScrolling;
|
|
12
24
|
private lastScrollTop;
|
|
@@ -15,6 +27,7 @@ export declare class ScrollSnapper extends Snapper {
|
|
|
15
27
|
private doc;
|
|
16
28
|
private reportProgress;
|
|
17
29
|
private handleScroll;
|
|
30
|
+
private enableScrollProtection;
|
|
18
31
|
mount(wnd: ReadiumWindow, comms: Comms): boolean;
|
|
19
32
|
unmount(wnd: ReadiumWindow, comms: Comms): boolean;
|
|
20
33
|
}
|
|
@@ -2,11 +2,16 @@ import { Comms } from "../../comms/comms";
|
|
|
2
2
|
import { ReadiumWindow } from "../../helpers/dom";
|
|
3
3
|
import { ModuleName } from "../ModuleLibrary";
|
|
4
4
|
import { Snapper } from "./Snapper";
|
|
5
|
+
import { SuspiciousScrollingEvent } from "./ScrollSnapper";
|
|
6
|
+
export type { SuspiciousScrollingEvent };
|
|
5
7
|
export declare class WebPubSnapper extends Snapper {
|
|
6
8
|
static readonly moduleName: ModuleName;
|
|
7
9
|
private wnd;
|
|
8
10
|
private comms;
|
|
9
11
|
private resizeObserver;
|
|
12
|
+
private patternAnalyzer;
|
|
13
|
+
private lastScrollTime;
|
|
14
|
+
private isScrollProtectionEnabled;
|
|
10
15
|
private initialScrollHandled;
|
|
11
16
|
private isScrolling;
|
|
12
17
|
private lastScrollTop;
|
|
@@ -15,6 +20,7 @@ export declare class WebPubSnapper extends Snapper {
|
|
|
15
20
|
private doc;
|
|
16
21
|
private reportProgress;
|
|
17
22
|
private handleScroll;
|
|
23
|
+
private enableScrollProtection;
|
|
18
24
|
mount(wnd: ReadiumWindow, comms: Comms): boolean;
|
|
19
25
|
unmount(wnd: ReadiumWindow, comms: Comms): boolean;
|
|
20
26
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ReadiumWindow } from "../helpers/dom";
|
|
2
|
+
export interface BulkCopyProtectionOptions {
|
|
3
|
+
enabled: boolean;
|
|
4
|
+
maxSelectionPercent: number;
|
|
5
|
+
absoluteMaxChars: number;
|
|
6
|
+
minThreshold: number;
|
|
7
|
+
historySize: number;
|
|
8
|
+
}
|
|
9
|
+
export declare class BulkCopyProtector {
|
|
10
|
+
private readonly window;
|
|
11
|
+
private options;
|
|
12
|
+
private copyHistory;
|
|
13
|
+
private lastSelectionLength;
|
|
14
|
+
private lastSelectionTime;
|
|
15
|
+
constructor(window: ReadiumWindow, options: BulkCopyProtectionOptions);
|
|
16
|
+
private cleanupOldHistory;
|
|
17
|
+
private isSuspiciousPattern;
|
|
18
|
+
shouldAllowCopy(event: ClipboardEvent): boolean;
|
|
19
|
+
destroy(): void;
|
|
20
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { KeyCombo, KeyboardPeripheral } from "./KeyboardCombinations";
|
|
2
|
+
import { BaseKeyboardPeripheralEvent, KeyboardEventData } from "../modules/Peripherals";
|
|
3
|
+
import { ReadiumWindow } from "../helpers/dom";
|
|
4
|
+
export type KeyHandler = (event: KeyboardEvent) => void;
|
|
5
|
+
export type ActivityEventDispatcher = (event: KeyboardPeripheralEvent) => void;
|
|
6
|
+
export interface KeyboardPeripheralEvent extends BaseKeyboardPeripheralEvent, KeyboardEventData {
|
|
7
|
+
type: string;
|
|
8
|
+
}
|
|
9
|
+
export interface KeyComboWithHandler extends KeyCombo {
|
|
10
|
+
handler?: (event: KeyboardEvent) => void;
|
|
11
|
+
}
|
|
12
|
+
export declare class KeyCombinationManager {
|
|
13
|
+
/**
|
|
14
|
+
* Checks if the given keyboard event matches any of the provided key combinations
|
|
15
|
+
*/
|
|
16
|
+
match(event: KeyboardEvent, combos: KeyCombo[]): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Creates an event handler that will call the provided handler when any of the key combinations match
|
|
19
|
+
*/
|
|
20
|
+
createKeyHandler(combos: KeyCombo[], handler: KeyHandler): (event: KeyboardEvent) => void;
|
|
21
|
+
/**
|
|
22
|
+
* Creates a standardized activity event for keyboard shortcuts
|
|
23
|
+
*/
|
|
24
|
+
private createActivityEvent;
|
|
25
|
+
/**
|
|
26
|
+
* Creates handlers for keyboard shortcuts with centralized activity event dispatch
|
|
27
|
+
*/
|
|
28
|
+
createKeyboardHandlers(targetFrameSrc: string, shortcuts: KeyboardPeripheral[], dispatcher: ActivityEventDispatcher, wnd?: ReadiumWindow): KeyComboWithHandler[];
|
|
29
|
+
/**
|
|
30
|
+
* Creates a unified keyboard event handler that processes all shortcuts
|
|
31
|
+
*/
|
|
32
|
+
createUnifiedHandler(targetFrameSrc: string, shortcuts: KeyboardPeripheral[], dispatcher: ActivityEventDispatcher, wnd?: ReadiumWindow): (event: KeyboardEvent) => void;
|
|
33
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface KeyCombo {
|
|
2
|
+
keyCode: number;
|
|
3
|
+
ctrl?: boolean;
|
|
4
|
+
shift?: boolean;
|
|
5
|
+
alt?: boolean;
|
|
6
|
+
meta?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface KeyboardPeripheral {
|
|
9
|
+
type: string;
|
|
10
|
+
keyCombos: KeyCombo[];
|
|
11
|
+
}
|
|
12
|
+
export declare const DEV_TOOLS: KeyboardPeripheral;
|
|
13
|
+
export declare const SELECT_ALL: KeyboardPeripheral;
|
|
14
|
+
export declare const PRINT: KeyboardPeripheral;
|
|
15
|
+
export declare const SAVE: KeyboardPeripheral;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
type Direction = "left" | "right" | "up" | "down";
|
|
2
|
+
export interface PatternAnalyzerOptions {
|
|
3
|
+
maxVelocity: number;
|
|
4
|
+
minVariance: number;
|
|
5
|
+
historySize?: number;
|
|
6
|
+
minDirectionChanges?: number;
|
|
7
|
+
maxConsistentScrolls?: number;
|
|
8
|
+
}
|
|
9
|
+
export declare class PatternAnalyzer {
|
|
10
|
+
private options;
|
|
11
|
+
private history;
|
|
12
|
+
private consistentScrollCount;
|
|
13
|
+
constructor(options?: Partial<PatternAnalyzerOptions>);
|
|
14
|
+
analyze(direction: Direction, distance: number, timeDelta: number): boolean;
|
|
15
|
+
private resetAfterDetection;
|
|
16
|
+
clear(): void;
|
|
17
|
+
}
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Comms } from "../comms";
|
|
2
|
+
import { ReadiumWindow } from "../helpers/dom";
|
|
3
|
+
import { Module } from "../modules/Module";
|
|
4
|
+
import { ModuleName } from "../modules/ModuleLibrary";
|
|
5
|
+
export interface PrintProtectionConfig {
|
|
6
|
+
disable?: boolean;
|
|
7
|
+
watermark?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare class PrintProtector extends Module {
|
|
10
|
+
static readonly moduleName: ModuleName;
|
|
11
|
+
private comms;
|
|
12
|
+
private wnd;
|
|
13
|
+
private styleElement;
|
|
14
|
+
private beforePrintHandler;
|
|
15
|
+
private configApplied;
|
|
16
|
+
private setupPrintProtection;
|
|
17
|
+
private registerPrintHandlers;
|
|
18
|
+
mount(wnd: ReadiumWindow, comms: Comms): boolean;
|
|
19
|
+
unmount(wnd: ReadiumWindow, _comms: Comms): boolean;
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface SelectionAnalyzerOptions {
|
|
2
|
+
maxSelectionsPerSecond: number;
|
|
3
|
+
minVariance: number;
|
|
4
|
+
historySize: number;
|
|
5
|
+
}
|
|
6
|
+
export declare class SelectionAnalyzer {
|
|
7
|
+
private readonly options;
|
|
8
|
+
private events;
|
|
9
|
+
private selectionStartTime;
|
|
10
|
+
private lastSelectionTime;
|
|
11
|
+
private lastSelectionPosition;
|
|
12
|
+
private selectionPatterns;
|
|
13
|
+
private lastSelectedText;
|
|
14
|
+
constructor(options?: SelectionAnalyzerOptions);
|
|
15
|
+
analyze(selection: Selection | null): boolean;
|
|
16
|
+
private analyzeSelectionPattern;
|
|
17
|
+
private calculateVariance;
|
|
18
|
+
private cleanup;
|
|
19
|
+
clear(): void;
|
|
20
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { BulkCopyProtectionOptions } from "./BulkCopyProtector";
|
|
2
|
+
import { PatternAnalyzerOptions } from "./PatternAnalyzer";
|
|
3
|
+
import { SelectionAnalyzerOptions } from "./SelectionAnalyzer";
|
|
4
|
+
export declare const DEFAULT_PATTERN_ANALYZER_CONFIG: PatternAnalyzerOptions;
|
|
5
|
+
export declare const SCROLL_PROTECTION_CONFIG: PatternAnalyzerOptions;
|
|
6
|
+
export declare const SELECTION_ANALYZER_CONFIG: SelectionAnalyzerOptions;
|
|
7
|
+
export declare const BULK_COPY_CONFIG: BulkCopyProtectionOptions;
|
package/src/types/protection.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
type SuspiciousActivityType =
|
|
2
|
-
| "developer_tools_attempt"
|
|
3
|
-
| "select_all_attempt"
|
|
4
|
-
| "suspicious_selection"
|
|
5
|
-
| "suspicious_copy"
|
|
6
|
-
| "suspicious_scrolling"
|
|
7
|
-
| "suspicious_snapping"
|
|
8
|
-
| "drag_detected"
|
|
9
|
-
| "drop_detected"
|
|
10
|
-
| "context_menu";
|
|
11
|
-
|
|
12
|
-
export interface SuspiciousActivityEvent {
|
|
13
|
-
timestamp: number;
|
|
14
|
-
type: SuspiciousActivityType;
|
|
15
|
-
event?: Event;
|
|
16
|
-
}
|