stealth-ws 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.
- package/LICENSE +21 -0
- package/README.md +273 -0
- package/bin/stealth-bridge.exe +0 -0
- package/lib/cookie-jar.js +276 -0
- package/lib/fingerprint.js +208 -0
- package/lib/index.d.ts +74 -0
- package/lib/index.js +29 -0
- package/lib/websocket.js +416 -0
- package/package.json +58 -0
- package/prebuilds/darwin-x64/stealth-bridge +0 -0
- package/prebuilds/linux-x64/stealth-bridge +0 -0
- package/prebuilds/win32-x64/stealth-bridge.exe +0 -0
- package/scripts/build-all.js +102 -0
- package/scripts/build-go.js +134 -0
- package/scripts/download-binary.js +50 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fingerprint profile management
|
|
3
|
+
*
|
|
4
|
+
* Manages TLS fingerprint profiles for different browsers.
|
|
5
|
+
* These are passed to the Go bridge for TLS handshake spoofing.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readFileSync } from 'fs';
|
|
9
|
+
import { join, dirname } from 'path';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
|
|
12
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Available fingerprint profiles
|
|
16
|
+
* Maps profile name to Go uTLS Hello spec name
|
|
17
|
+
*/
|
|
18
|
+
export const PROFILES = {
|
|
19
|
+
// Chrome versions
|
|
20
|
+
'chrome120': 'HelloChrome_120',
|
|
21
|
+
'chrome119': 'HelloChrome_119',
|
|
22
|
+
'chrome118': 'HelloChrome_118',
|
|
23
|
+
'chrome117': 'HelloChrome_117',
|
|
24
|
+
'chrome116': 'HelloChrome_116',
|
|
25
|
+
'chrome115': 'HelloChrome_115',
|
|
26
|
+
'chrome114': 'HelloChrome_114',
|
|
27
|
+
'chrome113': 'HelloChrome_113',
|
|
28
|
+
'chrome112': 'HelloChrome_112',
|
|
29
|
+
'chrome110': 'HelloChrome_110',
|
|
30
|
+
'chrome100': 'HelloChrome_100_Auto',
|
|
31
|
+
'chromeAuto': 'HelloChrome_Auto',
|
|
32
|
+
|
|
33
|
+
// Firefox versions
|
|
34
|
+
'firefox121': 'HelloFirefox_121',
|
|
35
|
+
'firefox120': 'HelloFirefox_120',
|
|
36
|
+
'firefox115': 'HelloFirefox_115',
|
|
37
|
+
'firefox110': 'HelloFirefox_110',
|
|
38
|
+
'firefox100': 'HelloFirefox_100',
|
|
39
|
+
'firefoxAuto': 'HelloFirefox_Auto',
|
|
40
|
+
|
|
41
|
+
// Safari versions
|
|
42
|
+
'safari17': 'HelloSafari_17_0',
|
|
43
|
+
'safari16': 'HelloSafari_16_6',
|
|
44
|
+
'safari15': 'HelloSafari_15_6',
|
|
45
|
+
'safari14': 'HelloSafari_14_1',
|
|
46
|
+
'safariAuto': 'HelloSafari_Auto',
|
|
47
|
+
|
|
48
|
+
// Edge versions
|
|
49
|
+
'edge120': 'HelloEdge_120',
|
|
50
|
+
'edge119': 'HelloEdge_119',
|
|
51
|
+
'edge118': 'HelloEdge_118',
|
|
52
|
+
'edge117': 'HelloEdge_117',
|
|
53
|
+
'edge116': 'HelloEdge_116',
|
|
54
|
+
'edgeAuto': 'HelloEdge_Auto',
|
|
55
|
+
|
|
56
|
+
// iOS
|
|
57
|
+
'ios17': 'HelloIOS_17_1',
|
|
58
|
+
'ios16': 'HelloIOS_16_1',
|
|
59
|
+
'ios15': 'HelloIOS_15_5',
|
|
60
|
+
'iosAuto': 'HelloIOS_Auto',
|
|
61
|
+
|
|
62
|
+
// Android
|
|
63
|
+
'android12': 'HelloAndroid_12',
|
|
64
|
+
'android11': 'HelloAndroid_11',
|
|
65
|
+
'androidAuto': 'HelloAndroid_Auto'
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* FingerprintProfiles class for managing browser fingerprints
|
|
70
|
+
*/
|
|
71
|
+
export class FingerprintProfiles {
|
|
72
|
+
/**
|
|
73
|
+
* List all available fingerprint profiles
|
|
74
|
+
*
|
|
75
|
+
* @returns {string[]} Array of profile names
|
|
76
|
+
*/
|
|
77
|
+
static list() {
|
|
78
|
+
return Object.keys(PROFILES);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Get the Go Hello spec name for a profile
|
|
83
|
+
*
|
|
84
|
+
* @param {string} profile - Profile name
|
|
85
|
+
* @returns {string} Go Hello spec name
|
|
86
|
+
*/
|
|
87
|
+
static getSpecName(profile) {
|
|
88
|
+
const specName = PROFILES[profile];
|
|
89
|
+
if (!specName) {
|
|
90
|
+
console.warn(`Unknown fingerprint profile: ${profile}, using chrome120`);
|
|
91
|
+
return 'HelloChrome_120';
|
|
92
|
+
}
|
|
93
|
+
return specName;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Validate a profile name
|
|
98
|
+
*
|
|
99
|
+
* @param {string} profile - Profile name
|
|
100
|
+
* @returns {boolean} True if valid
|
|
101
|
+
*/
|
|
102
|
+
static isValid(profile) {
|
|
103
|
+
return profile in PROFILES;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get profile by category
|
|
108
|
+
*
|
|
109
|
+
* @param {string} category - 'chrome', 'firefox', 'safari', 'edge', 'ios', 'android'
|
|
110
|
+
* @returns {string[]} Array of profile names in category
|
|
111
|
+
*/
|
|
112
|
+
static byCategory(category) {
|
|
113
|
+
const prefix = category.toLowerCase();
|
|
114
|
+
return Object.keys(PROFILES).filter(name => name.startsWith(prefix));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get recommended profile for general use
|
|
119
|
+
*
|
|
120
|
+
* @returns {string} Recommended profile name
|
|
121
|
+
*/
|
|
122
|
+
static recommended() {
|
|
123
|
+
return 'chrome120';
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Get profile with specific Chrome version
|
|
128
|
+
*
|
|
129
|
+
* @param {number} version - Chrome version (e.g., 120)
|
|
130
|
+
* @returns {string} Profile name
|
|
131
|
+
*/
|
|
132
|
+
static chromeVersion(version) {
|
|
133
|
+
const profile = `chrome${version}`;
|
|
134
|
+
if (PROFILES[profile]) {
|
|
135
|
+
return profile;
|
|
136
|
+
}
|
|
137
|
+
// Find closest version
|
|
138
|
+
const versions = Object.keys(PROFILES)
|
|
139
|
+
.filter(k => k.startsWith('chrome') && k !== 'chromeAuto')
|
|
140
|
+
.map(k => parseInt(k.replace('chrome', '')))
|
|
141
|
+
.sort((a, b) => b - a);
|
|
142
|
+
|
|
143
|
+
const closest = versions.find(v => v <= version) || versions[0];
|
|
144
|
+
return `chrome${closest}`;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Get all Chrome profiles
|
|
149
|
+
*
|
|
150
|
+
* @returns {string[]} Chrome profile names
|
|
151
|
+
*/
|
|
152
|
+
static chrome() {
|
|
153
|
+
return this.byCategory('chrome');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Get all Firefox profiles
|
|
158
|
+
*
|
|
159
|
+
* @returns {string[]} Firefox profile names
|
|
160
|
+
*/
|
|
161
|
+
static firefox() {
|
|
162
|
+
return this.byCategory('firefox');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Get all Safari profiles
|
|
167
|
+
*
|
|
168
|
+
* @returns {string[]} Safari profile names
|
|
169
|
+
*/
|
|
170
|
+
static safari() {
|
|
171
|
+
return this.byCategory('safari');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Get all Edge profiles
|
|
176
|
+
*
|
|
177
|
+
* @returns {string[]} Edge profile names
|
|
178
|
+
*/
|
|
179
|
+
static edge() {
|
|
180
|
+
return this.byCategory('edge');
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Get default fingerprint configuration
|
|
186
|
+
*
|
|
187
|
+
* @returns {Object} Default fingerprint config
|
|
188
|
+
*/
|
|
189
|
+
export function getDefaultFingerprint() {
|
|
190
|
+
return {
|
|
191
|
+
profile: 'chrome120',
|
|
192
|
+
specName: PROFILES['chrome120']
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Create a custom fingerprint configuration
|
|
198
|
+
*
|
|
199
|
+
* @param {Object} options - Custom fingerprint options
|
|
200
|
+
* @returns {Object} Fingerprint configuration
|
|
201
|
+
*/
|
|
202
|
+
export function createCustomFingerprint(options = {}) {
|
|
203
|
+
return {
|
|
204
|
+
profile: options.profile || 'custom',
|
|
205
|
+
specName: options.specName || 'HelloChrome_Auto',
|
|
206
|
+
customSpec: options.customSpec || null
|
|
207
|
+
};
|
|
208
|
+
}
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
|
|
3
|
+
import { EventEmitter } from 'events';
|
|
4
|
+
|
|
5
|
+
export interface WebSocketOptions {
|
|
6
|
+
fingerprint?: string;
|
|
7
|
+
cookies?: string | Array<{ name: string; value: string }> | CookieJar;
|
|
8
|
+
proxy?: string;
|
|
9
|
+
headers?: Record<string, string>;
|
|
10
|
+
perMessageDeflate?: boolean;
|
|
11
|
+
debug?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export declare class WebSocket extends EventEmitter {
|
|
15
|
+
static readonly CONNECTING: 0;
|
|
16
|
+
static readonly OPEN: 1;
|
|
17
|
+
static readonly CLOSING: 2;
|
|
18
|
+
static readonly CLOSED: 3;
|
|
19
|
+
|
|
20
|
+
readonly url: string;
|
|
21
|
+
readonly readyState: 0 | 1 | 2 | 3;
|
|
22
|
+
readonly protocol: string;
|
|
23
|
+
readonly extensions: string;
|
|
24
|
+
readonly bufferedAmount: number;
|
|
25
|
+
binaryType: 'nodebuffer' | 'arraybuffer' | 'fragments';
|
|
26
|
+
|
|
27
|
+
constructor(url: string, options?: WebSocketOptions);
|
|
28
|
+
|
|
29
|
+
send(data: string | Buffer, options?: { binary?: boolean }, callback?: (err?: Error) => void): void;
|
|
30
|
+
close(code?: number, reason?: string): void;
|
|
31
|
+
terminate(): void;
|
|
32
|
+
ping(data?: string | Buffer, mask?: boolean, callback?: (err?: Error) => void): void;
|
|
33
|
+
pong(data?: string | Buffer, mask?: boolean, callback?: (err?: Error) => void): void;
|
|
34
|
+
pause(): void;
|
|
35
|
+
resume(): void;
|
|
36
|
+
|
|
37
|
+
addEventListener(type: 'open', listener: () => void): void;
|
|
38
|
+
addEventListener(type: 'message', listener: (data: Buffer, isBinary: boolean) => void): void;
|
|
39
|
+
addEventListener(type: 'close', listener: (code: number, reason: string) => void): void;
|
|
40
|
+
addEventListener(type: 'error', listener: (err: Error) => void): void;
|
|
41
|
+
addEventListener(type: string, listener: (...args: unknown[]) => void): void;
|
|
42
|
+
|
|
43
|
+
removeEventListener(type: string, listener: (...args: unknown[]) => void): void;
|
|
44
|
+
|
|
45
|
+
on(event: 'open', listener: () => void): this;
|
|
46
|
+
on(event: 'message', listener: (data: Buffer, isBinary: boolean) => void): this;
|
|
47
|
+
on(event: 'close', listener: (code: number, reason: string) => void): this;
|
|
48
|
+
on(event: 'error', listener: (err: Error) => void): this;
|
|
49
|
+
on(event: 'ping', listener: (data?: Buffer) => void): this;
|
|
50
|
+
on(event: 'auth_required', listener: () => void): this;
|
|
51
|
+
on(event: string, listener: (...args: unknown[]) => void): this;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export declare class FingerprintProfiles {
|
|
55
|
+
static list(): string[];
|
|
56
|
+
static getSpecName(profile: string): string;
|
|
57
|
+
static isValid(profile: string): boolean;
|
|
58
|
+
static byCategory(category: 'chrome' | 'firefox' | 'safari' | 'edge' | 'ios' | 'android'): string[];
|
|
59
|
+
static recommended(): string;
|
|
60
|
+
static chromeVersion(version: number): string;
|
|
61
|
+
static chrome(): string[];
|
|
62
|
+
static firefox(): string[];
|
|
63
|
+
static safari(): string[];
|
|
64
|
+
static edge(): string[];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export declare class CookieJar {
|
|
68
|
+
loadFromFile(path: string): void;
|
|
69
|
+
getCookieString(url: string): string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export declare const VERSION: string;
|
|
73
|
+
|
|
74
|
+
export default WebSocket;
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* stealth-ws - WebSocket client with TLS fingerprint spoofing
|
|
3
|
+
*
|
|
4
|
+
* A drop-in replacement for the 'ws' package that spoofs browser
|
|
5
|
+
* TLS fingerprints to bypass Cloudflare and other bot detection systems.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { WebSocket } from './websocket.js';
|
|
9
|
+
import { FingerprintProfiles } from './fingerprint.js';
|
|
10
|
+
import { CookieJar } from './cookie-jar.js';
|
|
11
|
+
|
|
12
|
+
// Re-export WebSocket class as default export
|
|
13
|
+
export default WebSocket;
|
|
14
|
+
|
|
15
|
+
// Export named exports
|
|
16
|
+
export {
|
|
17
|
+
WebSocket,
|
|
18
|
+
FingerprintProfiles,
|
|
19
|
+
CookieJar
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// Static properties for compatibility with ws
|
|
23
|
+
WebSocket.CONNECTING = 0;
|
|
24
|
+
WebSocket.OPEN = 1;
|
|
25
|
+
WebSocket.CLOSING = 2;
|
|
26
|
+
WebSocket.CLOSED = 3;
|
|
27
|
+
|
|
28
|
+
// Version
|
|
29
|
+
export const VERSION = '1.0.0';
|