brave-real-browser 2.0.7 → 2.0.9

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/lib/cjs/index.js CHANGED
@@ -29,6 +29,11 @@ async function connect({
29
29
  } else {
30
30
  // Use DEFAULT_FLAGS from brave-real-launcher
31
31
  const flags = [...DEFAULT_FLAGS];
32
+ // Add AutomationControlled to "disable-features" flag to improve ReCaptcha V3 score
33
+ const indexDisableFeatures = flags.findIndex((flag) => flag.startsWith('--disable-features'));
34
+ if (indexDisableFeatures !== -1) {
35
+ flags[indexDisableFeatures] = `${flags[indexDisableFeatures]},AutomationControlled`;
36
+ }
32
37
  braveFlags = [
33
38
  ...flags,
34
39
  ...args,
@@ -2,35 +2,10 @@ const { createCursor } = require('ghost-cursor');
2
2
  const { checkTurnstile } = require('./turnstile.js');
3
3
  const kill = require('tree-kill');
4
4
 
5
- function getRandomInt(min, max) {
6
- min = Math.ceil(min);
7
- max = Math.floor(max);
8
- return Math.floor(Math.random() * (max - min + 1)) + min;
9
- }
10
-
11
5
  async function pageController({ browser, page, proxy, turnstile, xvfbsession, pid, plugins, killProcess = false, brave }) {
12
6
 
13
7
  let solveStatus = turnstile
14
8
 
15
- // 🛡️ Import stealth scripts properly (Dynamic Import for compatibility)
16
- let injectStealth = () => { };
17
- try {
18
- const stealthModule = require('brave-real-puppeteer-core/scripts/stealth-injector.js');
19
- // Combine essential stealth scripts
20
- const ultrafast = stealthModule.injectUltraFastPerformance();
21
- const navStealth = stealthModule.injectNavigatorStealth();
22
- injectStealth = async (page) => {
23
- await page.evaluateOnNewDocument(ultrafast);
24
- await page.evaluateOnNewDocument(navStealth);
25
- };
26
- } catch (e) {
27
- // Fallback if module not found (shouldn't happen in monorepo)
28
- console.error('Stealth module not found, using minimal fallback', e);
29
- }
30
-
31
- // Apply stealth BEFORE any other page actions
32
- if (page) await injectStealth(page);
33
-
34
9
  page.on('close', () => {
35
10
  solveStatus = false
36
11
  });
@@ -63,233 +38,6 @@ async function pageController({ browser, page, proxy, turnstile, xvfbsession, pi
63
38
  }
64
39
  }
65
40
 
66
- // 🛡️ COMPREHENSIVE STEALTH INJECTION - For Brotector/Datadome bypass
67
- await page.evaluateOnNewDocument(() => {
68
- // ============ NAVIGATOR WEBDRIVER ELIMINATION ============
69
- Object.defineProperty(navigator, 'webdriver', {
70
- get: () => undefined,
71
- configurable: true
72
- });
73
-
74
- // Delete webdriver from Object.getOwnPropertyDescriptor
75
- const originalGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
76
- Object.getOwnPropertyDescriptor = function (obj, prop) {
77
- if (obj === navigator && prop === 'webdriver') return undefined;
78
- return originalGetOwnPropertyDescriptor.apply(this, arguments);
79
- };
80
-
81
- // ============ PLUGINS & MIME TYPES ============
82
- Object.defineProperty(navigator, 'plugins', {
83
- get: () => {
84
- const plugins = [
85
- { name: 'Chrome PDF Plugin', filename: 'internal-pdf-viewer', description: 'Portable Document Format' },
86
- { name: 'Chrome PDF Viewer', filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai', description: '' },
87
- { name: 'Native Client', filename: 'internal-nacl-plugin', description: '' }
88
- ];
89
- plugins.length = 3;
90
- plugins.item = i => plugins[i];
91
- plugins.namedItem = n => plugins.find(p => p.name === n);
92
- plugins.refresh = () => { };
93
- return plugins;
94
- },
95
- configurable: true
96
- });
97
-
98
- Object.defineProperty(navigator, 'mimeTypes', {
99
- get: () => {
100
- const mimeTypes = [
101
- { type: 'application/pdf', suffixes: 'pdf', description: 'Portable Document Format' },
102
- { type: 'text/pdf', suffixes: 'pdf', description: '' }
103
- ];
104
- mimeTypes.length = 2;
105
- mimeTypes.item = i => mimeTypes[i];
106
- mimeTypes.namedItem = n => mimeTypes.find(m => m.type === n);
107
- return mimeTypes;
108
- },
109
- configurable: true
110
- });
111
-
112
- // ============ LANGUAGES ============
113
- Object.defineProperty(navigator, 'languages', {
114
- get: () => ['en-US', 'en'],
115
- configurable: true
116
- });
117
-
118
- // ============ CHROME RUNTIME REMOVAL ============
119
- if (window.chrome) {
120
- const originalChrome = window.chrome;
121
- window.chrome = new Proxy(originalChrome, {
122
- get(target, prop) {
123
- if (prop === 'runtime') return undefined;
124
- if (prop === 'csi') return undefined;
125
- if (prop === 'loadTimes') return undefined;
126
- return Reflect.get(target, prop);
127
- }
128
- });
129
- }
130
-
131
- // ============ PERMISSIONS API ============
132
- if (navigator.permissions) {
133
- const originalQuery = navigator.permissions.query.bind(navigator.permissions);
134
- navigator.permissions.query = (params) => {
135
- if (params.name === 'notifications') {
136
- return Promise.resolve({ state: 'prompt', onchange: null });
137
- }
138
- return originalQuery(params);
139
- };
140
- }
141
-
142
- // ============ AUTOMATION SIGNATURES REMOVAL ============
143
- const automationProps = [
144
- '__puppeteer__', 'puppeteer', '__playwright__', 'playwright',
145
- '__selenium_unwrapped', '__selenium_evaluate', '__webdriver_evaluate',
146
- '__driver_evaluate', '_phantom', '__nightmare', 'callPhantom',
147
- '__webdriver_script_fn', '__webdriver_script_func', 'cdc_adoQpoasnfa76pfcZLmcfl_Array',
148
- 'cdc_adoQpoasnfa76pfcZLmcfl_Promise', 'cdc_adoQpoasnfa76pfcZLmcfl_Symbol'
149
- ];
150
- automationProps.forEach(prop => {
151
- try { delete window[prop]; } catch (e) { }
152
- });
153
-
154
- // ============ ERROR STACK SANITIZATION ============
155
- const originalError = Error;
156
- window.Error = function (...args) {
157
- const error = new originalError(...args);
158
- if (error.stack) {
159
- error.stack = error.stack.split('\\n')
160
- .filter(line => !line.includes('puppeteer') && !line.includes('playwright') && !line.includes('UtilityScript'))
161
- .join('\\n');
162
- }
163
- return error;
164
- };
165
- window.Error.prototype = originalError.prototype;
166
-
167
- // ============ HARDWARE SPOOFING ============
168
- Object.defineProperty(navigator, 'hardwareConcurrency', { get: () => 4 });
169
- Object.defineProperty(navigator, 'deviceMemory', { get: () => 8 });
170
-
171
- // ============ CONNECTION API ============
172
- if (navigator.connection) {
173
- Object.defineProperty(navigator.connection, 'rtt', { get: () => 50 });
174
- Object.defineProperty(navigator.connection, 'downlink', { get: () => 10 });
175
- Object.defineProperty(navigator.connection, 'effectiveType', { get: () => '4g' });
176
- }
177
- });
178
-
179
- // MouseEvent screenX/screenY fix
180
- // 🛡️ COMPREHENSIVE STEALTH INJECTION - For Brotector/Datadome bypass
181
- await page.evaluateOnNewDocument(() => {
182
- // ============ NAVIGATOR WEBDRIVER ELIMINATION ============
183
- Object.defineProperty(navigator, 'webdriver', {
184
- get: () => undefined,
185
- configurable: true
186
- });
187
-
188
- // Delete webdriver from Object.getOwnPropertyDescriptor
189
- const originalGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
190
- Object.getOwnPropertyDescriptor = function (obj, prop) {
191
- if (obj === navigator && prop === 'webdriver') return undefined;
192
- return originalGetOwnPropertyDescriptor.apply(this, arguments);
193
- };
194
-
195
- // ============ PLUGINS & MIME TYPES ============
196
- Object.defineProperty(navigator, 'plugins', {
197
- get: () => {
198
- const plugins = [
199
- { name: 'Chrome PDF Plugin', filename: 'internal-pdf-viewer', description: 'Portable Document Format' },
200
- { name: 'Chrome PDF Viewer', filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai', description: '' },
201
- { name: 'Native Client', filename: 'internal-nacl-plugin', description: '' }
202
- ];
203
- plugins.length = 3;
204
- plugins.item = i => plugins[i];
205
- plugins.namedItem = n => plugins.find(p => p.name === n);
206
- plugins.refresh = () => { };
207
- return plugins;
208
- },
209
- configurable: true
210
- });
211
-
212
- Object.defineProperty(navigator, 'mimeTypes', {
213
- get: () => {
214
- const mimeTypes = [
215
- { type: 'application/pdf', suffixes: 'pdf', description: 'Portable Document Format' },
216
- { type: 'text/pdf', suffixes: 'pdf', description: '' }
217
- ];
218
- mimeTypes.length = 2;
219
- mimeTypes.item = i => mimeTypes[i];
220
- mimeTypes.namedItem = n => mimeTypes.find(m => m.type === n);
221
- return mimeTypes;
222
- },
223
- configurable: true
224
- });
225
-
226
- // ============ LANGUAGES ============
227
- Object.defineProperty(navigator, 'languages', {
228
- get: () => ['en-US', 'en'],
229
- configurable: true
230
- });
231
-
232
- // ============ CHROME RUNTIME REMOVAL ============
233
- if (window.chrome) {
234
- const originalChrome = window.chrome;
235
- window.chrome = new Proxy(originalChrome, {
236
- get(target, prop) {
237
- if (prop === 'runtime') return undefined;
238
- if (prop === 'csi') return undefined;
239
- if (prop === 'loadTimes') return undefined;
240
- return Reflect.get(target, prop);
241
- }
242
- });
243
- }
244
-
245
- // ============ PERMISSIONS API ============
246
- if (navigator.permissions) {
247
- const originalQuery = navigator.permissions.query.bind(navigator.permissions);
248
- navigator.permissions.query = (params) => {
249
- if (params.name === 'notifications') {
250
- return Promise.resolve({ state: 'prompt', onchange: null });
251
- }
252
- return originalQuery(params);
253
- };
254
- }
255
-
256
- // ============ AUTOMATION SIGNATURES REMOVAL ============
257
- const automationProps = [
258
- '__puppeteer__', 'puppeteer', '__playwright__', 'playwright',
259
- '__selenium_unwrapped', '__selenium_evaluate', '__webdriver_evaluate',
260
- '__driver_evaluate', '_phantom', '__nightmare', 'callPhantom',
261
- '__webdriver_script_fn', '__webdriver_script_func', 'cdc_adoQpoasnfa76pfcZLmcfl_Array',
262
- 'cdc_adoQpoasnfa76pfcZLmcfl_Promise', 'cdc_adoQpoasnfa76pfcZLmcfl_Symbol'
263
- ];
264
- automationProps.forEach(prop => {
265
- try { delete window[prop]; } catch (e) { }
266
- });
267
-
268
- // ============ ERROR STACK SANITIZATION ============
269
- const originalError = Error;
270
- window.Error = function (...args) {
271
- const error = new originalError(...args);
272
- if (error.stack) {
273
- error.stack = error.stack.split('\\n')
274
- .filter(line => !line.includes('puppeteer') && !line.includes('playwright') && !line.includes('UtilityScript'))
275
- .join('\\n');
276
- }
277
- return error;
278
- };
279
- window.Error.prototype = originalError.prototype;
280
-
281
- // ============ HARDWARE SPOOFING ============
282
- Object.defineProperty(navigator, 'hardwareConcurrency', { get: () => 4 });
283
- Object.defineProperty(navigator, 'deviceMemory', { get: () => 8 });
284
-
285
- // ============ CONNECTION API ============
286
- if (navigator.connection) {
287
- Object.defineProperty(navigator.connection, 'rtt', { get: () => 50 });
288
- Object.defineProperty(navigator.connection, 'downlink', { get: () => 10 });
289
- Object.defineProperty(navigator.connection, 'effectiveType', { get: () => '4g' });
290
- }
291
- });
292
-
293
41
  await page.evaluateOnNewDocument(() => {
294
42
  Object.defineProperty(MouseEvent.prototype, 'screenX', {
295
43
  get: function () {
package/lib/esm/index.mjs CHANGED
@@ -28,6 +28,11 @@ export async function connect({
28
28
  } else {
29
29
  // Use DEFAULT_FLAGS from brave-real-launcher
30
30
  const flags = [...DEFAULT_FLAGS];
31
+ // Add AutomationControlled to "disable-features" flag to improve ReCaptcha V3 score
32
+ const indexDisableFeatures = flags.findIndex((flag) => flag.startsWith('--disable-features'));
33
+ if (indexDisableFeatures !== -1) {
34
+ flags[indexDisableFeatures] = `${flags[indexDisableFeatures]},AutomationControlled`;
35
+ }
31
36
  braveFlags = [
32
37
  ...flags,
33
38
  ...args,
@@ -2,32 +2,10 @@ import { createCursor } from 'ghost-cursor';
2
2
  import { checkTurnstile } from './turnstile.mjs';
3
3
  import kill from 'tree-kill';
4
4
 
5
- function getRandomInt(min, max) {
6
- min = Math.ceil(min);
7
- max = Math.floor(max);
8
- return Math.floor(Math.random() * (max - min + 1)) + min;
9
- }
10
-
11
5
  export async function pageController({ browser, page, proxy, turnstile, xvfbsession, pid, plugins, killProcess = false, brave }) {
12
6
 
13
7
  let solveStatus = turnstile
14
8
 
15
- // 🛡️ Import stealth scripts properly (Dynamic Import for compatibility)
16
- let injectStealth = () => { };
17
- try {
18
- // Use dynamic import for ESM
19
- const { injectUltraFastPerformance, injectNavigatorStealth } = await import('brave-real-puppeteer-core/scripts/stealth-injector.js');
20
- injectStealth = async (page) => {
21
- await page.evaluateOnNewDocument(injectUltraFastPerformance());
22
- await page.evaluateOnNewDocument(injectNavigatorStealth());
23
- };
24
- } catch (e) {
25
- console.error('Stealth module not found, using minimal fallback', e);
26
- }
27
-
28
- // Apply stealth BEFORE any other page actions
29
- if (page) await injectStealth(page);
30
-
31
9
  page.on('close', () => {
32
10
  solveStatus = false
33
11
  });
@@ -60,233 +38,6 @@ export async function pageController({ browser, page, proxy, turnstile, xvfbsess
60
38
  }
61
39
  }
62
40
 
63
- // 🛡️ COMPREHENSIVE STEALTH INJECTION - For Brotector/Datadome bypass
64
- await page.evaluateOnNewDocument(() => {
65
- // ============ NAVIGATOR WEBDRIVER ELIMINATION ============
66
- Object.defineProperty(navigator, 'webdriver', {
67
- get: () => undefined,
68
- configurable: true
69
- });
70
-
71
- // Delete webdriver from Object.getOwnPropertyDescriptor
72
- const originalGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
73
- Object.getOwnPropertyDescriptor = function (obj, prop) {
74
- if (obj === navigator && prop === 'webdriver') return undefined;
75
- return originalGetOwnPropertyDescriptor.apply(this, arguments);
76
- };
77
-
78
- // ============ PLUGINS & MIME TYPES ============
79
- Object.defineProperty(navigator, 'plugins', {
80
- get: () => {
81
- const plugins = [
82
- { name: 'Chrome PDF Plugin', filename: 'internal-pdf-viewer', description: 'Portable Document Format' },
83
- { name: 'Chrome PDF Viewer', filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai', description: '' },
84
- { name: 'Native Client', filename: 'internal-nacl-plugin', description: '' }
85
- ];
86
- plugins.length = 3;
87
- plugins.item = i => plugins[i];
88
- plugins.namedItem = n => plugins.find(p => p.name === n);
89
- plugins.refresh = () => { };
90
- return plugins;
91
- },
92
- configurable: true
93
- });
94
-
95
- Object.defineProperty(navigator, 'mimeTypes', {
96
- get: () => {
97
- const mimeTypes = [
98
- { type: 'application/pdf', suffixes: 'pdf', description: 'Portable Document Format' },
99
- { type: 'text/pdf', suffixes: 'pdf', description: '' }
100
- ];
101
- mimeTypes.length = 2;
102
- mimeTypes.item = i => mimeTypes[i];
103
- mimeTypes.namedItem = n => mimeTypes.find(m => m.type === n);
104
- return mimeTypes;
105
- },
106
- configurable: true
107
- });
108
-
109
- // ============ LANGUAGES ============
110
- Object.defineProperty(navigator, 'languages', {
111
- get: () => ['en-US', 'en'],
112
- configurable: true
113
- });
114
-
115
- // ============ CHROME RUNTIME REMOVAL ============
116
- if (window.chrome) {
117
- const originalChrome = window.chrome;
118
- window.chrome = new Proxy(originalChrome, {
119
- get(target, prop) {
120
- if (prop === 'runtime') return undefined;
121
- if (prop === 'csi') return undefined;
122
- if (prop === 'loadTimes') return undefined;
123
- return Reflect.get(target, prop);
124
- }
125
- });
126
- }
127
-
128
- // ============ PERMISSIONS API ============
129
- if (navigator.permissions) {
130
- const originalQuery = navigator.permissions.query.bind(navigator.permissions);
131
- navigator.permissions.query = (params) => {
132
- if (params.name === 'notifications') {
133
- return Promise.resolve({ state: 'prompt', onchange: null });
134
- }
135
- return originalQuery(params);
136
- };
137
- }
138
-
139
- // ============ AUTOMATION SIGNATURES REMOVAL ============
140
- const automationProps = [
141
- '__puppeteer__', 'puppeteer', '__playwright__', 'playwright',
142
- '__selenium_unwrapped', '__selenium_evaluate', '__webdriver_evaluate',
143
- '__driver_evaluate', '_phantom', '__nightmare', 'callPhantom',
144
- '__webdriver_script_fn', '__webdriver_script_func', 'cdc_adoQpoasnfa76pfcZLmcfl_Array',
145
- 'cdc_adoQpoasnfa76pfcZLmcfl_Promise', 'cdc_adoQpoasnfa76pfcZLmcfl_Symbol'
146
- ];
147
- automationProps.forEach(prop => {
148
- try { delete window[prop]; } catch (e) { }
149
- });
150
-
151
- // ============ ERROR STACK SANITIZATION ============
152
- const originalError = Error;
153
- window.Error = function (...args) {
154
- const error = new originalError(...args);
155
- if (error.stack) {
156
- error.stack = error.stack.split('\\n')
157
- .filter(line => !line.includes('puppeteer') && !line.includes('playwright') && !line.includes('UtilityScript'))
158
- .join('\\n');
159
- }
160
- return error;
161
- };
162
- window.Error.prototype = originalError.prototype;
163
-
164
- // ============ HARDWARE SPOOFING ============
165
- Object.defineProperty(navigator, 'hardwareConcurrency', { get: () => 4 });
166
- Object.defineProperty(navigator, 'deviceMemory', { get: () => 8 });
167
-
168
- // ============ CONNECTION API ============
169
- if (navigator.connection) {
170
- Object.defineProperty(navigator.connection, 'rtt', { get: () => 50 });
171
- Object.defineProperty(navigator.connection, 'downlink', { get: () => 10 });
172
- Object.defineProperty(navigator.connection, 'effectiveType', { get: () => '4g' });
173
- }
174
- });
175
-
176
- // MouseEvent screenX/screenY fix
177
- // 🛡️ COMPREHENSIVE STEALTH INJECTION - For Brotector/Datadome bypass
178
- await page.evaluateOnNewDocument(() => {
179
- // ============ NAVIGATOR WEBDRIVER ELIMINATION ============
180
- Object.defineProperty(navigator, 'webdriver', {
181
- get: () => undefined,
182
- configurable: true
183
- });
184
-
185
- // Delete webdriver from Object.getOwnPropertyDescriptor
186
- const originalGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
187
- Object.getOwnPropertyDescriptor = function (obj, prop) {
188
- if (obj === navigator && prop === 'webdriver') return undefined;
189
- return originalGetOwnPropertyDescriptor.apply(this, arguments);
190
- };
191
-
192
- // ============ PLUGINS & MIME TYPES ============
193
- Object.defineProperty(navigator, 'plugins', {
194
- get: () => {
195
- const plugins = [
196
- { name: 'Chrome PDF Plugin', filename: 'internal-pdf-viewer', description: 'Portable Document Format' },
197
- { name: 'Chrome PDF Viewer', filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai', description: '' },
198
- { name: 'Native Client', filename: 'internal-nacl-plugin', description: '' }
199
- ];
200
- plugins.length = 3;
201
- plugins.item = i => plugins[i];
202
- plugins.namedItem = n => plugins.find(p => p.name === n);
203
- plugins.refresh = () => { };
204
- return plugins;
205
- },
206
- configurable: true
207
- });
208
-
209
- Object.defineProperty(navigator, 'mimeTypes', {
210
- get: () => {
211
- const mimeTypes = [
212
- { type: 'application/pdf', suffixes: 'pdf', description: 'Portable Document Format' },
213
- { type: 'text/pdf', suffixes: 'pdf', description: '' }
214
- ];
215
- mimeTypes.length = 2;
216
- mimeTypes.item = i => mimeTypes[i];
217
- mimeTypes.namedItem = n => mimeTypes.find(m => m.type === n);
218
- return mimeTypes;
219
- },
220
- configurable: true
221
- });
222
-
223
- // ============ LANGUAGES ============
224
- Object.defineProperty(navigator, 'languages', {
225
- get: () => ['en-US', 'en'],
226
- configurable: true
227
- });
228
-
229
- // ============ CHROME RUNTIME REMOVAL ============
230
- if (window.chrome) {
231
- const originalChrome = window.chrome;
232
- window.chrome = new Proxy(originalChrome, {
233
- get(target, prop) {
234
- if (prop === 'runtime') return undefined;
235
- if (prop === 'csi') return undefined;
236
- if (prop === 'loadTimes') return undefined;
237
- return Reflect.get(target, prop);
238
- }
239
- });
240
- }
241
-
242
- // ============ PERMISSIONS API ============
243
- if (navigator.permissions) {
244
- const originalQuery = navigator.permissions.query.bind(navigator.permissions);
245
- navigator.permissions.query = (params) => {
246
- if (params.name === 'notifications') {
247
- return Promise.resolve({ state: 'prompt', onchange: null });
248
- }
249
- return originalQuery(params);
250
- };
251
- }
252
-
253
- // ============ AUTOMATION SIGNATURES REMOVAL ============
254
- const automationProps = [
255
- '__puppeteer__', 'puppeteer', '__playwright__', 'playwright',
256
- '__selenium_unwrapped', '__selenium_evaluate', '__webdriver_evaluate',
257
- '__driver_evaluate', '_phantom', '__nightmare', 'callPhantom',
258
- '__webdriver_script_fn', '__webdriver_script_func', 'cdc_adoQpoasnfa76pfcZLmcfl_Array',
259
- 'cdc_adoQpoasnfa76pfcZLmcfl_Promise', 'cdc_adoQpoasnfa76pfcZLmcfl_Symbol'
260
- ];
261
- automationProps.forEach(prop => {
262
- try { delete window[prop]; } catch (e) { }
263
- });
264
-
265
- // ============ ERROR STACK SANITIZATION ============
266
- const originalError = Error;
267
- window.Error = function (...args) {
268
- const error = new originalError(...args);
269
- if (error.stack) {
270
- error.stack = error.stack.split('\\n')
271
- .filter(line => !line.includes('puppeteer') && !line.includes('playwright') && !line.includes('UtilityScript'))
272
- .join('\\n');
273
- }
274
- return error;
275
- };
276
- window.Error.prototype = originalError.prototype;
277
-
278
- // ============ HARDWARE SPOOFING ============
279
- Object.defineProperty(navigator, 'hardwareConcurrency', { get: () => 4 });
280
- Object.defineProperty(navigator, 'deviceMemory', { get: () => 8 });
281
-
282
- // ============ CONNECTION API ============
283
- if (navigator.connection) {
284
- Object.defineProperty(navigator.connection, 'rtt', { get: () => 50 });
285
- Object.defineProperty(navigator.connection, 'downlink', { get: () => 10 });
286
- Object.defineProperty(navigator.connection, 'effectiveType', { get: () => '4g' });
287
- }
288
- });
289
-
290
41
  await page.evaluateOnNewDocument(() => {
291
42
  Object.defineProperty(MouseEvent.prototype, 'screenX', {
292
43
  get: function () {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brave-real-browser",
3
- "version": "2.0.7",
3
+ "version": "2.0.9",
4
4
  "description": "This package is designed to bypass puppeteer's bot-detecting captchas such as Cloudflare. It acts like a real browser and can be managed with puppeteer.",
5
5
  "main": "lib/cjs/index.js",
6
6
  "module": "lib/esm/index.mjs",
@@ -37,9 +37,11 @@
37
37
  "license": "ISC",
38
38
  "dependencies": {
39
39
  "brave-real-launcher": "^1.2.48",
40
- "brave-real-puppeteer-core": "^24.34.0-patch.11",
40
+ "brave-real-puppeteer-core": "^24.34.0-patch.13",
41
41
  "ghost-cursor": "^1.4.1",
42
42
  "puppeteer-extra": "^3.3.6",
43
+ "puppeteer-extra-plugin-adblocker": "^2.13.6",
44
+ "puppeteer-extra-plugin-stealth": "^2.11.2",
43
45
  "tree-kill": "^1.2.2",
44
46
  "xvfb": "^0.4.0"
45
47
  },
package/test/cjs/test.js CHANGED
@@ -2,11 +2,18 @@ const test = require('node:test');
2
2
  const assert = require('node:assert');
3
3
  const { connect } = require('../../lib/cjs/index.js');
4
4
 
5
+ // Import Stealth and Adblocker Plugins
6
+ const StealthPlugin = require('puppeteer-extra-plugin-stealth');
7
+ const AdblockerPlugin = require('puppeteer-extra-plugin-adblocker');
8
+
5
9
  const realBrowserOption = {
6
10
  turnstile: true,
7
11
  headless: false,
8
12
  customConfig: {},
9
- plugins: []
13
+ plugins: [
14
+ StealthPlugin(),
15
+ AdblockerPlugin({ blockTrackers: true })
16
+ ]
10
17
  }
11
18
 
12
19
  // Shared browser instance for all tests
@@ -38,11 +45,14 @@ test('DrissionPage Detector', async () => {
38
45
  assert.strictEqual(result, true, "DrissionPage Detector test failed!")
39
46
  })
40
47
 
41
- test('Brotector, a webdriver detector', async () => {
42
- await page.goto("https://kaliiiiiiiiii.github.io/brotector/");
48
+ test('Sannysoft WebDriver Detector', async () => {
49
+ await page.goto("https://bot.sannysoft.com/");
43
50
  await new Promise(r => setTimeout(r, 3000));
44
- let result = await page.evaluate(() => { return document.querySelector('#table-keys').getAttribute('bgcolor') })
45
- assert.strictEqual(result === "darkgreen", true, "Brotector, a webdriver detector test failed!")
51
+ let result = await page.evaluate(() => {
52
+ const webdriverEl = document.getElementById('webdriver-result');
53
+ return webdriverEl && webdriverEl.classList.contains('passed');
54
+ });
55
+ assert.strictEqual(result, true, "Sannysoft WebDriver Detector test failed! Browser detected as bot.")
46
56
  })
47
57
 
48
58
  test('Cloudflare WAF', async () => {
@@ -117,8 +127,24 @@ test('Datadome Bot Detector', async (t) => {
117
127
  // If this test fails, please first check if you can access https://antcpt.com/score_detector/
118
128
  test('Recaptcha V3 Score (hard)', async () => {
119
129
  await page.goto("https://antcpt.com/score_detector/");
130
+
131
+ // Human-like warm-up interactions before clicking
132
+ // 1. Random mouse movements using realCursor
133
+ await page.realCursor.move('body', { paddingPercentage: 20 });
134
+ await new Promise(r => setTimeout(r, 500 + Math.random() * 500));
135
+
136
+ // 2. Scroll down a bit to simulate reading
137
+ await page.mouse.wheel({ deltaY: 100 + Math.random() * 100 });
138
+ await new Promise(r => setTimeout(r, 800 + Math.random() * 400));
139
+
140
+ // 3. Move mouse towards button area naturally
141
+ await page.realCursor.move('button', { paddingPercentage: 10 });
142
+ await new Promise(r => setTimeout(r, 300 + Math.random() * 300));
143
+
144
+ // 4. Now click the button
120
145
  await page.realClick("button")
121
- await new Promise(r => setTimeout(r, 5000));
146
+ await new Promise(r => setTimeout(r, 6000));
147
+
122
148
  const score = await page.evaluate(() => {
123
149
  return document.querySelector('big').textContent.replace(/[^0-9.]/g, '')
124
150
  })
package/test/esm/test.js CHANGED
@@ -2,11 +2,18 @@ import test from 'node:test';
2
2
  import assert from 'node:assert';
3
3
  import { connect } from '../../lib/esm/index.mjs';
4
4
 
5
+ // Import Stealth and Adblocker Plugins
6
+ import StealthPlugin from 'puppeteer-extra-plugin-stealth';
7
+ import AdblockerPlugin from 'puppeteer-extra-plugin-adblocker';
8
+
5
9
  const realBrowserOption = {
6
10
  turnstile: true,
7
11
  headless: false,
8
12
  customConfig: {},
9
- plugins: []
13
+ plugins: [
14
+ StealthPlugin(),
15
+ AdblockerPlugin({ blockTrackers: true })
16
+ ]
10
17
  }
11
18
 
12
19
  // Shared browser instance for all tests
@@ -38,11 +45,14 @@ test('DrissionPage Detector', async () => {
38
45
  assert.strictEqual(result, true, "DrissionPage Detector test failed!")
39
46
  })
40
47
 
41
- test('Brotector, a webdriver detector', async () => {
42
- await page.goto("https://kaliiiiiiiiii.github.io/brotector/");
48
+ test('Sannysoft WebDriver Detector', async () => {
49
+ await page.goto("https://bot.sannysoft.com/");
43
50
  await new Promise(r => setTimeout(r, 3000));
44
- let result = await page.evaluate(() => { return document.querySelector('#table-keys').getAttribute('bgcolor') })
45
- assert.strictEqual(result === "darkgreen", true, "Brotector, a webdriver detector test failed!")
51
+ let result = await page.evaluate(() => {
52
+ const webdriverEl = document.getElementById('webdriver-result');
53
+ return webdriverEl && webdriverEl.classList.contains('passed');
54
+ });
55
+ assert.strictEqual(result, true, "Sannysoft WebDriver Detector test failed! Browser detected as bot.")
46
56
  })
47
57
 
48
58
  test('Cloudflare WAF', async () => {
@@ -118,8 +128,24 @@ test('Datadome Bot Detector', async (t) => {
118
128
  // If this test fails, please first check if you can access https://antcpt.com/score_detector/
119
129
  test('Recaptcha V3 Score (hard)', async () => {
120
130
  await page.goto("https://antcpt.com/score_detector/");
131
+
132
+ // Human-like warm-up interactions before clicking
133
+ // 1. Random mouse movements using realCursor
134
+ await page.realCursor.move('body', { paddingPercentage: 20 });
135
+ await new Promise(r => setTimeout(r, 500 + Math.random() * 500));
136
+
137
+ // 2. Scroll down a bit to simulate reading
138
+ await page.mouse.wheel({ deltaY: 100 + Math.random() * 100 });
139
+ await new Promise(r => setTimeout(r, 800 + Math.random() * 400));
140
+
141
+ // 3. Move mouse towards button area naturally
142
+ await page.realCursor.move('button', { paddingPercentage: 10 });
143
+ await new Promise(r => setTimeout(r, 300 + Math.random() * 300));
144
+
145
+ // 4. Now click the button
121
146
  await page.realClick("button")
122
- await new Promise(r => setTimeout(r, 5000));
147
+ await new Promise(r => setTimeout(r, 6000));
148
+
123
149
  const score = await page.evaluate(() => {
124
150
  return document.querySelector('big').textContent.replace(/[^0-9.]/g, '')
125
151
  })
package/data/sdo.gif DELETED
Binary file