@rester159/blacktip 0.2.0 → 0.5.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 +222 -0
- package/README.md +25 -0
- package/dist/akamai-sensor.d.ts +128 -0
- package/dist/akamai-sensor.d.ts.map +1 -0
- package/dist/akamai-sensor.js +190 -0
- package/dist/akamai-sensor.js.map +1 -0
- package/dist/behavioral/parsers.d.ts +89 -0
- package/dist/behavioral/parsers.d.ts.map +1 -0
- package/dist/behavioral/parsers.js +223 -0
- package/dist/behavioral/parsers.js.map +1 -0
- package/dist/blacktip.d.ts +68 -1
- package/dist/blacktip.d.ts.map +1 -1
- package/dist/blacktip.js +140 -1
- package/dist/blacktip.js.map +1 -1
- package/dist/browser-core.d.ts +10 -0
- package/dist/browser-core.d.ts.map +1 -1
- package/dist/browser-core.js +49 -0
- package/dist/browser-core.js.map +1 -1
- package/dist/diagnostics.d.ts +31 -0
- package/dist/diagnostics.d.ts.map +1 -1
- package/dist/diagnostics.js +146 -0
- package/dist/diagnostics.js.map +1 -1
- package/dist/identity-pool.d.ts +160 -0
- package/dist/identity-pool.d.ts.map +1 -0
- package/dist/identity-pool.js +288 -0
- package/dist/identity-pool.js.map +1 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -1
- package/dist/index.js.map +1 -1
- package/dist/tls-rewriter.d.ts +74 -0
- package/dist/tls-rewriter.d.ts.map +1 -0
- package/dist/tls-rewriter.js +203 -0
- package/dist/tls-rewriter.js.map +1 -0
- package/dist/tls-side-channel.d.ts +91 -0
- package/dist/tls-side-channel.d.ts.map +1 -0
- package/dist/tls-side-channel.js +248 -0
- package/dist/tls-side-channel.js.map +1 -0
- package/dist/types.d.ts +46 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/docs/akamai-bypass.md +257 -0
- package/docs/akamai-sensor.md +183 -0
- package/docs/anti-bot-validation.md +84 -0
- package/docs/calibration-validation.md +93 -0
- package/docs/identity-pool.md +176 -0
- package/docs/tls-rewriting.md +121 -0
- package/docs/tls-side-channel.md +83 -0
- package/native/tls-client/go.mod +21 -0
- package/native/tls-client/go.sum +36 -0
- package/native/tls-client/main.go +216 -0
- package/package.json +8 -2
- package/scripts/fit-cmu-keystroke.mjs +186 -0
package/dist/index.d.ts
CHANGED
|
@@ -8,14 +8,23 @@ export { Logger } from './logging.js';
|
|
|
8
8
|
export { BrowserCore } from './browser-core.js';
|
|
9
9
|
export { fitDistribution, fitFittsLaw, fitMouseDynamics, fitTypingDynamics, fitFromSamples, deriveProfileConfig, } from './behavioral/calibration.js';
|
|
10
10
|
export type { MouseSample, MouseMovement, KeystrokeSample, TypingSession, DistributionFit, MouseFit, TypingFit, CalibratedProfile, } from './behavioral/calibration.js';
|
|
11
|
+
export { parseCmuKeystrokeCsv, parseBalabitMouseCsv, parseGenericTelemetryJson, CMU_PHRASE, } from './behavioral/parsers.js';
|
|
12
|
+
export { TlsSideChannel } from './tls-side-channel.js';
|
|
13
|
+
export type { TlsRequest, TlsResponse, ParsedCookie } from './tls-side-channel.js';
|
|
14
|
+
export { installTlsRewriter } from './tls-rewriter.js';
|
|
15
|
+
export type { TlsRewriterOptions, TlsRewriterStats } from './tls-rewriter.js';
|
|
16
|
+
export { solveAkamaiChallenge, parseAbckState } from './akamai-sensor.js';
|
|
17
|
+
export type { AkamaiChallengeResult } from './akamai-sensor.js';
|
|
18
|
+
export { IdentityPool } from './identity-pool.js';
|
|
19
|
+
export type { Identity, IdentityPoolOptions, RotationPolicy, DeviceProfileName, } from './identity-pool.js';
|
|
11
20
|
export { ProxyPool, ProxyProviders, proxyToUrl } from './proxy-pool.js';
|
|
12
21
|
export type { ProxyDescriptor, ProxyProtocol, PoolOptions } from './proxy-pool.js';
|
|
13
22
|
export { SnapshotManager } from './snapshot.js';
|
|
14
23
|
export type { SessionSnapshot } from './snapshot.js';
|
|
15
24
|
export { attachObservability, JsonlFileExporter, ConsoleExporter, newTraceId, } from './observability.js';
|
|
16
25
|
export type { StructuredEvent, EventExporter } from './observability.js';
|
|
17
|
-
export { captureFingerprint, checkIpReputation, testAgainstAkamai, } from './diagnostics.js';
|
|
18
|
-
export type { FingerprintSnapshot, IpReputationResult, AkamaiTestResult, } from './diagnostics.js';
|
|
26
|
+
export { captureFingerprint, checkIpReputation, testAgainstAkamai, testAgainstAntiBot, } from './diagnostics.js';
|
|
27
|
+
export type { FingerprintSnapshot, IpReputationResult, AkamaiTestResult, AntiBotTestResult, AntiBotVendor, } from './diagnostics.js';
|
|
19
28
|
export type { BlackTipConfig, ProfileConfig, DeviceProfile, PluginData, ActionResult, NavigateResult, ScreenshotResult, WaitResult, ActionEvent, BehavioralMetadata, ErrorEvent, RetryEvent, TabChangeEvent, LogEntry, TabInfo, FrameInfo, LogLevel, ClickOptions, TypeOptions, ScrollOptions, HoverOptions, SelectOptions, PressKeyOptions, UploadFileOptions, NavigateOptions, ScreenshotOptions, WaitForOptions, WaitForNavigationOptions, ExtractTextOptions, PageContentOptions, ErrorCode, RetryStrategy, Point, BoundingBox, } from './types.js';
|
|
20
29
|
export { ErrorCodes } from './types.js';
|
|
21
30
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC1F,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AACrG,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhD,OAAO,EACL,eAAe,EACf,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,mBAAmB,GACpB,MAAM,6BAA6B,CAAC;AACrC,YAAY,EACV,WAAW,EACX,aAAa,EACb,eAAe,EACf,aAAa,EACb,eAAe,EACf,QAAQ,EACR,SAAS,EACT,iBAAiB,GAClB,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC1F,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AACrG,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhD,OAAO,EACL,eAAe,EACf,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,mBAAmB,GACpB,MAAM,6BAA6B,CAAC;AACrC,YAAY,EACV,WAAW,EACX,aAAa,EACb,eAAe,EACf,aAAa,EACb,eAAe,EACf,QAAQ,EACR,SAAS,EACT,iBAAiB,GAClB,MAAM,6BAA6B,CAAC;AAGrC,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,yBAAyB,EACzB,UAAU,GACX,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAGnF,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,YAAY,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAG9E,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC1E,YAAY,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAGhE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,YAAY,EACV,QAAQ,EACR,mBAAmB,EACnB,cAAc,EACd,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACxE,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnF,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,eAAe,EACf,UAAU,GACX,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGzE,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,GACd,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EACV,cAAc,EACd,aAAa,EACb,aAAa,EACb,UAAU,EACV,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,WAAW,EACX,kBAAkB,EAClB,UAAU,EACV,UAAU,EACV,cAAc,EACd,QAAQ,EACR,OAAO,EACP,SAAS,EACT,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,aAAa,EACb,YAAY,EACZ,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,wBAAwB,EACxB,kBAAkB,EAClB,kBAAkB,EAClB,SAAS,EACT,aAAa,EACb,KAAK,EACL,WAAW,GACZ,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -7,10 +7,20 @@ export { Logger } from './logging.js';
|
|
|
7
7
|
export { BrowserCore } from './browser-core.js';
|
|
8
8
|
// v2 additions — Tier 2 calibration, Tier 3 infrastructure, observability.
|
|
9
9
|
export { fitDistribution, fitFittsLaw, fitMouseDynamics, fitTypingDynamics, fitFromSamples, deriveProfileConfig, } from './behavioral/calibration.js';
|
|
10
|
+
// v0.3.0 — dataset parsers for end-to-end calibration
|
|
11
|
+
export { parseCmuKeystrokeCsv, parseBalabitMouseCsv, parseGenericTelemetryJson, CMU_PHRASE, } from './behavioral/parsers.js';
|
|
12
|
+
// v0.3.0 — TLS side-channel via bogdanfinn/tls-client
|
|
13
|
+
export { TlsSideChannel } from './tls-side-channel.js';
|
|
14
|
+
// v0.5.0 — full TLS rewriting via CDP Fetch interception + Go daemon
|
|
15
|
+
export { installTlsRewriter } from './tls-rewriter.js';
|
|
16
|
+
// v0.5.0 — Akamai sensor challenge solver
|
|
17
|
+
export { solveAkamaiChallenge, parseAbckState } from './akamai-sensor.js';
|
|
18
|
+
// v0.4.0 — IdentityPool: long-running session and identity rotation
|
|
19
|
+
export { IdentityPool } from './identity-pool.js';
|
|
10
20
|
export { ProxyPool, ProxyProviders, proxyToUrl } from './proxy-pool.js';
|
|
11
21
|
export { SnapshotManager } from './snapshot.js';
|
|
12
22
|
export { attachObservability, JsonlFileExporter, ConsoleExporter, newTraceId, } from './observability.js';
|
|
13
23
|
// v0.2.0 — stealth diagnostics
|
|
14
|
-
export { captureFingerprint, checkIpReputation, testAgainstAkamai, } from './diagnostics.js';
|
|
24
|
+
export { captureFingerprint, checkIpReputation, testAgainstAkamai, testAgainstAntiBot, } from './diagnostics.js';
|
|
15
25
|
export { ErrorCodes } from './types.js';
|
|
16
26
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE1F,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,2EAA2E;AAC3E,OAAO,EACL,eAAe,EACf,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,mBAAmB,GACpB,MAAM,6BAA6B,CAAC;AAYrC,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGxE,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAGhD,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,eAAe,EACf,UAAU,GACX,MAAM,oBAAoB,CAAC;AAG5B,+BAA+B;AAC/B,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE1F,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,2EAA2E;AAC3E,OAAO,EACL,eAAe,EACf,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,mBAAmB,GACpB,MAAM,6BAA6B,CAAC;AAYrC,sDAAsD;AACtD,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,yBAAyB,EACzB,UAAU,GACX,MAAM,yBAAyB,CAAC;AAEjC,sDAAsD;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAGvD,qEAAqE;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAGvD,0CAA0C;AAC1C,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAG1E,oEAAoE;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAQlD,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGxE,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAGhD,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,eAAe,EACf,UAAU,GACX,MAAM,oBAAoB,CAAC;AAG5B,+BAA+B;AAC/B,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AA8C1B,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TLS rewriting via CDP Fetch interception.
|
|
3
|
+
*
|
|
4
|
+
* The v0.5.0 answer to "every wire request should present a real Chrome
|
|
5
|
+
* TLS fingerprint, not just the gating ones." Without a TCP-level MITM
|
|
6
|
+
* proxy (and the OS-specific cert installation hell that entails), we
|
|
7
|
+
* use Chrome DevTools Protocol's `Fetch.enable` to pause every HTTP
|
|
8
|
+
* request the browser issues, hand it to the Go `bogdanfinn/tls-client`
|
|
9
|
+
* daemon for upstream execution, and fulfill the response back through
|
|
10
|
+
* CDP. The browser never opens an upstream TCP connection — all its
|
|
11
|
+
* HTTP is fulfilled by us.
|
|
12
|
+
*
|
|
13
|
+
* Patchright/Playwright expose this as `context.route('**', handler)`,
|
|
14
|
+
* which is the high-level wrapper around CDP Fetch. We use the route
|
|
15
|
+
* handler so we don't need to manage CDP sessions ourselves.
|
|
16
|
+
*
|
|
17
|
+
* What this fixes vs the v0.3.0 side-channel (`bt.fetchWithTls`):
|
|
18
|
+
* - The side-channel only handled gating requests the caller made
|
|
19
|
+
* explicitly. Page subresources, XHR, fetch() from page JS, all
|
|
20
|
+
* went through Chrome's own TLS — meaning the host OS's Chrome
|
|
21
|
+
* fingerprint reached the wire. With this rewriter installed, every
|
|
22
|
+
* subresource also goes through Go.
|
|
23
|
+
* - Cross-platform UA spoofing is restored. The daemon controls every
|
|
24
|
+
* header on the wire; spoof to your heart's content.
|
|
25
|
+
*
|
|
26
|
+
* Known limitations:
|
|
27
|
+
* - WebSocket upgrades can't be intercepted by Fetch.enable. They
|
|
28
|
+
* bypass the rewriter and present Chrome's native TLS. The rewriter
|
|
29
|
+
* logs WS leaks for awareness.
|
|
30
|
+
* - Streaming responses are buffered fully. Bad for video, fine for
|
|
31
|
+
* HTML/JSON/typical web pages.
|
|
32
|
+
* - HTTP/3 (QUIC) requests bypass Fetch.enable entirely because Chrome
|
|
33
|
+
* short-circuits them. We launch with `--disable-quic` so this never
|
|
34
|
+
* fires in practice.
|
|
35
|
+
*/
|
|
36
|
+
import type { BrowserContext } from 'patchright';
|
|
37
|
+
import type { TlsSideChannel } from './tls-side-channel.js';
|
|
38
|
+
import type { Logger } from './logging.js';
|
|
39
|
+
export interface TlsRewriterOptions {
|
|
40
|
+
/** TLS daemon spawned by `TlsSideChannel.spawn()`. The rewriter does
|
|
41
|
+
* not own its lifecycle — caller is responsible for `close()`. */
|
|
42
|
+
channel: TlsSideChannel;
|
|
43
|
+
logger: Logger;
|
|
44
|
+
/** Hard timeout per request to the daemon. Default 30s. */
|
|
45
|
+
perRequestTimeoutMs?: number;
|
|
46
|
+
/** Profile name passed to the daemon for every request. Defaults to
|
|
47
|
+
* `chrome_133`. Cross-platform spoofing happens by overriding this
|
|
48
|
+
* per-launch (e.g. `chrome_124`) plus the User-Agent header. */
|
|
49
|
+
profile?: string;
|
|
50
|
+
}
|
|
51
|
+
export interface TlsRewriterStats {
|
|
52
|
+
/** Total requests intercepted. */
|
|
53
|
+
intercepted: number;
|
|
54
|
+
/** Requests fulfilled successfully via the daemon. */
|
|
55
|
+
fulfilled: number;
|
|
56
|
+
/** Requests that fell through to `route.continue()` because of an
|
|
57
|
+
* error in the daemon path (e.g. daemon crashed, upstream timeout). */
|
|
58
|
+
fellThrough: number;
|
|
59
|
+
/** WebSocket upgrade requests we saw but couldn't intercept. */
|
|
60
|
+
webSocketLeaks: number;
|
|
61
|
+
/** Average daemon round-trip in ms. */
|
|
62
|
+
avgDurationMs: number;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Install the TLS rewriter on a Playwright BrowserContext. After this
|
|
66
|
+
* call, every HTTP/HTTPS request the browser issues is intercepted and
|
|
67
|
+
* forwarded through the TLS daemon. Returns a `stats()` accessor and an
|
|
68
|
+
* `uninstall()` callback.
|
|
69
|
+
*/
|
|
70
|
+
export declare function installTlsRewriter(context: BrowserContext, options: TlsRewriterOptions): Promise<{
|
|
71
|
+
stats: () => TlsRewriterStats;
|
|
72
|
+
uninstall: () => Promise<void>;
|
|
73
|
+
}>;
|
|
74
|
+
//# sourceMappingURL=tls-rewriter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tls-rewriter.d.ts","sourceRoot":"","sources":["../src/tls-rewriter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAuC,MAAM,YAAY,CAAC;AACtF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAmC3C,MAAM,WAAW,kBAAkB;IACjC;uEACmE;IACnE,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;qEAEiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,kCAAkC;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,SAAS,EAAE,MAAM,CAAC;IAClB;4EACwE;IACxE,WAAW,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,cAAc,EAAE,MAAM,CAAC;IACvB,uCAAuC;IACvC,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC;IACT,KAAK,EAAE,MAAM,gBAAgB,CAAC;IAC9B,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAChC,CAAC,CAqID"}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TLS rewriting via CDP Fetch interception.
|
|
3
|
+
*
|
|
4
|
+
* The v0.5.0 answer to "every wire request should present a real Chrome
|
|
5
|
+
* TLS fingerprint, not just the gating ones." Without a TCP-level MITM
|
|
6
|
+
* proxy (and the OS-specific cert installation hell that entails), we
|
|
7
|
+
* use Chrome DevTools Protocol's `Fetch.enable` to pause every HTTP
|
|
8
|
+
* request the browser issues, hand it to the Go `bogdanfinn/tls-client`
|
|
9
|
+
* daemon for upstream execution, and fulfill the response back through
|
|
10
|
+
* CDP. The browser never opens an upstream TCP connection — all its
|
|
11
|
+
* HTTP is fulfilled by us.
|
|
12
|
+
*
|
|
13
|
+
* Patchright/Playwright expose this as `context.route('**', handler)`,
|
|
14
|
+
* which is the high-level wrapper around CDP Fetch. We use the route
|
|
15
|
+
* handler so we don't need to manage CDP sessions ourselves.
|
|
16
|
+
*
|
|
17
|
+
* What this fixes vs the v0.3.0 side-channel (`bt.fetchWithTls`):
|
|
18
|
+
* - The side-channel only handled gating requests the caller made
|
|
19
|
+
* explicitly. Page subresources, XHR, fetch() from page JS, all
|
|
20
|
+
* went through Chrome's own TLS — meaning the host OS's Chrome
|
|
21
|
+
* fingerprint reached the wire. With this rewriter installed, every
|
|
22
|
+
* subresource also goes through Go.
|
|
23
|
+
* - Cross-platform UA spoofing is restored. The daemon controls every
|
|
24
|
+
* header on the wire; spoof to your heart's content.
|
|
25
|
+
*
|
|
26
|
+
* Known limitations:
|
|
27
|
+
* - WebSocket upgrades can't be intercepted by Fetch.enable. They
|
|
28
|
+
* bypass the rewriter and present Chrome's native TLS. The rewriter
|
|
29
|
+
* logs WS leaks for awareness.
|
|
30
|
+
* - Streaming responses are buffered fully. Bad for video, fine for
|
|
31
|
+
* HTML/JSON/typical web pages.
|
|
32
|
+
* - HTTP/3 (QUIC) requests bypass Fetch.enable entirely because Chrome
|
|
33
|
+
* short-circuits them. We launch with `--disable-quic` so this never
|
|
34
|
+
* fires in practice.
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* Headers that Chrome's request lifecycle manages itself and that we
|
|
38
|
+
* MUST NOT pass through verbatim — re-sending them through the daemon
|
|
39
|
+
* either breaks the request (Content-Length) or duplicates state
|
|
40
|
+
* (Cookie, which the daemon will set automatically from the upstream's
|
|
41
|
+
* Set-Cookie response, while the browser's own cookie jar handles the
|
|
42
|
+
* reverse direction). The browser cookie jar IS the source of truth;
|
|
43
|
+
* we forward Cookie verbatim and let Set-Cookie come back via fulfill.
|
|
44
|
+
*/
|
|
45
|
+
const STRIP_REQUEST_HEADERS = new Set([
|
|
46
|
+
'host',
|
|
47
|
+
'content-length',
|
|
48
|
+
'connection',
|
|
49
|
+
'keep-alive',
|
|
50
|
+
'transfer-encoding',
|
|
51
|
+
'proxy-authorization',
|
|
52
|
+
'proxy-connection',
|
|
53
|
+
'upgrade',
|
|
54
|
+
'expect',
|
|
55
|
+
]);
|
|
56
|
+
/**
|
|
57
|
+
* Headers Chrome's response lifecycle re-computes and that we should
|
|
58
|
+
* NOT pass back via fulfill — letting them through breaks framing.
|
|
59
|
+
*/
|
|
60
|
+
const STRIP_RESPONSE_HEADERS = new Set([
|
|
61
|
+
'content-length',
|
|
62
|
+
'content-encoding', // upstream returns gzip; we hand fulfill the decoded body
|
|
63
|
+
'transfer-encoding',
|
|
64
|
+
'connection',
|
|
65
|
+
'keep-alive',
|
|
66
|
+
]);
|
|
67
|
+
/**
|
|
68
|
+
* Install the TLS rewriter on a Playwright BrowserContext. After this
|
|
69
|
+
* call, every HTTP/HTTPS request the browser issues is intercepted and
|
|
70
|
+
* forwarded through the TLS daemon. Returns a `stats()` accessor and an
|
|
71
|
+
* `uninstall()` callback.
|
|
72
|
+
*/
|
|
73
|
+
export async function installTlsRewriter(context, options) {
|
|
74
|
+
const { channel, logger } = options;
|
|
75
|
+
const profile = options.profile ?? 'chrome_133';
|
|
76
|
+
const perRequestTimeoutMs = options.perRequestTimeoutMs ?? 30_000;
|
|
77
|
+
let intercepted = 0;
|
|
78
|
+
let fulfilled = 0;
|
|
79
|
+
let fellThrough = 0;
|
|
80
|
+
let webSocketLeaks = 0;
|
|
81
|
+
let totalDurationMs = 0;
|
|
82
|
+
const handler = async (route, request) => {
|
|
83
|
+
intercepted++;
|
|
84
|
+
const url = request.url();
|
|
85
|
+
const method = request.method();
|
|
86
|
+
// WebSocket upgrades come through `route` but Fetch can't intercept
|
|
87
|
+
// the upgrade itself — Chrome handles WS frames at a layer we can't
|
|
88
|
+
// see from here. Let them pass through and log a warning.
|
|
89
|
+
if (request.isNavigationRequest() && (url.startsWith('ws://') || url.startsWith('wss://'))) {
|
|
90
|
+
webSocketLeaks++;
|
|
91
|
+
logger.warn('TLS rewriter: WebSocket leak — upgrade bypasses the rewriter', { url });
|
|
92
|
+
await route.continue();
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const upgradeHeader = request.headers()['upgrade'];
|
|
96
|
+
if (upgradeHeader && upgradeHeader.toLowerCase() === 'websocket') {
|
|
97
|
+
webSocketLeaks++;
|
|
98
|
+
logger.warn('TLS rewriter: WebSocket leak — upgrade bypasses the rewriter', { url });
|
|
99
|
+
await route.continue();
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
// Build the daemon request from the browser-side request.
|
|
103
|
+
const reqHeaders = {};
|
|
104
|
+
for (const [key, value] of Object.entries(request.headers())) {
|
|
105
|
+
if (STRIP_REQUEST_HEADERS.has(key.toLowerCase()))
|
|
106
|
+
continue;
|
|
107
|
+
reqHeaders[key] = value;
|
|
108
|
+
}
|
|
109
|
+
const postBuffer = request.postDataBuffer();
|
|
110
|
+
const body = postBuffer ?? undefined;
|
|
111
|
+
try {
|
|
112
|
+
const resp = await Promise.race([
|
|
113
|
+
channel.fetch({
|
|
114
|
+
url,
|
|
115
|
+
method,
|
|
116
|
+
headers: reqHeaders,
|
|
117
|
+
body,
|
|
118
|
+
profile,
|
|
119
|
+
timeoutMs: perRequestTimeoutMs,
|
|
120
|
+
}),
|
|
121
|
+
new Promise((_resolve, reject) => {
|
|
122
|
+
setTimeout(() => reject(new Error(`TLS rewriter: per-request timeout ${perRequestTimeoutMs}ms`)), perRequestTimeoutMs + 1000).unref();
|
|
123
|
+
}),
|
|
124
|
+
]);
|
|
125
|
+
totalDurationMs += resp.durationMs;
|
|
126
|
+
// Flatten response headers. Multi-valued headers (e.g. Set-Cookie)
|
|
127
|
+
// need special handling: Playwright's fulfill takes a single string
|
|
128
|
+
// per key, but lets you pass an array via the headers parameter
|
|
129
|
+
// since 1.50 — we use the comma-join fallback for older versions.
|
|
130
|
+
// For Set-Cookie specifically, we use the multi-value extension
|
|
131
|
+
// because cookies must not be merged.
|
|
132
|
+
const respHeaders = {};
|
|
133
|
+
for (const [key, values] of Object.entries(resp.headers)) {
|
|
134
|
+
if (STRIP_RESPONSE_HEADERS.has(key.toLowerCase()))
|
|
135
|
+
continue;
|
|
136
|
+
if (values.length === 1) {
|
|
137
|
+
respHeaders[key] = values[0];
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
// For Set-Cookie, joining with comma is wrong (cookies have
|
|
141
|
+
// their own commas in Expires). Playwright's `fulfill` accepts
|
|
142
|
+
// multiValueHeaders via the headers field as Record<string, string>
|
|
143
|
+
// by joining with `\n` for some headers. Safest fallback: pass
|
|
144
|
+
// each Set-Cookie as a separate header by using the array form
|
|
145
|
+
// if available, otherwise the last cookie wins.
|
|
146
|
+
//
|
|
147
|
+
// Since we can't pass arrays directly to Playwright's fulfill
|
|
148
|
+
// headers, we encode the multi-value as `\n`-separated for
|
|
149
|
+
// Set-Cookie (Chrome accepts this) and comma-join for everything
|
|
150
|
+
// else.
|
|
151
|
+
if (key.toLowerCase() === 'set-cookie') {
|
|
152
|
+
respHeaders[key] = values.join('\n');
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
respHeaders[key] = values.join(', ');
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
await route.fulfill({
|
|
160
|
+
status: resp.status,
|
|
161
|
+
headers: respHeaders,
|
|
162
|
+
body: resp.bodyBuffer,
|
|
163
|
+
});
|
|
164
|
+
fulfilled++;
|
|
165
|
+
}
|
|
166
|
+
catch (err) {
|
|
167
|
+
fellThrough++;
|
|
168
|
+
logger.warn('TLS rewriter: daemon path failed, falling through to native fetch', {
|
|
169
|
+
url,
|
|
170
|
+
error: err instanceof Error ? err.message : String(err),
|
|
171
|
+
});
|
|
172
|
+
// Fall through to the browser's native request. Less stealthy but
|
|
173
|
+
// doesn't break the page.
|
|
174
|
+
try {
|
|
175
|
+
await route.continue();
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
// Route may already be fulfilled/aborted in race conditions; ignore.
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
// Install the route handler for all URLs. Patchright/Playwright accept
|
|
183
|
+
// a glob ('**/*') or RegExp; we use the glob for clarity.
|
|
184
|
+
await context.route('**/*', handler);
|
|
185
|
+
return {
|
|
186
|
+
stats: () => ({
|
|
187
|
+
intercepted,
|
|
188
|
+
fulfilled,
|
|
189
|
+
fellThrough,
|
|
190
|
+
webSocketLeaks,
|
|
191
|
+
avgDurationMs: fulfilled > 0 ? Math.round(totalDurationMs / fulfilled) : 0,
|
|
192
|
+
}),
|
|
193
|
+
uninstall: async () => {
|
|
194
|
+
try {
|
|
195
|
+
await context.unroute('**/*', handler);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
// Context may be closing; ignore.
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=tls-rewriter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tls-rewriter.js","sourceRoot":"","sources":["../src/tls-rewriter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TLS side-channel — spawns the Go-based bogdanfinn/tls-client daemon
|
|
3
|
+
* and exposes a `fetchWithTls()` primitive that performs HTTP requests
|
|
4
|
+
* with a real Chrome TLS ClientHello, real H2 frame settings, and real
|
|
5
|
+
* H2 frame order. Used to make gating requests that the browser can't
|
|
6
|
+
* make through itself, then inject the resulting cookies into the
|
|
7
|
+
* BlackTip browser session.
|
|
8
|
+
*
|
|
9
|
+
* This is the v0.3.0 answer to "an edge gates the very first request
|
|
10
|
+
* before BlackTip's browser even has a session." Use it as follows:
|
|
11
|
+
*
|
|
12
|
+
* const channel = await TlsSideChannel.spawn();
|
|
13
|
+
* const resp = await channel.fetch('https://protected.example.com/');
|
|
14
|
+
* await bt.setCookies(resp.cookies.map(c => ({ name: c.name, value: c.value, domain: c.domain, path: c.path })));
|
|
15
|
+
* await bt.navigate('https://protected.example.com/');
|
|
16
|
+
* // ... browser session now has the cookies the gating request earned
|
|
17
|
+
* await channel.close();
|
|
18
|
+
*
|
|
19
|
+
* The daemon binary lives in `native/tls-client/blacktip-tls` (Linux/macOS)
|
|
20
|
+
* or `native/tls-client/blacktip-tls.exe` (Windows). Build it once with
|
|
21
|
+
* `cd native/tls-client && go build -o blacktip-tls .` — Go is the only
|
|
22
|
+
* build dependency, and you can grab it from https://go.dev/dl/.
|
|
23
|
+
*
|
|
24
|
+
* The daemon stays alive across many requests so we don't pay subprocess
|
|
25
|
+
* startup cost per call. It uses a newline-delimited JSON wire protocol
|
|
26
|
+
* with per-request IDs, so multiple `fetch()` calls can be in flight
|
|
27
|
+
* concurrently without interleaving issues.
|
|
28
|
+
*/
|
|
29
|
+
export interface TlsRequest {
|
|
30
|
+
url: string;
|
|
31
|
+
method?: string;
|
|
32
|
+
headers?: Record<string, string>;
|
|
33
|
+
/** Request body as a UTF-8 string OR raw Buffer. Buffer is required for
|
|
34
|
+
* binary uploads (multipart, octet-stream); string is fine for form
|
|
35
|
+
* bodies and JSON. Encoded as base64 on the wire. */
|
|
36
|
+
body?: string | Buffer;
|
|
37
|
+
timeoutMs?: number;
|
|
38
|
+
/** Chrome / Firefox / Safari profile name; defaults to chrome_133. */
|
|
39
|
+
profile?: string;
|
|
40
|
+
}
|
|
41
|
+
export interface TlsResponse {
|
|
42
|
+
status: number;
|
|
43
|
+
/** Headers from the upstream response. Multi-valued — Set-Cookie commonly
|
|
44
|
+
* has multiple entries. Header keys preserve the casing the upstream sent. */
|
|
45
|
+
headers: Record<string, string[]>;
|
|
46
|
+
/** Response body as a UTF-8 string. May be garbage for binary content;
|
|
47
|
+
* use `bodyBuffer` for that. Kept as the primary body field for callers
|
|
48
|
+
* that just want JSON / HTML. */
|
|
49
|
+
body: string;
|
|
50
|
+
/** Response body as raw bytes. Use this when the upstream returns binary
|
|
51
|
+
* data (images, fonts, video, anything non-UTF-8). The TLS rewriting
|
|
52
|
+
* route handler always uses this so subresources don't get mangled. */
|
|
53
|
+
bodyBuffer: Buffer;
|
|
54
|
+
finalUrl: string;
|
|
55
|
+
durationMs: number;
|
|
56
|
+
/** Cookies parsed from `Set-Cookie` headers, ready to inject via `bt.setCookies()`. */
|
|
57
|
+
cookies: ParsedCookie[];
|
|
58
|
+
}
|
|
59
|
+
export interface ParsedCookie {
|
|
60
|
+
name: string;
|
|
61
|
+
value: string;
|
|
62
|
+
domain: string;
|
|
63
|
+
path: string;
|
|
64
|
+
httpOnly: boolean;
|
|
65
|
+
secure: boolean;
|
|
66
|
+
sameSite?: 'Strict' | 'Lax' | 'None';
|
|
67
|
+
expires?: number;
|
|
68
|
+
}
|
|
69
|
+
export declare class TlsSideChannel {
|
|
70
|
+
private proc;
|
|
71
|
+
private rl;
|
|
72
|
+
private pending;
|
|
73
|
+
private nextId;
|
|
74
|
+
private closed;
|
|
75
|
+
private constructor();
|
|
76
|
+
/**
|
|
77
|
+
* Spawn the daemon. Throws if the binary isn't built or can't start.
|
|
78
|
+
* The daemon stays alive until you call `close()`.
|
|
79
|
+
*/
|
|
80
|
+
static spawn(): Promise<TlsSideChannel>;
|
|
81
|
+
private handleLine;
|
|
82
|
+
/**
|
|
83
|
+
* Perform a single TLS-impersonated request. Multiple `fetch()` calls
|
|
84
|
+
* can be in flight concurrently — the daemon handles each in its own
|
|
85
|
+
* goroutine and matches responses by id.
|
|
86
|
+
*/
|
|
87
|
+
fetch(req: TlsRequest): Promise<TlsResponse>;
|
|
88
|
+
/** Shut down the daemon and clean up the subprocess. */
|
|
89
|
+
close(): Promise<void>;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=tls-side-channel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tls-side-channel.d.ts","sourceRoot":"","sources":["../src/tls-side-channel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAUH,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC;;0DAEsD;IACtD,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf;mFAC+E;IAC/E,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAClC;;sCAEkC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb;;4EAEwE;IACxE,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,uFAAuF;IACvF,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAoGD,qBAAa,cAAc;IACzB,OAAO,CAAC,IAAI,CAAiC;IAC7C,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,MAAM,CAAS;IAEvB,OAAO;IAYP;;;OAGG;WACU,KAAK,IAAI,OAAO,CAAC,cAAc,CAAC;IAU7C,OAAO,CAAC,UAAU;IAgDlB;;;;OAIG;IACG,KAAK,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC;IA4BlD,wDAAwD;IAClD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAc7B"}
|