device-passport 1.0.0 → 1.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/CHANGELOG.md +30 -0
- package/README.md +69 -2
- package/dist/adapters/index.cjs +19 -0
- package/dist/adapters/index.d.cts +156 -0
- package/dist/adapters/index.d.mts +156 -0
- package/dist/adapters/index.d.ts +156 -0
- package/dist/adapters/index.mjs +3 -0
- package/dist/evaluator/index.cjs +702 -0
- package/dist/evaluator/index.d.cts +204 -0
- package/dist/evaluator/index.d.mts +204 -0
- package/dist/evaluator/index.d.ts +204 -0
- package/dist/evaluator/index.mjs +680 -0
- package/dist/geo/index.cjs +50 -0
- package/dist/geo/index.d.cts +209 -0
- package/dist/geo/index.d.mts +209 -0
- package/dist/geo/index.d.ts +209 -0
- package/dist/geo/index.mjs +18 -0
- package/dist/index.cjs +113 -3543
- package/dist/index.d.cts +45 -903
- package/dist/index.d.mts +45 -903
- package/dist/index.d.ts +45 -903
- package/dist/index.mjs +11 -3438
- package/dist/shared/device-passport.BBf9OKap.mjs +1237 -0
- package/dist/shared/device-passport.BPd2-w8e.cjs +1265 -0
- package/dist/shared/device-passport.CLaBZWUV.cjs +1569 -0
- package/dist/shared/device-passport.C_F1vBE8.d.cts +79 -0
- package/dist/shared/device-passport.C_F1vBE8.d.mts +79 -0
- package/dist/shared/device-passport.C_F1vBE8.d.ts +79 -0
- package/dist/shared/device-passport.Cjxlv8xI.cjs +245 -0
- package/dist/shared/device-passport.CyQtcZeD.mjs +237 -0
- package/dist/shared/device-passport.NHw_f6HT.mjs +1535 -0
- package/dist/storage/index.cjs +10 -0
- package/dist/storage/index.d.cts +128 -0
- package/dist/storage/index.d.mts +128 -0
- package/dist/storage/index.d.ts +128 -0
- package/dist/storage/index.mjs +3 -0
- package/dist/types/index.cjs +2 -0
- package/dist/types/index.d.cts +215 -0
- package/dist/types/index.d.mts +215 -0
- package/dist/types/index.d.ts +215 -0
- package/dist/types/index.mjs +1 -0
- package/package.json +27 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,36 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.1.0] - 2026-11-04
|
|
9
|
+
|
|
10
|
+
> ⚠️ 发布受阻说明:本版本代码已合并并打好 tag,但 **npm 发布尚未完成** ——
|
|
11
|
+
> `device-passport` 在 npmjs.com 上没有配置 Trusted Publisher 信任关系。
|
|
12
|
+
> 证据:`device-passport@1.0.0` 没有 OIDC attestation(对比 `im-notify-kit@0.4.1` 有),
|
|
13
|
+
> 说明该包从未成功走通 OIDC 通道;workflow 里的 `id-token: write` 只是必要条件,
|
|
14
|
+
> 不是充分条件。需要在 npmjs.com → Packages → device-passport → Settings →
|
|
15
|
+
> Trusted Publisher 里登记:GitHub Actions / 组织 `webkubor` / 仓库 `device-passport` /
|
|
16
|
+
> 工作流 `publish.yml`,并允许 `npm publish`。登记后 2 天内需完成首次发布,否则配置过期。
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- **Pixel-level protection**: `getStealthScript()` now appends a deterministic noise layer that patches `canvas.toDataURL()`, `getImageData()`, `gl.readPixels()` and the audio analyser, so read-back no longer exposes the host GPU or a shared hash across personas. The noise is derived from the passport seed — stable per account, distinct per account — and uses discrete quantization levels rather than per-pixel dithering, because a smooth ±1 RGB jitter is itself a fingerprint.
|
|
20
|
+
- **Raw CDP adapter**: `getCdpPlan()` and `applyCdpPassport()` emit an ordered, transport-agnostic Chrome DevTools Protocol command plan for tools that speak bare CDP (e.g. `lite-browser`'s `cdp` channel). Covers headers, `Emulation.setUserAgentOverride` with Client-Hints metadata, timezone, locale, device metrics, touch emulation and init-script installation.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
- **Canvas serialization recursion**: the temporary canvas used for re-serialization shares the patched prototype, so calling its `toDataURL` recursed until the stack limit. Serialization now uses the captured original method (verified with 25 repeated calls in real Chromium).
|
|
24
|
+
- **WebGL canvases leaked the real framebuffer through `toDataURL()`**: a canvas with a WebGL context returns `null` from `getContext('2d')`, so that path silently skipped protection. Pixel perturbation now runs through `gl.readPixels` for such canvases.
|
|
25
|
+
- **Alpha bytes were perturbed**: RGBA alpha now stays untouched, so transparent and opaque pixels keep their semantics (masks and hit-testing unaffected).
|
|
26
|
+
- **Audio perturbation was seed-independent**: a fixed `1e-7` step was identical for every persona and rounded away in Float32 at typical dB values. The step is now seed-derived and representable.
|
|
27
|
+
- **Injected stealth script was invalid JavaScript**: the runtime script embedded TypeScript-only syntax (`navigator as any`), which raised a `SyntaxError` in the page and silently disabled the **entire** stealth layer — `navigator.webdriver` stayed `true` and every spoofed API reverted to the real values. The script is now valid browser JavaScript, and a regression test parses it to prevent this from returning.
|
|
28
|
+
- **CDP init scripts never executed**: `Page.addScriptToEvaluateOnNewDocument` was issued without `Page.enable`. Chrome accepted the script, returned an identifier and then never ran it, so a CDP-driven persona looked like a plain automation session. `Page.enable` is now emitted first, with a regression test covering the ordering.
|
|
29
|
+
|
|
30
|
+
## [1.0.1] - 2026-10-17
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
- **Standard ESM subpath exports**: `device-passport/geo`, `device-passport/storage`, `device-passport/adapters` are now importable as separate entry points, each shipping its own `types` / `import` / `require` conditions. Consumers can tree-shake by pulling only the module they need instead of the whole bundle.
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
- **npm publish path**: the release workflow now runs on Node 24 with pure OIDC Trusted Publishing (the previous `NODE_AUTH_TOKEN` pointed at a secret that was never configured, which forced npm down the token-auth path and made Trusted Publishing ineffective).
|
|
37
|
+
|
|
8
38
|
## [1.0.0] - 2026-10-17
|
|
9
39
|
|
|
10
40
|
### Added
|
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="
|
|
2
|
+
<img src="https://cdn.jsdelivr.net/gh/webkubor/free-imgbed@main/device-passport/banner.svg" alt="device-passport banner" width="800"/>
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<h1 align="center">🛂 device-passport</h1>
|
|
@@ -59,7 +59,7 @@ Modern anti-bot classifiers (Cloudflare Turnstile, DataDome, Akamai, GitHub Anti
|
|
|
59
59
|
Under-parameterized headers ($H(X) < 4.6$ bits/char) scream "naive scraper"; chaotic randomized noise ($H(X) > 5.6$ bits/char) screams "evasion bot". `device-passport` operates strictly within the natural human entropy equilibrium ($5.24$ bits/char).
|
|
60
60
|
|
|
61
61
|
<p align="center">
|
|
62
|
-
<img src="
|
|
62
|
+
<img src="https://cdn.jsdelivr.net/gh/webkubor/free-imgbed@main/device-passport/architecture.svg" alt="device-passport formal state-space architecture" width="100%"/>
|
|
63
63
|
</p>
|
|
64
64
|
|
|
65
65
|
---
|
|
@@ -292,6 +292,60 @@ await passport.injectToPuppeteer(page);
|
|
|
292
292
|
await page.goto('https://bot.sannysoft.com');
|
|
293
293
|
```
|
|
294
294
|
|
|
295
|
+
### Raw CDP Integration (lite-browser, custom transports)
|
|
296
|
+
|
|
297
|
+
Tools that drive Chrome over a bare DevTools Protocol socket cannot use the Playwright or
|
|
298
|
+
Puppeteer adapters, because those return framework-shaped options. `getCdpPlan()` returns
|
|
299
|
+
an ordered, transport-agnostic command plan instead:
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
import { createPassport, applyCdpPassport } from 'device-passport';
|
|
303
|
+
|
|
304
|
+
const passport = createPassport({ seed: 'worker_de_19', geo: 'DE', os: 'windows' });
|
|
305
|
+
|
|
306
|
+
// `send` is yours: a WebSocket, stdio, or an in-process socket.
|
|
307
|
+
await applyCdpPassport(passport, (cmd) => transport.send(cmd.method, cmd.params));
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
The plan covers `Page.enable`, extra HTTP headers, `Emulation.setUserAgentOverride`
|
|
311
|
+
(with Client-Hints metadata), timezone, locale, device metrics and touch emulation, and
|
|
312
|
+
installs the stealth script through `Page.addScriptToEvaluateOnNewDocument`.
|
|
313
|
+
|
|
314
|
+
> **`Page.enable` is mandatory.** Without it Chrome accepts the init script, returns an
|
|
315
|
+
> identifier, and then never runs it — the persona stays a plain automation session.
|
|
316
|
+
> `applyCdpPassport` emits it first for that reason.
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Pixel-Level Protection
|
|
321
|
+
|
|
322
|
+
API-level spoofing is not sufficient on its own. `canvas.toDataURL()` and
|
|
323
|
+
`gl.readPixels()` hand back the **real** framebuffer, so a headless browser exposes
|
|
324
|
+
SwiftShader while its User-Agent claims a discrete GPU — and every persona rendered on
|
|
325
|
+
the same machine produces a **byte-identical** canvas hash, which makes a fleet easy to
|
|
326
|
+
cluster.
|
|
327
|
+
|
|
328
|
+
`getStealthScript()` now appends a deterministic pixel-protection layer that patches
|
|
329
|
+
`toDataURL`, `getImageData`, `readPixels` and the audio analyser:
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
import { createPassport, getStealthScript } from 'device-passport';
|
|
333
|
+
|
|
334
|
+
const passport = createPassport('alice');
|
|
335
|
+
const script = getStealthScript(passport);
|
|
336
|
+
// Use with Playwright addInitScript, Puppeteer evaluateOnNewDocument, or CDP.
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
Properties:
|
|
340
|
+
|
|
341
|
+
- **Deterministic per seed** — the same account reads back identical pixels forever,
|
|
342
|
+
which is what makes "sticky hardware" true beyond the API layer.
|
|
343
|
+
- **Distinct per account** — different seeds produce different noise tables.
|
|
344
|
+
- **Plausible** — a discrete quantization of a few levels, not per-pixel dithering. Real
|
|
345
|
+
GPUs quantize; a smooth ±1 RGB jitter on every pixel is itself a fingerprint.
|
|
346
|
+
- **Self-contained** — dependency-free IIFE, safe to inject repeatedly (re-injection is
|
|
347
|
+
a no-op).
|
|
348
|
+
|
|
295
349
|
---
|
|
296
350
|
|
|
297
351
|
## Multi-Account Fleet Management (`PassportManager`)
|
|
@@ -411,6 +465,19 @@ npx device-passport inspect --seed production_user_102
|
|
|
411
465
|
│ Native Fetch │ │ Playwright │ │ Puppeteer │
|
|
412
466
|
│ Headers │ │Context Options│ │ Emulation Hook│
|
|
413
467
|
└──────────────┘ └──────────────┘ └──────────────┘
|
|
468
|
+
│ │
|
|
469
|
+
└───────────────┬───────────────┘
|
|
470
|
+
▼
|
|
471
|
+
┌───────────────────────────┐
|
|
472
|
+
│ Pixel-Level Protection │
|
|
473
|
+
│ canvas / WebGL / audio │
|
|
474
|
+
│ deterministic per seed │
|
|
475
|
+
└───────────┬───────────────┘
|
|
476
|
+
▼
|
|
477
|
+
┌───────────────────────────┐
|
|
478
|
+
│ Raw CDP Command Plan │
|
|
479
|
+
│ (lite-browser, custom) │
|
|
480
|
+
└───────────────────────────┘
|
|
414
481
|
```
|
|
415
482
|
|
|
416
483
|
---
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const cdp = require('../shared/device-passport.CLaBZWUV.cjs');
|
|
4
|
+
require('node:crypto');
|
|
5
|
+
require('../shared/device-passport.BPd2-w8e.cjs');
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
exports.CANONICAL_HEADER_ORDER = cdp.CANONICAL_HEADER_ORDER;
|
|
10
|
+
exports.applyCdpPassport = cdp.applyCdpPassport;
|
|
11
|
+
exports.applyToPlaywrightContext = cdp.applyToPlaywrightContext;
|
|
12
|
+
exports.applyToPuppeteerPage = cdp.applyToPuppeteerPage;
|
|
13
|
+
exports.formatCanonicalHeaders = cdp.formatCanonicalHeaders;
|
|
14
|
+
exports.getCdpPlan = cdp.getCdpPlan;
|
|
15
|
+
exports.getPlaywrightContext = cdp.getPlaywrightContext;
|
|
16
|
+
exports.getPuppeteerArgs = cdp.getPuppeteerArgs;
|
|
17
|
+
exports.getStealthScript = cdp.getStealthScript;
|
|
18
|
+
exports.toCurl = cdp.toCurl;
|
|
19
|
+
exports.withPassport = cdp.withPassport;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { DevicePassport } from '../types/index.cjs';
|
|
2
|
+
import '../shared/device-passport.C_F1vBE8.cjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Standard Chromium browser header ordering.
|
|
6
|
+
* Anti-bot systems (Cloudflare, Akamai, DataDome, Kasada) strictly inspect
|
|
7
|
+
* the sequence of HTTP request headers. A mismatch in header order triggers
|
|
8
|
+
* immediate bot heuristics.
|
|
9
|
+
*/
|
|
10
|
+
declare const CANONICAL_HEADER_ORDER: readonly string[];
|
|
11
|
+
/**
|
|
12
|
+
* Sorts and formats HTTP headers according to canonical browser ordering.
|
|
13
|
+
* Merges base headers with optional overrides.
|
|
14
|
+
*/
|
|
15
|
+
declare function formatCanonicalHeaders(baseHeaders: Record<string, string>, overrides?: Record<string, string>): Record<string, string>;
|
|
16
|
+
interface PassportLike {
|
|
17
|
+
headers: Record<string, string>;
|
|
18
|
+
getHeaders?: (overrides?: Record<string, string>) => Record<string, string>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Higher-order fetch function wrapping standard fetch with canonical DevicePassport headers.
|
|
22
|
+
* Ensures consistent TLS/HTTP persona across API calls, scraping requests, and webhooks.
|
|
23
|
+
*
|
|
24
|
+
* @param passport DevicePassport instance or object
|
|
25
|
+
* @param customFetch Optional fetch implementation (e.g. node-fetch, undici, or global fetch)
|
|
26
|
+
*/
|
|
27
|
+
declare function withPassport(passport: PassportLike | DevicePassport, customFetch?: typeof fetch): typeof fetch;
|
|
28
|
+
/**
|
|
29
|
+
* Generates an executable cURL command with correct browser headers.
|
|
30
|
+
*/
|
|
31
|
+
declare function toCurl(passport: PassportLike | DevicePassport, url: string, options?: {
|
|
32
|
+
method?: string;
|
|
33
|
+
data?: any;
|
|
34
|
+
headers?: Record<string, string>;
|
|
35
|
+
}): string;
|
|
36
|
+
|
|
37
|
+
interface PlaywrightContextOptions {
|
|
38
|
+
userAgent: string;
|
|
39
|
+
viewport: {
|
|
40
|
+
width: number;
|
|
41
|
+
height: number;
|
|
42
|
+
};
|
|
43
|
+
deviceScaleFactor: number;
|
|
44
|
+
locale: string;
|
|
45
|
+
timezoneId: string;
|
|
46
|
+
geolocation?: {
|
|
47
|
+
latitude: number;
|
|
48
|
+
longitude: number;
|
|
49
|
+
accuracy?: number;
|
|
50
|
+
};
|
|
51
|
+
permissions?: string[];
|
|
52
|
+
extraHTTPHeaders: Record<string, string>;
|
|
53
|
+
hasTouch: boolean;
|
|
54
|
+
isMobile: boolean;
|
|
55
|
+
colorScheme?: 'light' | 'dark' | 'no-preference';
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Builds Playwright BrowserContext options directly from a DevicePassport.
|
|
59
|
+
* Ready for `browser.newContext(getPlaywrightContext(passport))`.
|
|
60
|
+
*/
|
|
61
|
+
declare function getPlaywrightContext(passport: DevicePassport): PlaywrightContextOptions;
|
|
62
|
+
/**
|
|
63
|
+
* Generates an in-page stealth script to patch runtime JavaScript APIs
|
|
64
|
+
* (navigator.webdriver, hardwareConcurrency, deviceMemory, WebGL unmasked vendor/renderer,
|
|
65
|
+
* navigator.userAgentData Client Hints, and Screen dimensions).
|
|
66
|
+
*
|
|
67
|
+
* 🔴 API-level spoofing is not enough on its own: `canvas.toDataURL()` and `gl.readPixels()`
|
|
68
|
+
* still return the **real** framebuffer, so a headless browser exposes SwiftShader while
|
|
69
|
+
* its UA claims a discrete GPU — and every persona on one machine hashes identically.
|
|
70
|
+
* The pixel-protection script is therefore appended here, so existing users get
|
|
71
|
+
* read-back-safe personas without changing their call sites.
|
|
72
|
+
*
|
|
73
|
+
* The noise is seeded from the passport seed: stable per account, different per account.
|
|
74
|
+
*/
|
|
75
|
+
declare function getStealthScript(passport: DevicePassport): string;
|
|
76
|
+
/**
|
|
77
|
+
* Applies stealth injections and extra headers to a Playwright BrowserContext.
|
|
78
|
+
*
|
|
79
|
+
* @param context Playwright BrowserContext instance
|
|
80
|
+
* @param passport DevicePassport instance
|
|
81
|
+
*/
|
|
82
|
+
declare function applyToPlaywrightContext(context: any, passport: DevicePassport): Promise<void>;
|
|
83
|
+
|
|
84
|
+
interface PuppeteerEmulationOptions {
|
|
85
|
+
launchArgs: string[];
|
|
86
|
+
defaultViewport: {
|
|
87
|
+
width: number;
|
|
88
|
+
height: number;
|
|
89
|
+
deviceScaleFactor: number;
|
|
90
|
+
isMobile: boolean;
|
|
91
|
+
hasTouch: boolean;
|
|
92
|
+
};
|
|
93
|
+
userAgent: string;
|
|
94
|
+
locale: string;
|
|
95
|
+
timezoneId: string;
|
|
96
|
+
extraHTTPHeaders: Record<string, string>;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Returns Puppeteer launch args and page emulation parameters matching the DevicePassport.
|
|
100
|
+
*/
|
|
101
|
+
declare function getPuppeteerArgs(passport: DevicePassport): PuppeteerEmulationOptions;
|
|
102
|
+
/**
|
|
103
|
+
* Applies stealth injections, headers, viewport, and timezone emulation to a Puppeteer Page.
|
|
104
|
+
*
|
|
105
|
+
* @param page Puppeteer Page instance
|
|
106
|
+
* @param passport DevicePassport instance
|
|
107
|
+
*/
|
|
108
|
+
declare function applyToPuppeteerPage(page: any, passport: DevicePassport): Promise<void>;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Raw Chrome DevTools Protocol adapter.
|
|
112
|
+
*
|
|
113
|
+
* 🔴 Why this exists (2026-11-04):
|
|
114
|
+
* Tools that drive Chrome over a bare CDP socket — `lite-browser`'s `cdp <method> [json]`
|
|
115
|
+
* channel, for example — cannot use the Playwright/Puppeteer adapters, because those
|
|
116
|
+
* hand back framework-shaped options. This adapter speaks plain CDP so any CDP client
|
|
117
|
+
* can apply a passport.
|
|
118
|
+
*
|
|
119
|
+
* The returned plan is deliberately **declarative**: a `send(method, params)` function
|
|
120
|
+
* applies every command in order. The caller owns the transport, so this module has no
|
|
121
|
+
* dependency and works over WebSocket, stdio, or an in-process socket.
|
|
122
|
+
*/
|
|
123
|
+
interface CdpCommand {
|
|
124
|
+
method: string;
|
|
125
|
+
params?: Record<string, unknown>;
|
|
126
|
+
/** `sessionId` is required for page-scoped domains when using flattened sessions. */
|
|
127
|
+
sessionId?: string;
|
|
128
|
+
}
|
|
129
|
+
interface CdpPassportPlan {
|
|
130
|
+
/** Ordered commands to apply the passport to a target. */
|
|
131
|
+
commands: CdpCommand[];
|
|
132
|
+
/** Init scripts to install on every new document. */
|
|
133
|
+
initScripts: {
|
|
134
|
+
source: string;
|
|
135
|
+
}[];
|
|
136
|
+
/** Extra HTTP headers to set on the session. */
|
|
137
|
+
headers: Record<string, string>;
|
|
138
|
+
/** The raw stealth + pixel-protection script, for callers that prefer their own path. */
|
|
139
|
+
stealthScript: string;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Builds the ordered CDP command plan for a passport.
|
|
143
|
+
*
|
|
144
|
+
* Order matters: the UA override must precede navigation, and the init script has to be
|
|
145
|
+
* installed before the first document is created.
|
|
146
|
+
*/
|
|
147
|
+
declare function getCdpPlan(passport: DevicePassport, sessionId?: string): CdpPassportPlan;
|
|
148
|
+
/**
|
|
149
|
+
* Applies a passport through a raw CDP transport.
|
|
150
|
+
*
|
|
151
|
+
* @param send Function that sends one CDP command and resolves with its result.
|
|
152
|
+
*/
|
|
153
|
+
declare function applyCdpPassport(passport: DevicePassport, send: (command: CdpCommand) => Promise<unknown>, sessionId?: string): Promise<void>;
|
|
154
|
+
|
|
155
|
+
export { CANONICAL_HEADER_ORDER, applyCdpPassport, applyToPlaywrightContext, applyToPuppeteerPage, formatCanonicalHeaders, getCdpPlan, getPlaywrightContext, getPuppeteerArgs, getStealthScript, toCurl, withPassport };
|
|
156
|
+
export type { CdpCommand, CdpPassportPlan, PassportLike, PlaywrightContextOptions, PuppeteerEmulationOptions };
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { DevicePassport } from '../types/index.mjs';
|
|
2
|
+
import '../shared/device-passport.C_F1vBE8.mjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Standard Chromium browser header ordering.
|
|
6
|
+
* Anti-bot systems (Cloudflare, Akamai, DataDome, Kasada) strictly inspect
|
|
7
|
+
* the sequence of HTTP request headers. A mismatch in header order triggers
|
|
8
|
+
* immediate bot heuristics.
|
|
9
|
+
*/
|
|
10
|
+
declare const CANONICAL_HEADER_ORDER: readonly string[];
|
|
11
|
+
/**
|
|
12
|
+
* Sorts and formats HTTP headers according to canonical browser ordering.
|
|
13
|
+
* Merges base headers with optional overrides.
|
|
14
|
+
*/
|
|
15
|
+
declare function formatCanonicalHeaders(baseHeaders: Record<string, string>, overrides?: Record<string, string>): Record<string, string>;
|
|
16
|
+
interface PassportLike {
|
|
17
|
+
headers: Record<string, string>;
|
|
18
|
+
getHeaders?: (overrides?: Record<string, string>) => Record<string, string>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Higher-order fetch function wrapping standard fetch with canonical DevicePassport headers.
|
|
22
|
+
* Ensures consistent TLS/HTTP persona across API calls, scraping requests, and webhooks.
|
|
23
|
+
*
|
|
24
|
+
* @param passport DevicePassport instance or object
|
|
25
|
+
* @param customFetch Optional fetch implementation (e.g. node-fetch, undici, or global fetch)
|
|
26
|
+
*/
|
|
27
|
+
declare function withPassport(passport: PassportLike | DevicePassport, customFetch?: typeof fetch): typeof fetch;
|
|
28
|
+
/**
|
|
29
|
+
* Generates an executable cURL command with correct browser headers.
|
|
30
|
+
*/
|
|
31
|
+
declare function toCurl(passport: PassportLike | DevicePassport, url: string, options?: {
|
|
32
|
+
method?: string;
|
|
33
|
+
data?: any;
|
|
34
|
+
headers?: Record<string, string>;
|
|
35
|
+
}): string;
|
|
36
|
+
|
|
37
|
+
interface PlaywrightContextOptions {
|
|
38
|
+
userAgent: string;
|
|
39
|
+
viewport: {
|
|
40
|
+
width: number;
|
|
41
|
+
height: number;
|
|
42
|
+
};
|
|
43
|
+
deviceScaleFactor: number;
|
|
44
|
+
locale: string;
|
|
45
|
+
timezoneId: string;
|
|
46
|
+
geolocation?: {
|
|
47
|
+
latitude: number;
|
|
48
|
+
longitude: number;
|
|
49
|
+
accuracy?: number;
|
|
50
|
+
};
|
|
51
|
+
permissions?: string[];
|
|
52
|
+
extraHTTPHeaders: Record<string, string>;
|
|
53
|
+
hasTouch: boolean;
|
|
54
|
+
isMobile: boolean;
|
|
55
|
+
colorScheme?: 'light' | 'dark' | 'no-preference';
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Builds Playwright BrowserContext options directly from a DevicePassport.
|
|
59
|
+
* Ready for `browser.newContext(getPlaywrightContext(passport))`.
|
|
60
|
+
*/
|
|
61
|
+
declare function getPlaywrightContext(passport: DevicePassport): PlaywrightContextOptions;
|
|
62
|
+
/**
|
|
63
|
+
* Generates an in-page stealth script to patch runtime JavaScript APIs
|
|
64
|
+
* (navigator.webdriver, hardwareConcurrency, deviceMemory, WebGL unmasked vendor/renderer,
|
|
65
|
+
* navigator.userAgentData Client Hints, and Screen dimensions).
|
|
66
|
+
*
|
|
67
|
+
* 🔴 API-level spoofing is not enough on its own: `canvas.toDataURL()` and `gl.readPixels()`
|
|
68
|
+
* still return the **real** framebuffer, so a headless browser exposes SwiftShader while
|
|
69
|
+
* its UA claims a discrete GPU — and every persona on one machine hashes identically.
|
|
70
|
+
* The pixel-protection script is therefore appended here, so existing users get
|
|
71
|
+
* read-back-safe personas without changing their call sites.
|
|
72
|
+
*
|
|
73
|
+
* The noise is seeded from the passport seed: stable per account, different per account.
|
|
74
|
+
*/
|
|
75
|
+
declare function getStealthScript(passport: DevicePassport): string;
|
|
76
|
+
/**
|
|
77
|
+
* Applies stealth injections and extra headers to a Playwright BrowserContext.
|
|
78
|
+
*
|
|
79
|
+
* @param context Playwright BrowserContext instance
|
|
80
|
+
* @param passport DevicePassport instance
|
|
81
|
+
*/
|
|
82
|
+
declare function applyToPlaywrightContext(context: any, passport: DevicePassport): Promise<void>;
|
|
83
|
+
|
|
84
|
+
interface PuppeteerEmulationOptions {
|
|
85
|
+
launchArgs: string[];
|
|
86
|
+
defaultViewport: {
|
|
87
|
+
width: number;
|
|
88
|
+
height: number;
|
|
89
|
+
deviceScaleFactor: number;
|
|
90
|
+
isMobile: boolean;
|
|
91
|
+
hasTouch: boolean;
|
|
92
|
+
};
|
|
93
|
+
userAgent: string;
|
|
94
|
+
locale: string;
|
|
95
|
+
timezoneId: string;
|
|
96
|
+
extraHTTPHeaders: Record<string, string>;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Returns Puppeteer launch args and page emulation parameters matching the DevicePassport.
|
|
100
|
+
*/
|
|
101
|
+
declare function getPuppeteerArgs(passport: DevicePassport): PuppeteerEmulationOptions;
|
|
102
|
+
/**
|
|
103
|
+
* Applies stealth injections, headers, viewport, and timezone emulation to a Puppeteer Page.
|
|
104
|
+
*
|
|
105
|
+
* @param page Puppeteer Page instance
|
|
106
|
+
* @param passport DevicePassport instance
|
|
107
|
+
*/
|
|
108
|
+
declare function applyToPuppeteerPage(page: any, passport: DevicePassport): Promise<void>;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Raw Chrome DevTools Protocol adapter.
|
|
112
|
+
*
|
|
113
|
+
* 🔴 Why this exists (2026-11-04):
|
|
114
|
+
* Tools that drive Chrome over a bare CDP socket — `lite-browser`'s `cdp <method> [json]`
|
|
115
|
+
* channel, for example — cannot use the Playwright/Puppeteer adapters, because those
|
|
116
|
+
* hand back framework-shaped options. This adapter speaks plain CDP so any CDP client
|
|
117
|
+
* can apply a passport.
|
|
118
|
+
*
|
|
119
|
+
* The returned plan is deliberately **declarative**: a `send(method, params)` function
|
|
120
|
+
* applies every command in order. The caller owns the transport, so this module has no
|
|
121
|
+
* dependency and works over WebSocket, stdio, or an in-process socket.
|
|
122
|
+
*/
|
|
123
|
+
interface CdpCommand {
|
|
124
|
+
method: string;
|
|
125
|
+
params?: Record<string, unknown>;
|
|
126
|
+
/** `sessionId` is required for page-scoped domains when using flattened sessions. */
|
|
127
|
+
sessionId?: string;
|
|
128
|
+
}
|
|
129
|
+
interface CdpPassportPlan {
|
|
130
|
+
/** Ordered commands to apply the passport to a target. */
|
|
131
|
+
commands: CdpCommand[];
|
|
132
|
+
/** Init scripts to install on every new document. */
|
|
133
|
+
initScripts: {
|
|
134
|
+
source: string;
|
|
135
|
+
}[];
|
|
136
|
+
/** Extra HTTP headers to set on the session. */
|
|
137
|
+
headers: Record<string, string>;
|
|
138
|
+
/** The raw stealth + pixel-protection script, for callers that prefer their own path. */
|
|
139
|
+
stealthScript: string;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Builds the ordered CDP command plan for a passport.
|
|
143
|
+
*
|
|
144
|
+
* Order matters: the UA override must precede navigation, and the init script has to be
|
|
145
|
+
* installed before the first document is created.
|
|
146
|
+
*/
|
|
147
|
+
declare function getCdpPlan(passport: DevicePassport, sessionId?: string): CdpPassportPlan;
|
|
148
|
+
/**
|
|
149
|
+
* Applies a passport through a raw CDP transport.
|
|
150
|
+
*
|
|
151
|
+
* @param send Function that sends one CDP command and resolves with its result.
|
|
152
|
+
*/
|
|
153
|
+
declare function applyCdpPassport(passport: DevicePassport, send: (command: CdpCommand) => Promise<unknown>, sessionId?: string): Promise<void>;
|
|
154
|
+
|
|
155
|
+
export { CANONICAL_HEADER_ORDER, applyCdpPassport, applyToPlaywrightContext, applyToPuppeteerPage, formatCanonicalHeaders, getCdpPlan, getPlaywrightContext, getPuppeteerArgs, getStealthScript, toCurl, withPassport };
|
|
156
|
+
export type { CdpCommand, CdpPassportPlan, PassportLike, PlaywrightContextOptions, PuppeteerEmulationOptions };
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { DevicePassport } from '../types/index.js';
|
|
2
|
+
import '../shared/device-passport.C_F1vBE8.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Standard Chromium browser header ordering.
|
|
6
|
+
* Anti-bot systems (Cloudflare, Akamai, DataDome, Kasada) strictly inspect
|
|
7
|
+
* the sequence of HTTP request headers. A mismatch in header order triggers
|
|
8
|
+
* immediate bot heuristics.
|
|
9
|
+
*/
|
|
10
|
+
declare const CANONICAL_HEADER_ORDER: readonly string[];
|
|
11
|
+
/**
|
|
12
|
+
* Sorts and formats HTTP headers according to canonical browser ordering.
|
|
13
|
+
* Merges base headers with optional overrides.
|
|
14
|
+
*/
|
|
15
|
+
declare function formatCanonicalHeaders(baseHeaders: Record<string, string>, overrides?: Record<string, string>): Record<string, string>;
|
|
16
|
+
interface PassportLike {
|
|
17
|
+
headers: Record<string, string>;
|
|
18
|
+
getHeaders?: (overrides?: Record<string, string>) => Record<string, string>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Higher-order fetch function wrapping standard fetch with canonical DevicePassport headers.
|
|
22
|
+
* Ensures consistent TLS/HTTP persona across API calls, scraping requests, and webhooks.
|
|
23
|
+
*
|
|
24
|
+
* @param passport DevicePassport instance or object
|
|
25
|
+
* @param customFetch Optional fetch implementation (e.g. node-fetch, undici, or global fetch)
|
|
26
|
+
*/
|
|
27
|
+
declare function withPassport(passport: PassportLike | DevicePassport, customFetch?: typeof fetch): typeof fetch;
|
|
28
|
+
/**
|
|
29
|
+
* Generates an executable cURL command with correct browser headers.
|
|
30
|
+
*/
|
|
31
|
+
declare function toCurl(passport: PassportLike | DevicePassport, url: string, options?: {
|
|
32
|
+
method?: string;
|
|
33
|
+
data?: any;
|
|
34
|
+
headers?: Record<string, string>;
|
|
35
|
+
}): string;
|
|
36
|
+
|
|
37
|
+
interface PlaywrightContextOptions {
|
|
38
|
+
userAgent: string;
|
|
39
|
+
viewport: {
|
|
40
|
+
width: number;
|
|
41
|
+
height: number;
|
|
42
|
+
};
|
|
43
|
+
deviceScaleFactor: number;
|
|
44
|
+
locale: string;
|
|
45
|
+
timezoneId: string;
|
|
46
|
+
geolocation?: {
|
|
47
|
+
latitude: number;
|
|
48
|
+
longitude: number;
|
|
49
|
+
accuracy?: number;
|
|
50
|
+
};
|
|
51
|
+
permissions?: string[];
|
|
52
|
+
extraHTTPHeaders: Record<string, string>;
|
|
53
|
+
hasTouch: boolean;
|
|
54
|
+
isMobile: boolean;
|
|
55
|
+
colorScheme?: 'light' | 'dark' | 'no-preference';
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Builds Playwright BrowserContext options directly from a DevicePassport.
|
|
59
|
+
* Ready for `browser.newContext(getPlaywrightContext(passport))`.
|
|
60
|
+
*/
|
|
61
|
+
declare function getPlaywrightContext(passport: DevicePassport): PlaywrightContextOptions;
|
|
62
|
+
/**
|
|
63
|
+
* Generates an in-page stealth script to patch runtime JavaScript APIs
|
|
64
|
+
* (navigator.webdriver, hardwareConcurrency, deviceMemory, WebGL unmasked vendor/renderer,
|
|
65
|
+
* navigator.userAgentData Client Hints, and Screen dimensions).
|
|
66
|
+
*
|
|
67
|
+
* 🔴 API-level spoofing is not enough on its own: `canvas.toDataURL()` and `gl.readPixels()`
|
|
68
|
+
* still return the **real** framebuffer, so a headless browser exposes SwiftShader while
|
|
69
|
+
* its UA claims a discrete GPU — and every persona on one machine hashes identically.
|
|
70
|
+
* The pixel-protection script is therefore appended here, so existing users get
|
|
71
|
+
* read-back-safe personas without changing their call sites.
|
|
72
|
+
*
|
|
73
|
+
* The noise is seeded from the passport seed: stable per account, different per account.
|
|
74
|
+
*/
|
|
75
|
+
declare function getStealthScript(passport: DevicePassport): string;
|
|
76
|
+
/**
|
|
77
|
+
* Applies stealth injections and extra headers to a Playwright BrowserContext.
|
|
78
|
+
*
|
|
79
|
+
* @param context Playwright BrowserContext instance
|
|
80
|
+
* @param passport DevicePassport instance
|
|
81
|
+
*/
|
|
82
|
+
declare function applyToPlaywrightContext(context: any, passport: DevicePassport): Promise<void>;
|
|
83
|
+
|
|
84
|
+
interface PuppeteerEmulationOptions {
|
|
85
|
+
launchArgs: string[];
|
|
86
|
+
defaultViewport: {
|
|
87
|
+
width: number;
|
|
88
|
+
height: number;
|
|
89
|
+
deviceScaleFactor: number;
|
|
90
|
+
isMobile: boolean;
|
|
91
|
+
hasTouch: boolean;
|
|
92
|
+
};
|
|
93
|
+
userAgent: string;
|
|
94
|
+
locale: string;
|
|
95
|
+
timezoneId: string;
|
|
96
|
+
extraHTTPHeaders: Record<string, string>;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Returns Puppeteer launch args and page emulation parameters matching the DevicePassport.
|
|
100
|
+
*/
|
|
101
|
+
declare function getPuppeteerArgs(passport: DevicePassport): PuppeteerEmulationOptions;
|
|
102
|
+
/**
|
|
103
|
+
* Applies stealth injections, headers, viewport, and timezone emulation to a Puppeteer Page.
|
|
104
|
+
*
|
|
105
|
+
* @param page Puppeteer Page instance
|
|
106
|
+
* @param passport DevicePassport instance
|
|
107
|
+
*/
|
|
108
|
+
declare function applyToPuppeteerPage(page: any, passport: DevicePassport): Promise<void>;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Raw Chrome DevTools Protocol adapter.
|
|
112
|
+
*
|
|
113
|
+
* 🔴 Why this exists (2026-11-04):
|
|
114
|
+
* Tools that drive Chrome over a bare CDP socket — `lite-browser`'s `cdp <method> [json]`
|
|
115
|
+
* channel, for example — cannot use the Playwright/Puppeteer adapters, because those
|
|
116
|
+
* hand back framework-shaped options. This adapter speaks plain CDP so any CDP client
|
|
117
|
+
* can apply a passport.
|
|
118
|
+
*
|
|
119
|
+
* The returned plan is deliberately **declarative**: a `send(method, params)` function
|
|
120
|
+
* applies every command in order. The caller owns the transport, so this module has no
|
|
121
|
+
* dependency and works over WebSocket, stdio, or an in-process socket.
|
|
122
|
+
*/
|
|
123
|
+
interface CdpCommand {
|
|
124
|
+
method: string;
|
|
125
|
+
params?: Record<string, unknown>;
|
|
126
|
+
/** `sessionId` is required for page-scoped domains when using flattened sessions. */
|
|
127
|
+
sessionId?: string;
|
|
128
|
+
}
|
|
129
|
+
interface CdpPassportPlan {
|
|
130
|
+
/** Ordered commands to apply the passport to a target. */
|
|
131
|
+
commands: CdpCommand[];
|
|
132
|
+
/** Init scripts to install on every new document. */
|
|
133
|
+
initScripts: {
|
|
134
|
+
source: string;
|
|
135
|
+
}[];
|
|
136
|
+
/** Extra HTTP headers to set on the session. */
|
|
137
|
+
headers: Record<string, string>;
|
|
138
|
+
/** The raw stealth + pixel-protection script, for callers that prefer their own path. */
|
|
139
|
+
stealthScript: string;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Builds the ordered CDP command plan for a passport.
|
|
143
|
+
*
|
|
144
|
+
* Order matters: the UA override must precede navigation, and the init script has to be
|
|
145
|
+
* installed before the first document is created.
|
|
146
|
+
*/
|
|
147
|
+
declare function getCdpPlan(passport: DevicePassport, sessionId?: string): CdpPassportPlan;
|
|
148
|
+
/**
|
|
149
|
+
* Applies a passport through a raw CDP transport.
|
|
150
|
+
*
|
|
151
|
+
* @param send Function that sends one CDP command and resolves with its result.
|
|
152
|
+
*/
|
|
153
|
+
declare function applyCdpPassport(passport: DevicePassport, send: (command: CdpCommand) => Promise<unknown>, sessionId?: string): Promise<void>;
|
|
154
|
+
|
|
155
|
+
export { CANONICAL_HEADER_ORDER, applyCdpPassport, applyToPlaywrightContext, applyToPuppeteerPage, formatCanonicalHeaders, getCdpPlan, getPlaywrightContext, getPuppeteerArgs, getStealthScript, toCurl, withPassport };
|
|
156
|
+
export type { CdpCommand, CdpPassportPlan, PassportLike, PlaywrightContextOptions, PuppeteerEmulationOptions };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { C as CANONICAL_HEADER_ORDER, a as applyCdpPassport, b as applyToPlaywrightContext, c as applyToPuppeteerPage, f as formatCanonicalHeaders, g as getCdpPlan, d as getPlaywrightContext, e as getPuppeteerArgs, h as getStealthScript, t as toCurl, w as withPassport } from '../shared/device-passport.NHw_f6HT.mjs';
|
|
2
|
+
import 'node:crypto';
|
|
3
|
+
import '../shared/device-passport.BBf9OKap.mjs';
|