jumpy-lion 0.0.41 → 0.0.42
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/browser-controller.d.ts.map +1 -1
- package/dist/browser-controller.js +102 -16
- package/dist/browser-controller.js.map +1 -1
- package/dist/browser-plugin.d.ts +20 -1
- package/dist/browser-plugin.d.ts.map +1 -1
- package/dist/browser-plugin.js +21 -1
- package/dist/browser-plugin.js.map +1 -1
- package/dist/browser-process/browser.d.ts +22 -1
- package/dist/browser-process/browser.d.ts.map +1 -1
- package/dist/browser-process/browser.js +77 -5
- package/dist/browser-process/browser.js.map +1 -1
- package/dist/browser-profiles/chrome/default.d.ts +116 -0
- package/dist/browser-profiles/chrome/default.d.ts.map +1 -1
- package/dist/browser-profiles/chrome/default.js +118 -1
- package/dist/browser-profiles/chrome/default.js.map +1 -1
- package/dist/browser-profiles/chrome/populate-profile.d.ts +76 -0
- package/dist/browser-profiles/chrome/populate-profile.d.ts.map +1 -0
- package/dist/browser-profiles/chrome/populate-profile.js +300 -0
- package/dist/browser-profiles/chrome/populate-profile.js.map +1 -0
- package/dist/browser-profiles/index.d.ts +1 -0
- package/dist/browser-profiles/index.d.ts.map +1 -1
- package/dist/browser-profiles/index.js +2 -0
- package/dist/browser-profiles/index.js.map +1 -1
- package/dist/crawler.d.ts +32 -1
- package/dist/crawler.d.ts.map +1 -1
- package/dist/crawler.js +7 -0
- package/dist/crawler.js.map +1 -1
- package/dist/fingerprinting/custom-fingerprint-injector.d.ts +87 -0
- package/dist/fingerprinting/custom-fingerprint-injector.d.ts.map +1 -0
- package/dist/fingerprinting/custom-fingerprint-injector.js +342 -0
- package/dist/fingerprinting/custom-fingerprint-injector.js.map +1 -0
- package/dist/fingerprinting/fingerprint-injector.d.ts +40 -2
- package/dist/fingerprinting/fingerprint-injector.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-injector.js +449 -44
- package/dist/fingerprinting/fingerprint-injector.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/audio-spoofing.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/audio-spoofing.js +11 -1
- package/dist/fingerprinting/fingerprint-overrides/audio-spoofing.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/canvas-protection.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/canvas-protection.js +11 -1
- package/dist/fingerprinting/fingerprint-overrides/canvas-protection.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/cdp-detection-bypass.d.ts +14 -0
- package/dist/fingerprinting/fingerprint-overrides/cdp-detection-bypass.d.ts.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/cdp-detection-bypass.js +497 -0
- package/dist/fingerprinting/fingerprint-overrides/cdp-detection-bypass.js.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/client-rect-spoofing.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/client-rect-spoofing.js +11 -1
- package/dist/fingerprinting/fingerprint-overrides/client-rect-spoofing.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/datadome-bypass.d.ts +14 -0
- package/dist/fingerprinting/fingerprint-overrides/datadome-bypass.d.ts.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/datadome-bypass.js +536 -0
- package/dist/fingerprinting/fingerprint-overrides/datadome-bypass.js.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/font-spoofing.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/font-spoofing.js +11 -1
- package/dist/fingerprinting/fingerprint-overrides/font-spoofing.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/index.d.ts +16 -1
- package/dist/fingerprinting/fingerprint-overrides/index.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/index.js +19 -1
- package/dist/fingerprinting/fingerprint-overrides/index.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/keyboard-humanization.d.ts +45 -0
- package/dist/fingerprinting/fingerprint-overrides/keyboard-humanization.d.ts.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/keyboard-humanization.js +291 -0
- package/dist/fingerprinting/fingerprint-overrides/keyboard-humanization.js.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/locale-spoofing.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/locale-spoofing.js +54 -22
- package/dist/fingerprinting/fingerprint-overrides/locale-spoofing.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/mouse-humanization.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/mouse-humanization.js +4 -5
- package/dist/fingerprinting/fingerprint-overrides/mouse-humanization.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/performance-spoofing.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/performance-spoofing.js +11 -1
- package/dist/fingerprinting/fingerprint-overrides/performance-spoofing.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/platform-consistency.d.ts +13 -0
- package/dist/fingerprinting/fingerprint-overrides/platform-consistency.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/platform-consistency.js +413 -70
- package/dist/fingerprinting/fingerprint-overrides/platform-consistency.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/runtime-enable-bypass.d.ts +10 -3
- package/dist/fingerprinting/fingerprint-overrides/runtime-enable-bypass.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/runtime-enable-bypass.js +132 -73
- package/dist/fingerprinting/fingerprint-overrides/runtime-enable-bypass.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/scroll-humanization.d.ts +55 -0
- package/dist/fingerprinting/fingerprint-overrides/scroll-humanization.d.ts.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/scroll-humanization.js +380 -0
- package/dist/fingerprinting/fingerprint-overrides/scroll-humanization.js.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/stealth-script.d.ts +8 -0
- package/dist/fingerprinting/fingerprint-overrides/stealth-script.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/stealth-script.js +417 -71
- package/dist/fingerprinting/fingerprint-overrides/stealth-script.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/storage-consistency.d.ts +13 -0
- package/dist/fingerprinting/fingerprint-overrides/storage-consistency.d.ts.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/storage-consistency.js +368 -0
- package/dist/fingerprinting/fingerprint-overrides/storage-consistency.js.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/timing-consistency.d.ts +13 -0
- package/dist/fingerprinting/fingerprint-overrides/timing-consistency.d.ts.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/timing-consistency.js +438 -0
- package/dist/fingerprinting/fingerprint-overrides/timing-consistency.js.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/utils.d.ts +12 -0
- package/dist/fingerprinting/fingerprint-overrides/utils.d.ts.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/utils.js +315 -0
- package/dist/fingerprinting/fingerprint-overrides/utils.js.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/webgl-spoofing.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/webgl-spoofing.js +13 -1
- package/dist/fingerprinting/fingerprint-overrides/webgl-spoofing.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/webgpu-spoofing.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/webgpu-spoofing.js +11 -1
- package/dist/fingerprinting/fingerprint-overrides/webgpu-spoofing.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/webrtc-spoofing.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/webrtc-spoofing.js +11 -1
- package/dist/fingerprinting/fingerprint-overrides/webrtc-spoofing.js.map +1 -1
- package/dist/page.d.ts +12 -0
- package/dist/page.d.ts.map +1 -1
- package/dist/page.js +35 -3
- package/dist/page.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/dist/fingerprinting/canvas-fingerprint.d.ts +0 -4
- package/dist/fingerprinting/canvas-fingerprint.d.ts.map +0 -1
- package/dist/fingerprinting/canvas-fingerprint.js +0 -60
- package/dist/fingerprinting/canvas-fingerprint.js.map +0 -1
|
@@ -1,84 +1,143 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Runtime.enable Bypass
|
|
3
|
-
* Prevents CDP detection through console.log(Error) technique
|
|
2
|
+
* Runtime.enable Bypass Script
|
|
3
|
+
* Prevents CDP detection through console.log(Error) technique and stack trace analysis.
|
|
4
|
+
*
|
|
5
|
+
* IMPORTANT: This is now a SCRIPT to be injected via addScriptToEvaluateOnNewDocument,
|
|
6
|
+
* NOT via Runtime.evaluate (which would defeat the purpose since Runtime.evaluate IS detectable!)
|
|
4
7
|
*/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Creates the runtime enable bypass script that should be bundled with other stealth scripts.
|
|
10
|
+
* This script runs BEFORE page JavaScript and patches Error/console to hide CDP markers.
|
|
11
|
+
*/
|
|
12
|
+
export const createRuntimeEnableBypassScript = () => `
|
|
9
13
|
(() => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const originalSend = window.__CDP_CLIENT__.send;
|
|
13
|
-
window.__CDP_CLIENT__.send = function(method, params) {
|
|
14
|
-
// Block or modify Runtime.enable calls
|
|
15
|
-
if (method === 'Runtime.enable') {
|
|
16
|
-
console.log('Runtime.enable blocked');
|
|
17
|
-
return Promise.resolve({});
|
|
18
|
-
}
|
|
19
|
-
return originalSend.apply(this, arguments);
|
|
20
|
-
};
|
|
21
|
-
}
|
|
14
|
+
'use strict';
|
|
15
|
+
const DEBUG_PREFIX = '[RUNTIME-ENABLE-BYPASS]';
|
|
22
16
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
17
|
+
try {
|
|
18
|
+
// =============================================================================
|
|
19
|
+
// PART 1: Error stack trace sanitization
|
|
20
|
+
// CDP detection often works by examining Error.stack for automation markers
|
|
21
|
+
// =============================================================================
|
|
22
|
+
|
|
23
|
+
const originalError = Error;
|
|
24
|
+
const originalErrorToString = Error.prototype.toString;
|
|
25
|
+
const originalErrorStackDesc = Object.getOwnPropertyDescriptor(Error.prototype, 'stack');
|
|
26
|
+
|
|
27
|
+
// Patterns that reveal CDP/automation
|
|
28
|
+
const CDP_MARKERS = [
|
|
29
|
+
'__puppeteer_evaluation_script__',
|
|
30
|
+
'__playwright_evaluation_script__',
|
|
31
|
+
'CDP_',
|
|
32
|
+
'chrome-extension://',
|
|
33
|
+
'devtools://',
|
|
34
|
+
'pptr:',
|
|
35
|
+
'Runtime.evaluate',
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const sanitizeStack = (stack) => {
|
|
39
|
+
if (!stack || typeof stack !== 'string') return stack;
|
|
30
40
|
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
});
|
|
41
|
+
let sanitized = stack;
|
|
42
|
+
for (const marker of CDP_MARKERS) {
|
|
43
|
+
if (sanitized.includes(marker)) {
|
|
44
|
+
// Replace with generic anonymous reference
|
|
45
|
+
// Simple string replacement (markers don't contain regex special chars)
|
|
46
|
+
while (sanitized.includes(marker)) {
|
|
47
|
+
sanitized = sanitized.split(marker).join('anonymous');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
41
50
|
}
|
|
42
|
-
|
|
43
|
-
|
|
51
|
+
return sanitized;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Override Error.prepareStackTrace if it exists (V8 specific)
|
|
55
|
+
if (typeof Error.prepareStackTrace === 'function') {
|
|
56
|
+
const originalPrepareStackTrace = Error.prepareStackTrace;
|
|
57
|
+
Error.prepareStackTrace = function(error, structuredStackTrace) {
|
|
58
|
+
const stack = originalPrepareStackTrace.call(this, error, structuredStackTrace);
|
|
59
|
+
return sanitizeStack(stack);
|
|
60
|
+
};
|
|
44
61
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
62
|
+
|
|
63
|
+
// Override Error.prototype.stack getter
|
|
64
|
+
if (originalErrorStackDesc && originalErrorStackDesc.get) {
|
|
65
|
+
Object.defineProperty(Error.prototype, 'stack', {
|
|
66
|
+
get: function() {
|
|
67
|
+
const stack = originalErrorStackDesc.get.call(this);
|
|
68
|
+
return sanitizeStack(stack);
|
|
69
|
+
},
|
|
70
|
+
set: originalErrorStackDesc.set,
|
|
71
|
+
configurable: true,
|
|
72
|
+
enumerable: false
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
console.log(DEBUG_PREFIX, '✓ Error stack trace sanitization applied');
|
|
77
|
+
|
|
78
|
+
// =============================================================================
|
|
79
|
+
// PART 2: Console protection
|
|
80
|
+
// Some CDP detection works by examining how Error objects are serialized in console
|
|
81
|
+
// =============================================================================
|
|
82
|
+
|
|
83
|
+
const originalConsole = window.console;
|
|
84
|
+
const consoleMethodsToProtect = ['log', 'warn', 'error', 'info', 'debug', 'trace'];
|
|
85
|
+
|
|
86
|
+
consoleMethodsToProtect.forEach(method => {
|
|
87
|
+
const original = originalConsole[method];
|
|
51
88
|
if (typeof original === 'function') {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
});
|
|
89
|
+
originalConsole[method] = function(...args) {
|
|
90
|
+
// Sanitize Error objects before logging
|
|
91
|
+
const sanitizedArgs = args.map(arg => {
|
|
92
|
+
if (arg instanceof Error) {
|
|
93
|
+
// Clone the error with sanitized stack
|
|
94
|
+
const sanitizedError = new Error(arg.message);
|
|
95
|
+
sanitizedError.name = arg.name;
|
|
96
|
+
// stack will be auto-sanitized by our getter
|
|
97
|
+
return sanitizedError;
|
|
98
|
+
}
|
|
99
|
+
return arg;
|
|
100
|
+
});
|
|
101
|
+
return original.apply(this, sanitizedArgs);
|
|
102
|
+
};
|
|
103
|
+
// Hide the patch
|
|
104
|
+
originalConsole[method].toString = () => \`function \${method}() { [native code] }\`;
|
|
69
105
|
}
|
|
70
|
-
return original;
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
try {
|
|
75
|
-
Object.defineProperty(window, 'console', {
|
|
76
|
-
value: protectedConsole,
|
|
77
|
-
writable: false,
|
|
78
|
-
configurable: false
|
|
79
106
|
});
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
107
|
+
|
|
108
|
+
console.log(DEBUG_PREFIX, '✓ Console protection applied');
|
|
109
|
+
|
|
110
|
+
// =============================================================================
|
|
111
|
+
// PART 3: Protect against CDP WebSocket detection
|
|
112
|
+
// Some advanced detection tries to find the CDP websocket connection
|
|
113
|
+
// =============================================================================
|
|
114
|
+
|
|
115
|
+
// Override WebSocket to hide CDP connections
|
|
116
|
+
const originalWebSocket = window.WebSocket;
|
|
117
|
+
window.WebSocket = function(url, protocols) {
|
|
118
|
+
// Check if this looks like a CDP connection
|
|
119
|
+
if (url && typeof url === 'string') {
|
|
120
|
+
const lowerUrl = url.toLowerCase();
|
|
121
|
+
if (lowerUrl.includes('devtools') ||
|
|
122
|
+
lowerUrl.includes('/json') ||
|
|
123
|
+
lowerUrl.includes('ws://localhost:') ||
|
|
124
|
+
lowerUrl.includes('ws://127.0.0.1:')) {
|
|
125
|
+
// This might be a CDP detection attempt, proceed but mark it
|
|
126
|
+
// We can't block it as that would break legitimate websockets
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return new originalWebSocket(url, protocols);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
window.WebSocket.prototype = originalWebSocket.prototype;
|
|
133
|
+
Object.defineProperty(window.WebSocket, 'name', { value: 'WebSocket' });
|
|
134
|
+
window.WebSocket.toString = () => 'function WebSocket() { [native code] }';
|
|
135
|
+
|
|
136
|
+
console.log(DEBUG_PREFIX, '✓ WebSocket protection applied');
|
|
137
|
+
|
|
138
|
+
} catch (error) {
|
|
139
|
+
console.error(DEBUG_PREFIX, 'Failed to apply bypass:', error.message);
|
|
140
|
+
}
|
|
141
|
+
})();
|
|
142
|
+
`;
|
|
84
143
|
//# sourceMappingURL=runtime-enable-bypass.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-enable-bypass.js","sourceRoot":"","sources":["../../../src/fingerprinting/fingerprint-overrides/runtime-enable-bypass.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"runtime-enable-bypass.js","sourceRoot":"","sources":["../../../src/fingerprinting/fingerprint-overrides/runtime-enable-bypass.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,GAAW,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkI5D,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scroll Humanization Module
|
|
3
|
+
* Simulates realistic scroll behavior for 2024-2025
|
|
4
|
+
*
|
|
5
|
+
* Features:
|
|
6
|
+
* - Natural scroll momentum physics
|
|
7
|
+
* - Reading-speed based scroll patterns
|
|
8
|
+
* - Scroll pause at content sections
|
|
9
|
+
* - Touch-like vs mouse wheel differentiation
|
|
10
|
+
* - CDP-level scroll enhancement
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Scroll configuration
|
|
14
|
+
*/
|
|
15
|
+
interface ScrollConfig {
|
|
16
|
+
/** Base scroll speed in pixels per scroll event */
|
|
17
|
+
baseScrollAmount: number;
|
|
18
|
+
/** Variance in scroll amount */
|
|
19
|
+
scrollVariance: number;
|
|
20
|
+
/** Deceleration factor for momentum scrolling */
|
|
21
|
+
deceleration: number;
|
|
22
|
+
/** Minimum velocity before stopping */
|
|
23
|
+
minVelocity: number;
|
|
24
|
+
/** Reading pause probability (0-1) */
|
|
25
|
+
readingPauseProbability: number;
|
|
26
|
+
/** Reading pause duration range in ms [min, max] */
|
|
27
|
+
readingPauseDuration: [number, number];
|
|
28
|
+
/** Delay between scroll events in ms */
|
|
29
|
+
eventDelay: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Generate human-like scroll positions using momentum physics
|
|
33
|
+
*/
|
|
34
|
+
export declare const generateScrollPath: (startY: number, targetY: number, config?: Partial<ScrollConfig>) => {
|
|
35
|
+
y: number;
|
|
36
|
+
delay: number;
|
|
37
|
+
}[];
|
|
38
|
+
/**
|
|
39
|
+
* Create a script that enhances scroll events in the browser
|
|
40
|
+
*/
|
|
41
|
+
export declare const createScrollHumanizationScript: () => string;
|
|
42
|
+
/**
|
|
43
|
+
* CDP-level human-like scroll function
|
|
44
|
+
*/
|
|
45
|
+
export declare const humanScroll: (client: unknown, targetY: number, config?: Partial<ScrollConfig>) => Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Scroll to element with human-like behavior
|
|
48
|
+
*/
|
|
49
|
+
export declare const humanScrollToElement: (client: unknown, selector: string, config?: Partial<ScrollConfig>) => Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Setup scroll humanization at CDP level
|
|
52
|
+
*/
|
|
53
|
+
export declare const setupScrollHumanization: (client: unknown) => Promise<void>;
|
|
54
|
+
export {};
|
|
55
|
+
//# sourceMappingURL=scroll-humanization.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scroll-humanization.d.ts","sourceRoot":"","sources":["../../../src/fingerprinting/fingerprint-overrides/scroll-humanization.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;GAEG;AACH,UAAU,YAAY;IAClB,mDAAmD;IACnD,gBAAgB,EAAE,MAAM,CAAC;IACzB,gCAAgC;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,iDAAiD;IACjD,YAAY,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,uBAAuB,EAAE,MAAM,CAAC;IAChC,oDAAoD;IACpD,oBAAoB,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,wCAAwC;IACxC,UAAU,EAAE,MAAM,CAAC;CACtB;AAYD;;GAEG;AACH,eAAO,MAAM,kBAAkB,WACnB,MAAM,WACL,MAAM,WACP,OAAO,CAAC,YAAY,CAAC,KAC9B;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAyD9B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,8BAA8B,QAAO,MA0NjD,CAAC;AAOF;;GAEG;AACH,eAAO,MAAM,WAAW,WACZ,OAAO,WACN,MAAM,WACP,OAAO,CAAC,YAAY,CAAC,KAC9B,OAAO,CAAC,IAAI,CA0Dd,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,oBAAoB,WACrB,OAAO,YACL,MAAM,WACR,OAAO,CAAC,YAAY,CAAC,KAC9B,OAAO,CAAC,IAAI,CAuCd,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,uBAAuB,WAAkB,OAAO,KAAG,OAAO,CAAC,IAAI,CAY3E,CAAC"}
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scroll Humanization Module
|
|
3
|
+
* Simulates realistic scroll behavior for 2024-2025
|
|
4
|
+
*
|
|
5
|
+
* Features:
|
|
6
|
+
* - Natural scroll momentum physics
|
|
7
|
+
* - Reading-speed based scroll patterns
|
|
8
|
+
* - Scroll pause at content sections
|
|
9
|
+
* - Touch-like vs mouse wheel differentiation
|
|
10
|
+
* - CDP-level scroll enhancement
|
|
11
|
+
*/
|
|
12
|
+
const DEFAULT_SCROLL_CONFIG = {
|
|
13
|
+
baseScrollAmount: 100,
|
|
14
|
+
scrollVariance: 30,
|
|
15
|
+
deceleration: 0.95,
|
|
16
|
+
minVelocity: 5,
|
|
17
|
+
readingPauseProbability: 0.15,
|
|
18
|
+
readingPauseDuration: [500, 2000],
|
|
19
|
+
eventDelay: 16, // ~60fps
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Generate human-like scroll positions using momentum physics
|
|
23
|
+
*/
|
|
24
|
+
export const generateScrollPath = (startY, targetY, config = {}) => {
|
|
25
|
+
const cfg = { ...DEFAULT_SCROLL_CONFIG, ...config };
|
|
26
|
+
const path = [];
|
|
27
|
+
const direction = targetY > startY ? 1 : -1;
|
|
28
|
+
const totalDistance = Math.abs(targetY - startY);
|
|
29
|
+
// Initial velocity based on distance
|
|
30
|
+
let velocity = Math.min(cfg.baseScrollAmount * 2, totalDistance / 10);
|
|
31
|
+
let currentY = startY;
|
|
32
|
+
while (Math.abs(targetY - currentY) > cfg.minVelocity) {
|
|
33
|
+
// Apply deceleration as we approach target
|
|
34
|
+
const remainingDistance = Math.abs(targetY - currentY);
|
|
35
|
+
const decelerationFactor = remainingDistance < 200 ?
|
|
36
|
+
cfg.deceleration * 0.8 : cfg.deceleration;
|
|
37
|
+
velocity *= decelerationFactor;
|
|
38
|
+
// Add variance to make it look natural
|
|
39
|
+
const variance = (Math.random() - 0.5) * cfg.scrollVariance;
|
|
40
|
+
const scrollAmount = (velocity + variance) * direction;
|
|
41
|
+
currentY += scrollAmount;
|
|
42
|
+
// Clamp to target
|
|
43
|
+
if (direction > 0) {
|
|
44
|
+
currentY = Math.min(currentY, targetY);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
currentY = Math.max(currentY, targetY);
|
|
48
|
+
}
|
|
49
|
+
// Add slight variation to timing
|
|
50
|
+
const delay = cfg.eventDelay + (Math.random() - 0.5) * 8;
|
|
51
|
+
path.push({ y: Math.round(currentY), delay: Math.round(delay) });
|
|
52
|
+
// Occasionally pause as if reading
|
|
53
|
+
if (Math.random() < cfg.readingPauseProbability && path.length > 3) {
|
|
54
|
+
const pauseDuration = cfg.readingPauseDuration[0] +
|
|
55
|
+
Math.random() * (cfg.readingPauseDuration[1] - cfg.readingPauseDuration[0]);
|
|
56
|
+
path.push({ y: Math.round(currentY), delay: Math.round(pauseDuration) });
|
|
57
|
+
// Reset velocity after pause (user starts scrolling again)
|
|
58
|
+
velocity = cfg.baseScrollAmount;
|
|
59
|
+
}
|
|
60
|
+
// Ensure we don't get stuck in an infinite loop
|
|
61
|
+
if (velocity < cfg.minVelocity) {
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Final position
|
|
66
|
+
path.push({ y: targetY, delay: cfg.eventDelay });
|
|
67
|
+
return path;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Create a script that enhances scroll events in the browser
|
|
71
|
+
*/
|
|
72
|
+
export const createScrollHumanizationScript = () => {
|
|
73
|
+
return `
|
|
74
|
+
(() => {
|
|
75
|
+
'use strict';
|
|
76
|
+
|
|
77
|
+
// Scroll Humanization for 2024-2025
|
|
78
|
+
const humanizeScroll = () => {
|
|
79
|
+
console.log('[SCROLL-DEBUG] Initializing scroll humanization');
|
|
80
|
+
|
|
81
|
+
// Track scroll state
|
|
82
|
+
let lastScrollTime = 0;
|
|
83
|
+
let scrollVelocity = 0;
|
|
84
|
+
let isScrolling = false;
|
|
85
|
+
let scrollTimeout = null;
|
|
86
|
+
|
|
87
|
+
// Generate consistent scroll variations
|
|
88
|
+
const generateScrollId = () => {
|
|
89
|
+
return Math.random().toString(36).substr(2, 9);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// Store original wheel event constructor
|
|
93
|
+
const OriginalWheelEvent = WheelEvent;
|
|
94
|
+
|
|
95
|
+
// Override WheelEvent to add realistic properties
|
|
96
|
+
window.WheelEvent = function(type, eventInitDict = {}) {
|
|
97
|
+
const enhancedInit = { ...eventInitDict };
|
|
98
|
+
|
|
99
|
+
// Add realistic delta values if not set
|
|
100
|
+
if (enhancedInit.deltaY === undefined) {
|
|
101
|
+
enhancedInit.deltaY = 100 + (Math.random() - 0.5) * 30;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Add variance to existing delta
|
|
105
|
+
if (typeof enhancedInit.deltaY === 'number') {
|
|
106
|
+
enhancedInit.deltaY += (Math.random() - 0.5) * 10;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Ensure proper delta mode (0 = pixels, 1 = lines, 2 = pages)
|
|
110
|
+
if (enhancedInit.deltaMode === undefined) {
|
|
111
|
+
enhancedInit.deltaMode = 0; // Pixels is most common
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return new OriginalWheelEvent(type, enhancedInit);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// Note: Don't assign to prototype - it may be frozen in modern Chrome
|
|
118
|
+
// The Proxy wrapper handles prototype access via get trap if needed
|
|
119
|
+
console.log('[SCROLL-DEBUG] WheelEvent override installed');
|
|
120
|
+
|
|
121
|
+
// Enhanced scroll behavior tracking
|
|
122
|
+
const trackScrollBehavior = () => {
|
|
123
|
+
console.log('[SCROLL-DEBUG] Setting up scroll behavior tracking');
|
|
124
|
+
const originalScrollTo = window.scrollTo;
|
|
125
|
+
const originalScrollBy = window.scrollBy;
|
|
126
|
+
const originalScrollIntoView = Element.prototype.scrollIntoView;
|
|
127
|
+
|
|
128
|
+
let scrollCallCount = 0;
|
|
129
|
+
|
|
130
|
+
// Override scrollTo to add human-like behavior
|
|
131
|
+
window.scrollTo = function(options) {
|
|
132
|
+
scrollCallCount++;
|
|
133
|
+
console.log('[SCROLL-DEBUG] scrollTo called', {
|
|
134
|
+
callCount: scrollCallCount,
|
|
135
|
+
options,
|
|
136
|
+
scrollY: window.scrollY
|
|
137
|
+
});
|
|
138
|
+
// Don't interfere with normal scrolling - just pass through
|
|
139
|
+
// The humanization happens at the wheel event level
|
|
140
|
+
return originalScrollTo.apply(this, arguments);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// Override scrollBy similarly
|
|
144
|
+
window.scrollBy = function(options) {
|
|
145
|
+
scrollCallCount++;
|
|
146
|
+
console.log('[SCROLL-DEBUG] scrollBy called', {
|
|
147
|
+
callCount: scrollCallCount,
|
|
148
|
+
options: typeof options === 'object' ? options : { x: arguments[0], y: arguments[1] },
|
|
149
|
+
scrollY: window.scrollY
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Create a copy of options to avoid modifying the original
|
|
153
|
+
if (typeof options === 'object' && options !== null) {
|
|
154
|
+
const modifiedOptions = { ...options };
|
|
155
|
+
// Add slight variance to scroll amount
|
|
156
|
+
if (modifiedOptions.top !== undefined) {
|
|
157
|
+
modifiedOptions.top += (Math.random() - 0.5) * 2;
|
|
158
|
+
}
|
|
159
|
+
if (modifiedOptions.left !== undefined) {
|
|
160
|
+
modifiedOptions.left += (Math.random() - 0.5) * 2;
|
|
161
|
+
}
|
|
162
|
+
return originalScrollBy.call(this, modifiedOptions);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return originalScrollBy.apply(this, arguments);
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// Override scrollIntoView
|
|
169
|
+
Element.prototype.scrollIntoView = function(options) {
|
|
170
|
+
scrollCallCount++;
|
|
171
|
+
console.log('[SCROLL-DEBUG] scrollIntoView called', {
|
|
172
|
+
callCount: scrollCallCount,
|
|
173
|
+
element: this.tagName,
|
|
174
|
+
options
|
|
175
|
+
});
|
|
176
|
+
// Just pass through to original - don't break scrolling
|
|
177
|
+
return originalScrollIntoView.call(this, options);
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
console.log('[SCROLL-DEBUG] Scroll overrides installed');
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// Simulate natural scroll momentum
|
|
184
|
+
const addScrollMomentum = () => {
|
|
185
|
+
let momentumId = null;
|
|
186
|
+
let currentVelocity = 0;
|
|
187
|
+
let wheelEventCount = 0;
|
|
188
|
+
|
|
189
|
+
console.log('[SCROLL-DEBUG] Setting up scroll momentum');
|
|
190
|
+
|
|
191
|
+
window.addEventListener('wheel', (event) => {
|
|
192
|
+
wheelEventCount++;
|
|
193
|
+
console.log('[SCROLL-DEBUG] Wheel event', {
|
|
194
|
+
count: wheelEventCount,
|
|
195
|
+
deltaY: event.deltaY,
|
|
196
|
+
scrollY: window.scrollY,
|
|
197
|
+
maxScrollY: document.documentElement.scrollHeight - window.innerHeight
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// Calculate scroll velocity
|
|
201
|
+
const now = performance.now();
|
|
202
|
+
const timeDelta = now - lastScrollTime;
|
|
203
|
+
|
|
204
|
+
if (timeDelta > 0 && timeDelta < 500) {
|
|
205
|
+
currentVelocity = event.deltaY / timeDelta;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
lastScrollTime = now;
|
|
209
|
+
|
|
210
|
+
// Cancel any existing momentum
|
|
211
|
+
if (momentumId) {
|
|
212
|
+
cancelAnimationFrame(momentumId);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Start momentum scrolling
|
|
216
|
+
const applyMomentum = () => {
|
|
217
|
+
if (Math.abs(currentVelocity) < 0.1) {
|
|
218
|
+
console.log('[SCROLL-DEBUG] Momentum stopped (velocity too low)');
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
currentVelocity *= 0.95; // Deceleration
|
|
223
|
+
window.scrollBy(0, currentVelocity * 16);
|
|
224
|
+
|
|
225
|
+
momentumId = requestAnimationFrame(applyMomentum);
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
// Only apply momentum on natural scroll end
|
|
229
|
+
// (not when user is actively scrolling)
|
|
230
|
+
clearTimeout(scrollTimeout);
|
|
231
|
+
scrollTimeout = setTimeout(() => {
|
|
232
|
+
if (Math.abs(currentVelocity) > 0.5) {
|
|
233
|
+
console.log('[SCROLL-DEBUG] Starting momentum scroll', { velocity: currentVelocity });
|
|
234
|
+
applyMomentum();
|
|
235
|
+
}
|
|
236
|
+
}, 100);
|
|
237
|
+
}, { passive: true });
|
|
238
|
+
|
|
239
|
+
console.log('[SCROLL-DEBUG] Wheel event listener attached');
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
// Track and enhance touch scroll events
|
|
243
|
+
const enhanceTouchScroll = () => {
|
|
244
|
+
let touchStartY = 0;
|
|
245
|
+
let touchStartTime = 0;
|
|
246
|
+
|
|
247
|
+
window.addEventListener('touchstart', (event) => {
|
|
248
|
+
if (event.touches.length === 1) {
|
|
249
|
+
touchStartY = event.touches[0].clientY;
|
|
250
|
+
touchStartTime = performance.now();
|
|
251
|
+
}
|
|
252
|
+
}, { passive: true });
|
|
253
|
+
|
|
254
|
+
window.addEventListener('touchend', (event) => {
|
|
255
|
+
const touchDuration = performance.now() - touchStartTime;
|
|
256
|
+
const touchEndY = event.changedTouches[0]?.clientY || touchStartY;
|
|
257
|
+
const distance = touchStartY - touchEndY;
|
|
258
|
+
|
|
259
|
+
// Calculate velocity for momentum
|
|
260
|
+
if (touchDuration > 0) {
|
|
261
|
+
scrollVelocity = distance / touchDuration;
|
|
262
|
+
}
|
|
263
|
+
}, { passive: true });
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
// Initialize scroll humanization
|
|
267
|
+
console.log('[SCROLL-DEBUG] Starting scroll humanization initialization');
|
|
268
|
+
trackScrollBehavior();
|
|
269
|
+
addScrollMomentum();
|
|
270
|
+
enhanceTouchScroll();
|
|
271
|
+
console.log('[SCROLL-DEBUG] Scroll humanization fully initialized');
|
|
272
|
+
|
|
273
|
+
// Log document ready state
|
|
274
|
+
console.log('[SCROLL-DEBUG] Document ready state:', document.readyState);
|
|
275
|
+
console.log('[SCROLL-DEBUG] Body scrollHeight:', document.body?.scrollHeight || 'N/A');
|
|
276
|
+
console.log('[SCROLL-DEBUG] Document scrollHeight:', document.documentElement?.scrollHeight || 'N/A');
|
|
277
|
+
console.log('[SCROLL-DEBUG] Window innerHeight:', window.innerHeight);
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
// Don't catch errors - let them bubble up so we can see them
|
|
281
|
+
humanizeScroll();
|
|
282
|
+
|
|
283
|
+
// Also log when DOM is ready
|
|
284
|
+
if (document.readyState === 'loading') {
|
|
285
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
286
|
+
console.log('[SCROLL-DEBUG] DOM loaded - scrollHeight:', document.documentElement?.scrollHeight || 'N/A');
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
})();`;
|
|
290
|
+
};
|
|
291
|
+
/**
|
|
292
|
+
* Helper sleep function
|
|
293
|
+
*/
|
|
294
|
+
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
295
|
+
/**
|
|
296
|
+
* CDP-level human-like scroll function
|
|
297
|
+
*/
|
|
298
|
+
export const humanScroll = async (client, targetY, config = {}) => {
|
|
299
|
+
const inputClient = client;
|
|
300
|
+
// Get current scroll position
|
|
301
|
+
const result = await inputClient.Runtime.evaluate({
|
|
302
|
+
expression: 'window.scrollY',
|
|
303
|
+
returnByValue: true,
|
|
304
|
+
});
|
|
305
|
+
const startY = result.result.value;
|
|
306
|
+
// Generate scroll path
|
|
307
|
+
const path = generateScrollPath(startY, targetY, config);
|
|
308
|
+
// Get viewport dimensions for mouse position
|
|
309
|
+
const viewportResult = await inputClient.Runtime.evaluate({
|
|
310
|
+
expression: 'JSON.stringify({ width: window.innerWidth, height: window.innerHeight })',
|
|
311
|
+
returnByValue: true,
|
|
312
|
+
});
|
|
313
|
+
const viewport = JSON.parse(viewportResult.result.value);
|
|
314
|
+
// Position mouse in center of viewport
|
|
315
|
+
const mouseX = viewport.width / 2;
|
|
316
|
+
const mouseY = viewport.height / 2;
|
|
317
|
+
// Execute scroll path
|
|
318
|
+
let lastY = startY;
|
|
319
|
+
for (const point of path) {
|
|
320
|
+
const deltaY = point.y - lastY;
|
|
321
|
+
if (deltaY !== 0) {
|
|
322
|
+
// Dispatch wheel event
|
|
323
|
+
await inputClient.Input.dispatchMouseEvent({
|
|
324
|
+
type: 'mouseWheel',
|
|
325
|
+
x: mouseX,
|
|
326
|
+
y: mouseY,
|
|
327
|
+
deltaX: 0,
|
|
328
|
+
deltaY: deltaY,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
lastY = point.y;
|
|
332
|
+
// Wait for delay
|
|
333
|
+
await sleep(point.delay);
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
/**
|
|
337
|
+
* Scroll to element with human-like behavior
|
|
338
|
+
*/
|
|
339
|
+
export const humanScrollToElement = async (client, selector, config = {}) => {
|
|
340
|
+
const runtimeClient = client;
|
|
341
|
+
// Get element position
|
|
342
|
+
const result = await runtimeClient.Runtime.evaluate({
|
|
343
|
+
expression: `
|
|
344
|
+
(() => {
|
|
345
|
+
const element = document.querySelector('${selector.replace(/'/g, "\\'")}');
|
|
346
|
+
if (!element) return null;
|
|
347
|
+
const rect = element.getBoundingClientRect();
|
|
348
|
+
return {
|
|
349
|
+
top: rect.top + window.scrollY,
|
|
350
|
+
height: rect.height
|
|
351
|
+
};
|
|
352
|
+
})()
|
|
353
|
+
`,
|
|
354
|
+
returnByValue: true,
|
|
355
|
+
});
|
|
356
|
+
const elementInfo = result.result.value;
|
|
357
|
+
if (!elementInfo) {
|
|
358
|
+
throw new Error(`Element not found: ${selector}`);
|
|
359
|
+
}
|
|
360
|
+
// Calculate target scroll position (element centered in viewport)
|
|
361
|
+
const viewportResult = await runtimeClient.Runtime.evaluate({
|
|
362
|
+
expression: 'window.innerHeight',
|
|
363
|
+
returnByValue: true,
|
|
364
|
+
});
|
|
365
|
+
const viewportHeight = viewportResult.result.value;
|
|
366
|
+
const targetY = elementInfo.top - (viewportHeight / 2) + (elementInfo.height / 2);
|
|
367
|
+
await humanScroll(client, Math.max(0, targetY), config);
|
|
368
|
+
};
|
|
369
|
+
/**
|
|
370
|
+
* Setup scroll humanization at CDP level
|
|
371
|
+
*/
|
|
372
|
+
export const setupScrollHumanization = async (client) => {
|
|
373
|
+
const runtimeClient = client;
|
|
374
|
+
// NOTE: Using addScriptToEvaluateOnNewDocument instead of Runtime.evaluate
|
|
375
|
+
// Runtime.evaluate is a CDP detection vector
|
|
376
|
+
await runtimeClient.Page.addScriptToEvaluateOnNewDocument({
|
|
377
|
+
source: createScrollHumanizationScript()
|
|
378
|
+
});
|
|
379
|
+
};
|
|
380
|
+
//# sourceMappingURL=scroll-humanization.js.map
|