capiumbrowser 0.1.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 +38 -0
- package/README.md +174 -0
- package/bin/capiumbrowser.js +305 -0
- package/index.d.ts +131 -0
- package/index.js +90 -0
- package/lib/channels.json +13 -0
- package/lib/config.js +286 -0
- package/lib/download.js +364 -0
- package/lib/errors.js +114 -0
- package/lib/human.js +142 -0
- package/lib/launch-common.js +82 -0
- package/lib/license.js +217 -0
- package/lib/proxy.js +218 -0
- package/lib/version.js +100 -0
- package/package.json +67 -0
- package/playwright.js +260 -0
- package/puppeteer.js +233 -0
package/index.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* capiumbrowser -- a drop-in stealth browser SDK on top of Playwright or Puppeteer.
|
|
3
|
+
*
|
|
4
|
+
* # With Playwright
|
|
5
|
+
* npm install capiumbrowser playwright-core
|
|
6
|
+
*
|
|
7
|
+
* # With Puppeteer
|
|
8
|
+
* npm install capiumbrowser puppeteer-core
|
|
9
|
+
*
|
|
10
|
+
* const { launchContext } = require('capiumbrowser'); // auto-detects the driver
|
|
11
|
+
* const { launch } = require('capiumbrowser/playwright'); // or pick one explicitly
|
|
12
|
+
* const { launch } = require('capiumbrowser/puppeteer');
|
|
13
|
+
*
|
|
14
|
+
* The driver (playwright-core / puppeteer-core) is a normal peer dependency, so you update
|
|
15
|
+
* the engine independently: `npm install -U playwright-core`.
|
|
16
|
+
*
|
|
17
|
+
* The browser itself is the Capium stealth Chromium (driven via its `capium` wrapper). No
|
|
18
|
+
* stealth plugin needed -- the binary's own 001/009 patches keep developer_tools/webdriver
|
|
19
|
+
* clean.
|
|
20
|
+
*
|
|
21
|
+
* Docs: https://docs.capiumbrowser.com · Site: https://capiumbrowser.com
|
|
22
|
+
*/
|
|
23
|
+
'use strict';
|
|
24
|
+
|
|
25
|
+
const config = require('./lib/config');
|
|
26
|
+
const download = require('./lib/download');
|
|
27
|
+
const errors = require('./lib/errors');
|
|
28
|
+
const human = require('./lib/human');
|
|
29
|
+
const license = require('./lib/license');
|
|
30
|
+
const proxy = require('./lib/proxy');
|
|
31
|
+
const version = require('./lib/version');
|
|
32
|
+
|
|
33
|
+
function hasModule(name) {
|
|
34
|
+
try {
|
|
35
|
+
require.resolve(name);
|
|
36
|
+
return true;
|
|
37
|
+
} catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Pick the installed driver front-end: Playwright if playwright-core/playwright is present,
|
|
44
|
+
* else Puppeteer if puppeteer-core/puppeteer is. Throws a clear error when neither is.
|
|
45
|
+
*/
|
|
46
|
+
function detectDriver() {
|
|
47
|
+
if (hasModule('playwright-core') || hasModule('playwright')) {
|
|
48
|
+
return require('./playwright');
|
|
49
|
+
}
|
|
50
|
+
if (hasModule('puppeteer-core') || hasModule('puppeteer')) {
|
|
51
|
+
return require('./puppeteer');
|
|
52
|
+
}
|
|
53
|
+
throw new errors.CapiumError(
|
|
54
|
+
'no driver installed. Run one of:\n' +
|
|
55
|
+
' npm install capiumbrowser playwright-core\n' +
|
|
56
|
+
' npm install capiumbrowser puppeteer-core');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Root-level launchers delegate to whichever driver is installed (Playwright preferred when
|
|
60
|
+
// both are). Use require('capiumbrowser/playwright') / require('capiumbrowser/puppeteer')
|
|
61
|
+
// to pick one explicitly.
|
|
62
|
+
const launch = (opts) => detectDriver().launch(opts);
|
|
63
|
+
const launchContext = (opts) => detectDriver().launchContext(opts);
|
|
64
|
+
const launchPersistentContext = (userDataDir, opts) =>
|
|
65
|
+
detectDriver().launchPersistentContext(userDataDir, opts);
|
|
66
|
+
|
|
67
|
+
module.exports = {
|
|
68
|
+
VERSION: version.SDK_VERSION,
|
|
69
|
+
CAPIUM_BINARY_VERSION: version.CAPIUM_BINARY_VERSION,
|
|
70
|
+
CAPIUM_BINARY_VERSIONS: version.CAPIUM_BINARY_VERSIONS,
|
|
71
|
+
|
|
72
|
+
launch,
|
|
73
|
+
launchContext,
|
|
74
|
+
launchPersistentContext,
|
|
75
|
+
detectDriver,
|
|
76
|
+
|
|
77
|
+
config,
|
|
78
|
+
proxy,
|
|
79
|
+
human,
|
|
80
|
+
download,
|
|
81
|
+
license,
|
|
82
|
+
status: license.status,
|
|
83
|
+
|
|
84
|
+
CapiumError: errors.CapiumError,
|
|
85
|
+
CapiumLicenseError: errors.CapiumLicenseError,
|
|
86
|
+
CapiumConfigError: errors.CapiumConfigError,
|
|
87
|
+
CapiumSeatLimitError: errors.CapiumSeatLimitError,
|
|
88
|
+
CapiumExpiredError: errors.CapiumExpiredError,
|
|
89
|
+
CapiumServerDownError: errors.CapiumServerDownError,
|
|
90
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "SINGLE SOURCE OF TRUTH for the current stable Capium browser build per OS/arch. Each OS reaches stable independently -- edit one entry without touching the others. This file ships inside the wheel (the SDK reads it to pick each host's stable build) AND is published to the download server by the release pipeline (so the server/website can resolve 'latest stable' without a new SDK release). `version` is the Chromium base of the immutable artifact folder; `revision` is the cap<N> re-spin counter. `published: false` means the tag is declared but no artifact exists yet -- the SDK raises a clear 'not published yet' error instead of a raw 404.",
|
|
3
|
+
"schema": 1,
|
|
4
|
+
"updated": "2026-09-01",
|
|
5
|
+
"channels": {
|
|
6
|
+
"stable": {
|
|
7
|
+
"windows-x64": { "version": "152.0.7977.65", "revision": 1, "sha256": "c3cf933325907b36a157ed74db80c1acb1d3aca0faa93c3bb224fe735cafc4a5", "published": true },
|
|
8
|
+
"macos-arm64": { "version": "152.0.7977.65", "revision": 1, "sha256": "e328158c5a272594e01d2135eb418f649f46ee5b0a80fbc79f6923dcc809be01", "published": true },
|
|
9
|
+
"linux-x64": { "version": "152.0.7977.64", "revision": 1, "sha256": "aeb36b509d26e40969d64502f0a2a3f0ad73c7cdd8c42c5fab8a54e953bc3b3a", "published": true },
|
|
10
|
+
"linux-arm64": { "version": "152.0.7977.64", "revision": 1, "sha256": null, "published": false }
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
package/lib/config.js
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* config -- default stealth arguments and binary discovery.
|
|
3
|
+
*
|
|
4
|
+
* The capium binary is driven through its `capium` bash wrapper (executablePath), which
|
|
5
|
+
* adds the GL/Metal backend, geoip, font, bluetooth/share and storage-quota flags per platform.
|
|
6
|
+
* This module only builds the *fingerprint-level* Chrome flags and locates that wrapper.
|
|
7
|
+
*
|
|
8
|
+
* Design note (why no stealth plugin): capium's own source patches make a vanilla driver
|
|
9
|
+
* stealthy --
|
|
10
|
+
* 001 (disable Runtime.enable console-reporting) -> FingerprintJS developer_tools = false
|
|
11
|
+
* 009 (webdriver) -> navigator.webdriver = false
|
|
12
|
+
* so we launch with plain Playwright / Puppeteer and let the binary do the CDP hardening.
|
|
13
|
+
*/
|
|
14
|
+
'use strict';
|
|
15
|
+
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const os = require('os');
|
|
18
|
+
const path = require('path');
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Return the default per-launch fingerprint flags.
|
|
22
|
+
*
|
|
23
|
+
* seed: int identity seed (a stable, coherent device per seed). Random if null.
|
|
24
|
+
* platform: "windows" | "macos" | "linux" -- the spoofed OS persona.
|
|
25
|
+
* screen: [width, height] for the spoofed screen AND the browser window, kept
|
|
26
|
+
* equal so screen == window == viewport. Pass null to skip (rely on the
|
|
27
|
+
* seed's device screen + fitWindow; only coherent on a real display).
|
|
28
|
+
*/
|
|
29
|
+
function getDefaultStealthArgs(seed = null, platform = 'windows', screen = [1440, 900]) {
|
|
30
|
+
const args = [];
|
|
31
|
+
if (seed !== null && seed !== undefined) {
|
|
32
|
+
const s = Math.trunc(Number(seed));
|
|
33
|
+
args.push(`--fingerprint=${s}`);
|
|
34
|
+
if (platform) args.push(`--fingerprint-platform=${platform}`);
|
|
35
|
+
// Storage quota: navigator.storage.estimate().quota otherwise reports the real
|
|
36
|
+
// host disk -- a VM/fingerprint tell (e.g. a 10 GB container), and on Windows it
|
|
37
|
+
// is UNSPOOFED because no bash wrapper runs there (chrome.exe is launched direct).
|
|
38
|
+
// Emit the SAME per-seed 128-576 GB SSD-like value the POSIX `capium` wrapper
|
|
39
|
+
// derives, so the quota is spoofed coherently on EVERY platform. On Linux/macOS the
|
|
40
|
+
// wrapper sees this flag (HAVE_QUOTA=1) and defers to it rather than adding its own.
|
|
41
|
+
args.push(`--fingerprint-storage-quota=${128 + (s % 8) * 64}`);
|
|
42
|
+
}
|
|
43
|
+
args.push(
|
|
44
|
+
// Allow third-party cookies (reCAPTCHA/login flows need them). capium blocks 3p cookies
|
|
45
|
+
// by DEFAULT (fpc sets CookieControlsMode=kBlockThirdParty); --fingerprint-allow-3p-cookies
|
|
46
|
+
// is the WIRED switch that sets CookieControlsMode=kOff (verified in cookie_settings.cc).
|
|
47
|
+
// We keep --disable-features=TrackingProtection3pcd too (belt-and-suspenders / other path).
|
|
48
|
+
'--fingerprint-allow-3p-cookies',
|
|
49
|
+
'--disable-features=TrackingProtection3pcd',
|
|
50
|
+
// present a desktop mouse (fine pointer + hover), not a touch device
|
|
51
|
+
'--blink-settings=primaryHoverType=2,availableHoverTypes=2,' +
|
|
52
|
+
'primaryPointerType=4,availablePointerTypes=4',
|
|
53
|
+
'--no-first-run',
|
|
54
|
+
'--no-default-browser-check',
|
|
55
|
+
);
|
|
56
|
+
// Patch 001 makes developer_tools always false, which unmasks FingerprintJS's
|
|
57
|
+
// tampering ML: per-seed canvas/audio NOISE would then read as tampering, so
|
|
58
|
+
// default it OFF (measured clean: tampering=false, ml~0.05, anti_detect=false).
|
|
59
|
+
// Trade-off: personas on the same host share its real canvas (no per-seed canvas
|
|
60
|
+
// uniqueness) -- pass --fingerprint-noise=true to restore it (accepts tampering).
|
|
61
|
+
args.push('--fingerprint-noise=false');
|
|
62
|
+
// A Windows persona's font metrics must match real Windows (needs Windows fonts
|
|
63
|
+
// present on the host); harmless elsewhere, so gate it to the windows persona.
|
|
64
|
+
if (platform === 'windows') args.push('--fingerprint-windows-font-metrics');
|
|
65
|
+
// Screen/viewport coherence: in a headless container the persona's seed-derived
|
|
66
|
+
// screen (e.g. 2560x1080) mismatches the actual small browser viewport (e.g.
|
|
67
|
+
// 945x939) -- browserscan flags "screen dimensions don't match viewport" as a
|
|
68
|
+
// virtual machine. Pin the spoofed screen to a window we also size, so
|
|
69
|
+
// screen == window == viewport in every environment (headless/container too),
|
|
70
|
+
// since the --window-size flag applies at launch without a window manager.
|
|
71
|
+
if (screen) {
|
|
72
|
+
const sw = Math.trunc(Number(screen[0]));
|
|
73
|
+
const sh = Math.trunc(Number(screen[1]));
|
|
74
|
+
args.push(`--window-size=${sw},${sh}`);
|
|
75
|
+
args.push(`--fingerprint-screen-width=${sw}`);
|
|
76
|
+
args.push(`--fingerprint-screen-height=${sh}`);
|
|
77
|
+
}
|
|
78
|
+
return args;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** A fresh random identity seed. */
|
|
82
|
+
function newSeed() {
|
|
83
|
+
return 1 + Math.floor(Math.random() * (2 ** 31 - 2));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ---- binary / wrapper discovery -------------------------------------------------------------
|
|
87
|
+
// The launch target (handed to the driver as executablePath) differs by OS:
|
|
88
|
+
// * POSIX (Linux/macOS): the `capium` bash wrapper -- it adds the GL/geoip/font flags and
|
|
89
|
+
// execs the real `chrome` engine beside it.
|
|
90
|
+
// * Windows: no wrapper ships; `chrome.exe` is launched directly (the persona flags the SDK
|
|
91
|
+
// passes are enough, and WebGL uses the host GPU).
|
|
92
|
+
// Distros extract as a `capium-<version>-<platform>/` directory (tar) or, on Windows, flat --
|
|
93
|
+
// the downloader normalizes both into a `capium-*` directory, and discovery GLOBS for those
|
|
94
|
+
// rather than pinning version numbers, so a new Chromium version needs no code change here.
|
|
95
|
+
//
|
|
96
|
+
// npm note: node_modules is volatile (a reinstall wipes it), so unlike the pip SDK the default
|
|
97
|
+
// install root is ~/.capium (CAPIUM_HOME overrides). The package dirs are still searched so a
|
|
98
|
+
// vendored distro next to the package keeps working.
|
|
99
|
+
|
|
100
|
+
// The engine binaries the wrapper spawns; they need +x alongside the wrapper itself.
|
|
101
|
+
const EXEC_SIBLINGS = ['chrome', 'chrome_crashpad_handler', 'chrome_sandbox', 'chrome-sandbox'];
|
|
102
|
+
|
|
103
|
+
function wrapperNames() {
|
|
104
|
+
// Launch-target filenames to look for, in priority order, for this OS.
|
|
105
|
+
if (process.platform === 'win32') return ['capium.exe', 'capium.bat', 'capium', 'chrome.exe'];
|
|
106
|
+
return ['capium'];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* True if `d` holds a capium distro (marker files present), so a generic `chrome.exe`
|
|
111
|
+
* found there is ours and not an unrelated system Chrome install.
|
|
112
|
+
*/
|
|
113
|
+
function isCapiumDir(d) {
|
|
114
|
+
const isDir = (p) => { try { return fs.statSync(p).isDirectory(); } catch { return false; } };
|
|
115
|
+
const isFile = (p) => { try { return fs.statSync(p).isFile(); } catch { return false; } };
|
|
116
|
+
// .capium-build is the downloader's own version stamp: the Windows distro tar is FLAT
|
|
117
|
+
// (chrome.exe at the root, no capium_fonts/BUILD_INFO inside), so the download layer marks
|
|
118
|
+
// the directory it extracted and discovery trusts that mark.
|
|
119
|
+
return isDir(path.join(d, 'capium_fonts')) || isFile(path.join(d, 'BUILD_INFO.txt')) ||
|
|
120
|
+
isFile(path.join(d, '.capium-build'));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Best-effort `chmod +x` on the wrapper and the engine binaries it spawns (POSIX only).
|
|
125
|
+
*
|
|
126
|
+
* Vendoring the distro through git-on-Windows, a .zip, an npm package, or a Docker COPY
|
|
127
|
+
* routinely strips the Unix execute bit -- which makes the driver fail to launch with the
|
|
128
|
+
* opaque `spawn <path> EACCES`. Self-heal it so a dropped permission bit isn't a hard stop.
|
|
129
|
+
* Returns true if `binPath` is executable afterwards. No-op on Windows (no +x concept).
|
|
130
|
+
*/
|
|
131
|
+
function ensureExecutable(binPath) {
|
|
132
|
+
if (process.platform === 'win32') return true;
|
|
133
|
+
const d = path.dirname(binPath);
|
|
134
|
+
for (const t of [binPath, ...EXEC_SIBLINGS.map((s) => path.join(d, s))]) {
|
|
135
|
+
try {
|
|
136
|
+
const st = fs.statSync(t);
|
|
137
|
+
if (st.isFile()) fs.chmodSync(t, st.mode | 0o111); // +x for user/group/other
|
|
138
|
+
} catch {}
|
|
139
|
+
}
|
|
140
|
+
try {
|
|
141
|
+
fs.accessSync(binPath, fs.constants.X_OK);
|
|
142
|
+
return true;
|
|
143
|
+
} catch {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function isExecutable(p) {
|
|
149
|
+
if (process.platform === 'win32') return true;
|
|
150
|
+
try {
|
|
151
|
+
fs.accessSync(p, fs.constants.X_OK);
|
|
152
|
+
return true;
|
|
153
|
+
} catch {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Directories under which distros are searched / installed (in priority order). */
|
|
159
|
+
function searchBases() {
|
|
160
|
+
const bases = [];
|
|
161
|
+
if (process.env.CAPIUM_HOME) bases.push(process.env.CAPIUM_HOME);
|
|
162
|
+
bases.push(path.join(os.homedir(), '.capium')); // default install root (survives npm i)
|
|
163
|
+
const here = __dirname; // <pkg>/lib
|
|
164
|
+
bases.push(path.dirname(path.dirname(here))); // node_modules (package parent)
|
|
165
|
+
bases.push(path.dirname(here)); // the package dir itself
|
|
166
|
+
bases.push(process.cwd());
|
|
167
|
+
return bases;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function capiumDirsUnder(base) {
|
|
171
|
+
// Extracted distro dirs `capium-*/` under base, newest name first (mirrors the glob sort).
|
|
172
|
+
try {
|
|
173
|
+
return fs
|
|
174
|
+
.readdirSync(base)
|
|
175
|
+
.filter((n) => n.startsWith('capium-'))
|
|
176
|
+
.sort()
|
|
177
|
+
.reverse()
|
|
178
|
+
.map((n) => path.join(base, n))
|
|
179
|
+
.filter((d) => {
|
|
180
|
+
try {
|
|
181
|
+
return fs.statSync(d).isDirectory();
|
|
182
|
+
} catch {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
} catch {
|
|
187
|
+
return [];
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function which(name) {
|
|
192
|
+
const dirs = (process.env.PATH || '').split(path.delimiter).filter(Boolean);
|
|
193
|
+
const exts = process.platform === 'win32'
|
|
194
|
+
? (process.env.PATHEXT || '.EXE;.BAT;.CMD').split(';').concat([''])
|
|
195
|
+
: [''];
|
|
196
|
+
for (const dir of dirs) {
|
|
197
|
+
for (const ext of exts) {
|
|
198
|
+
const p = path.join(dir, name + ext.toLowerCase());
|
|
199
|
+
try {
|
|
200
|
+
if (fs.statSync(p).isFile()) return p;
|
|
201
|
+
} catch {}
|
|
202
|
+
const p2 = path.join(dir, name);
|
|
203
|
+
try {
|
|
204
|
+
if (fs.statSync(p2).isFile()) return p2;
|
|
205
|
+
} catch {}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Locate the capium launch target (handed to the driver as executablePath).
|
|
213
|
+
*
|
|
214
|
+
* Search order: the explicit `binary` arg, then `CAPIUM_BINARY`, then -- under `CAPIUM_HOME`,
|
|
215
|
+
* `~/.capium`, the package's parent, the package dir, and the CWD -- a flat layout
|
|
216
|
+
* (`<base>/<name>`) or any extracted `capium-*` distro directory (newest first), then PATH.
|
|
217
|
+
* On Windows the target is `chrome.exe`, accepted only next to capium marker files so a
|
|
218
|
+
* system Chrome isn't picked up.
|
|
219
|
+
*
|
|
220
|
+
* A candidate that exists but isn't executable is repaired in place (chmod +x) rather than
|
|
221
|
+
* skipped -- a vendored distro losing its execute bit is a deployment slip, not a missing file.
|
|
222
|
+
*
|
|
223
|
+
* Throws (Error.code === 'ENOENT') when nothing is found -- the download layer catches that.
|
|
224
|
+
*/
|
|
225
|
+
function findBinary(binary = null) {
|
|
226
|
+
const names = wrapperNames();
|
|
227
|
+
// [candidatePath, containingDir] so a bare chrome.exe can be checked for capium markers.
|
|
228
|
+
const cands = [];
|
|
229
|
+
for (const explicit of [binary, process.env.CAPIUM_BINARY]) {
|
|
230
|
+
if (explicit) cands.push([explicit, path.dirname(path.resolve(explicit))]);
|
|
231
|
+
}
|
|
232
|
+
for (const base of searchBases()) {
|
|
233
|
+
for (const nm of names) cands.push([path.join(base, nm), base]); // flat: <base>/<name>
|
|
234
|
+
for (const d of capiumDirsUnder(base)) {
|
|
235
|
+
for (const nm of names) cands.push([path.join(d, nm), d]); // extracted distro dir
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
for (const nm of names) {
|
|
239
|
+
// PATH -- wrapper only, never a system chrome.exe
|
|
240
|
+
if (nm.toLowerCase() === 'chrome.exe') continue;
|
|
241
|
+
const onPath = which(nm);
|
|
242
|
+
if (onPath) cands.push([onPath, path.dirname(onPath)]);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const foundButUnusable = [];
|
|
246
|
+
const seen = new Set();
|
|
247
|
+
for (const [p, baseDir] of cands) {
|
|
248
|
+
if (!p) continue;
|
|
249
|
+
const ap = path.resolve(p);
|
|
250
|
+
if (seen.has(ap)) continue;
|
|
251
|
+
seen.add(ap);
|
|
252
|
+
try {
|
|
253
|
+
if (!fs.statSync(ap).isFile()) continue;
|
|
254
|
+
} catch {
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
// Guard a bare chrome.exe: only ours if its dir carries capium markers.
|
|
258
|
+
if (path.basename(ap).toLowerCase() === 'chrome.exe' && !isCapiumDir(baseDir)) continue;
|
|
259
|
+
if (ensureExecutable(ap) || isExecutable(ap)) return ap;
|
|
260
|
+
foundButUnusable.push(ap);
|
|
261
|
+
}
|
|
262
|
+
if (foundButUnusable.length) {
|
|
263
|
+
const f = foundButUnusable[0];
|
|
264
|
+
const err = new Error(
|
|
265
|
+
`found the capium binary at ${f} but it is not executable and \`chmod +x\` did not ` +
|
|
266
|
+
`stick. Run \`chmod +x ${f}\` (and the chrome/chrome_crashpad_handler beside it). If it ` +
|
|
267
|
+
'sits on a noexec mount (common in containers), relocate it to an exec-enabled path.');
|
|
268
|
+
err.code = 'EACCES';
|
|
269
|
+
throw err;
|
|
270
|
+
}
|
|
271
|
+
const err = new Error(
|
|
272
|
+
'capium binary not found. Set CAPIUM_BINARY=/path/to/<capium|chrome.exe>, put the distro ' +
|
|
273
|
+
'under CAPIUM_HOME (default ~/.capium) or next to the package, or add it to PATH.');
|
|
274
|
+
err.code = 'ENOENT';
|
|
275
|
+
throw err;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
module.exports = {
|
|
279
|
+
getDefaultStealthArgs,
|
|
280
|
+
newSeed,
|
|
281
|
+
findBinary,
|
|
282
|
+
searchBases,
|
|
283
|
+
_ensureExecutable: ensureExecutable,
|
|
284
|
+
_isCapiumDir: isCapiumDir,
|
|
285
|
+
_wrapperNames: wrapperNames,
|
|
286
|
+
};
|