@rester159/blacktip 0.1.0 → 0.4.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.
Files changed (42) hide show
  1. package/CHANGELOG.md +190 -0
  2. package/README.md +95 -0
  3. package/dist/behavioral/parsers.d.ts +89 -0
  4. package/dist/behavioral/parsers.d.ts.map +1 -0
  5. package/dist/behavioral/parsers.js +223 -0
  6. package/dist/behavioral/parsers.js.map +1 -0
  7. package/dist/blacktip.d.ts +86 -0
  8. package/dist/blacktip.d.ts.map +1 -1
  9. package/dist/blacktip.js +193 -0
  10. package/dist/blacktip.js.map +1 -1
  11. package/dist/browser-core.d.ts.map +1 -1
  12. package/dist/browser-core.js +125 -33
  13. package/dist/browser-core.js.map +1 -1
  14. package/dist/diagnostics.d.ts +150 -0
  15. package/dist/diagnostics.d.ts.map +1 -0
  16. package/dist/diagnostics.js +389 -0
  17. package/dist/diagnostics.js.map +1 -0
  18. package/dist/identity-pool.d.ts +160 -0
  19. package/dist/identity-pool.d.ts.map +1 -0
  20. package/dist/identity-pool.js +288 -0
  21. package/dist/identity-pool.js.map +1 -0
  22. package/dist/index.d.ts +7 -0
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +8 -0
  25. package/dist/index.js.map +1 -1
  26. package/dist/tls-side-channel.d.ts +82 -0
  27. package/dist/tls-side-channel.d.ts.map +1 -0
  28. package/dist/tls-side-channel.js +241 -0
  29. package/dist/tls-side-channel.js.map +1 -0
  30. package/dist/types.d.ts +26 -0
  31. package/dist/types.d.ts.map +1 -1
  32. package/dist/types.js.map +1 -1
  33. package/docs/akamai-bypass.md +257 -0
  34. package/docs/anti-bot-validation.md +84 -0
  35. package/docs/calibration-validation.md +93 -0
  36. package/docs/identity-pool.md +176 -0
  37. package/docs/tls-side-channel.md +83 -0
  38. package/native/tls-client/go.mod +21 -0
  39. package/native/tls-client/go.sum +36 -0
  40. package/native/tls-client/main.go +216 -0
  41. package/package.json +8 -2
  42. package/scripts/fit-cmu-keystroke.mjs +186 -0
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Diagnostic primitives for stealth validation.
3
+ *
4
+ * Captures the actual TLS / HTTP2 / HTTP header fingerprint that BlackTip
5
+ * is sending, and queries free IP reputation services to score the
6
+ * current network. Used to diagnose why a specific anti-bot target is
7
+ * blocking us — see docs/akamai-bypass.md for the full methodology.
8
+ *
9
+ * The primitives here run via the active BlackTip browser session — they
10
+ * don't make HTTP requests directly from Node, because the whole point
11
+ * is to capture what the browser actually sends, not what Node would send.
12
+ */
13
+ import type { BlackTip } from './blacktip.js';
14
+ export interface FingerprintSnapshot {
15
+ capturedAt: string;
16
+ ip: string | null;
17
+ tls: {
18
+ ja3: string | null;
19
+ ja3Hash: string | null;
20
+ ja4: string | null;
21
+ firstCipher: string | null;
22
+ cipherCount: number | null;
23
+ firstExtension: string | null;
24
+ extensionCount: number | null;
25
+ tlsVersion: string | null;
26
+ /** True if first cipher is GREASE — Chrome's signature */
27
+ hasGreaseCipher: boolean;
28
+ /** True if first extension is GREASE — Chrome's signature */
29
+ hasGreaseExtension: boolean;
30
+ /** True if JA4 starts with t13d — TLS 1.3 with Chrome's count signature */
31
+ isChromeLikeJa4: boolean;
32
+ };
33
+ http2: {
34
+ akamaiFingerprint: string | null;
35
+ akamaiFingerprintHash: string | null;
36
+ /** Sequence of frame types sent by the client (Chrome: SETTINGS, WINDOW_UPDATE, HEADERS) */
37
+ sentFrames: string[] | null;
38
+ };
39
+ headers: {
40
+ userAgent: string | null;
41
+ secChUa: string | null;
42
+ secChUaMobile: string | null;
43
+ secChUaPlatform: string | null;
44
+ acceptLanguage: string | null;
45
+ acceptEncoding: string | null;
46
+ secFetchSite: string | null;
47
+ secFetchMode: string | null;
48
+ secFetchDest: string | null;
49
+ secFetchUser: string | null;
50
+ upgradeInsecureRequests: string | null;
51
+ /** Chrome version parsed from User-Agent (e.g. 125 from "Chrome/125.0.0.0") */
52
+ uaChromeVersion: number | null;
53
+ /** Chrome version parsed from Sec-Ch-Ua (e.g. 146 from `"Google Chrome";v="146"`) */
54
+ secChUaChromeVersion: number | null;
55
+ /**
56
+ * THE CRITICAL CHECK: do User-Agent and Sec-Ch-Ua report the same Chrome
57
+ * version? If false, Akamai / DataDome / PerimeterX will catch you. This
58
+ * is the L016 fingerprint consistency signal.
59
+ */
60
+ uaConsistent: boolean;
61
+ };
62
+ }
63
+ export interface IpReputationResult {
64
+ ip: string | null;
65
+ hostname: string | null;
66
+ asn: string | null;
67
+ org: string | null;
68
+ city: string | null;
69
+ region: string | null;
70
+ country: string | null;
71
+ loc: string | null;
72
+ timezone: string | null;
73
+ /** Heuristic: ASN matches a well-known datacenter / cloud provider */
74
+ isDatacenter: boolean;
75
+ /** Heuristic: ASN matches a residential ISP */
76
+ isResidential: boolean;
77
+ /** Free-form notes from the heuristic checks */
78
+ notes: string[];
79
+ }
80
+ export interface AkamaiTestResult {
81
+ url: string;
82
+ /** True if the page loaded normally (not the Akamai Access Denied error) */
83
+ passed: boolean;
84
+ finalUrl: string;
85
+ title: string;
86
+ /** Akamai's reference number from the block page (null if not blocked) */
87
+ akamaiReference: string | null;
88
+ /** First 300 chars of body — useful for diagnosis */
89
+ bodyPreview: string;
90
+ /** Suggested next step based on what we observed */
91
+ suggestion: string;
92
+ durationMs: number;
93
+ }
94
+ /** Anti-bot vendors that BlackTip's diagnostics can recognize. */
95
+ export type AntiBotVendor = 'akamai' | 'datadome' | 'cloudflare' | 'perimeterx' | 'imperva' | 'kasada' | 'arkose' | 'unknown';
96
+ export interface AntiBotTestResult {
97
+ url: string;
98
+ /** True if the page loaded normally (no recognised challenge or block) */
99
+ passed: boolean;
100
+ finalUrl: string;
101
+ title: string;
102
+ /** Vendors that left a tell on the rendered page (block OR challenge interstitial) */
103
+ detectedVendors: AntiBotVendor[];
104
+ /** Vendor signatures observed even on a passing page (cookies, scripts, headers) */
105
+ vendorSignals: {
106
+ vendor: AntiBotVendor;
107
+ signal: string;
108
+ }[];
109
+ /** Akamai reference number, if a block from Akamai */
110
+ akamaiReference: string | null;
111
+ /** First 300 chars of body — useful for diagnosis */
112
+ bodyPreview: string;
113
+ /** Suggested next step based on what we observed */
114
+ suggestion: string;
115
+ durationMs: number;
116
+ }
117
+ /**
118
+ * Capture the active session's TLS, HTTP/2, and HTTP header fingerprint
119
+ * by navigating to tls.peet.ws/api/all and httpbin.org/headers.
120
+ *
121
+ * Returns a structured snapshot you can use to verify your stealth state
122
+ * before hitting a real target. The most important field is
123
+ * `headers.uaConsistent` — if that's false, you're on a pre-v0.2.0 build
124
+ * with the L016 bug.
125
+ */
126
+ export declare function captureFingerprint(bt: BlackTip): Promise<FingerprintSnapshot>;
127
+ /**
128
+ * Query the active session's egress IP and ASN, score it against known
129
+ * datacenter / residential patterns, and return a structured result.
130
+ *
131
+ * Uses the free ipinfo.io endpoint which doesn't require an API key for
132
+ * basic queries. Doesn't query commercial reputation services (those
133
+ * require API keys); for those, integrate at the caller's level.
134
+ */
135
+ export declare function checkIpReputation(bt: BlackTip): Promise<IpReputationResult>;
136
+ /**
137
+ * Visit an Akamai-protected URL and report the result with diagnosis.
138
+ * Recognizes the Akamai Access Denied error page format and extracts
139
+ * the reference number for triage.
140
+ */
141
+ export declare function testAgainstAkamai(bt: BlackTip, url: string): Promise<AkamaiTestResult>;
142
+ /**
143
+ * Visit a URL and report whether any major anti-bot vendor served a
144
+ * challenge or block. Recognises Akamai, DataDome, Cloudflare, PerimeterX/HUMAN,
145
+ * Imperva, Kasada and Arkose. Captures vendor signals (cookies, scripts) even
146
+ * on a passing page so you can verify a target is actually protected and
147
+ * BlackTip is sliding past it — not a false negative on an unprotected URL.
148
+ */
149
+ export declare function testAgainstAntiBot(bt: BlackTip, url: string): Promise<AntiBotTestResult>;
150
+ //# sourceMappingURL=diagnostics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,389 @@
1
+ /**
2
+ * Diagnostic primitives for stealth validation.
3
+ *
4
+ * Captures the actual TLS / HTTP2 / HTTP header fingerprint that BlackTip
5
+ * is sending, and queries free IP reputation services to score the
6
+ * current network. Used to diagnose why a specific anti-bot target is
7
+ * blocking us — see docs/akamai-bypass.md for the full methodology.
8
+ *
9
+ * The primitives here run via the active BlackTip browser session — they
10
+ * don't make HTTP requests directly from Node, because the whole point
11
+ * is to capture what the browser actually sends, not what Node would send.
12
+ */
13
+ // ── Datacenter ASN heuristics ──
14
+ //
15
+ // Not exhaustive — just covers the major cloud providers and known
16
+ // datacenter ranges. If your IP is on one of these, Akamai will almost
17
+ // certainly flag it. Add to this list as new providers come up.
18
+ const DATACENTER_ASN_PATTERNS = [
19
+ { pattern: /AS16509|AS14618/, name: 'Amazon AWS' },
20
+ { pattern: /AS15169/, name: 'Google Cloud' },
21
+ { pattern: /AS8075/, name: 'Microsoft Azure' },
22
+ { pattern: /AS16276/, name: 'OVH' },
23
+ { pattern: /AS14061/, name: 'DigitalOcean' },
24
+ { pattern: /AS20473/, name: 'Choopa / Vultr' },
25
+ { pattern: /AS24940/, name: 'Hetzner' },
26
+ { pattern: /AS63949/, name: 'Linode / Akamai (compute)' },
27
+ { pattern: /AS133752/, name: 'Leaseweb' },
28
+ { pattern: /AS54641|AS19551/, name: 'Cloudflare (compute)' },
29
+ { pattern: /AS396982/, name: 'Google Cloud Platform (compute)' },
30
+ { pattern: /AS200600/, name: 'Aeza Group (cheap VPS)' },
31
+ ];
32
+ const RESIDENTIAL_ASN_PATTERNS = [
33
+ { pattern: /AS5650|AS22773/, name: 'Frontier Communications / Cox (residential US)' },
34
+ { pattern: /AS7922/, name: 'Comcast (residential US)' },
35
+ { pattern: /AS7018/, name: 'AT&T (residential US)' },
36
+ { pattern: /AS20057|AS20115/, name: 'Charter / Spectrum (residential US)' },
37
+ { pattern: /AS6128/, name: 'Optimum / Cablevision (residential US)' },
38
+ { pattern: /AS33363/, name: 'BHN / Spectrum (residential US)' },
39
+ { pattern: /AS5089/, name: 'Virgin Media (residential UK)' },
40
+ { pattern: /AS3320/, name: 'Deutsche Telekom (residential DE)' },
41
+ { pattern: /AS9121/, name: 'Türk Telekom (residential TR)' },
42
+ ];
43
+ // ── captureFingerprint ──
44
+ /**
45
+ * Capture the active session's TLS, HTTP/2, and HTTP header fingerprint
46
+ * by navigating to tls.peet.ws/api/all and httpbin.org/headers.
47
+ *
48
+ * Returns a structured snapshot you can use to verify your stealth state
49
+ * before hitting a real target. The most important field is
50
+ * `headers.uaConsistent` — if that's false, you're on a pre-v0.2.0 build
51
+ * with the L016 bug.
52
+ */
53
+ export async function captureFingerprint(bt) {
54
+ // Step 1: TLS + HTTP/2 fingerprint via tls.peet.ws
55
+ await bt.navigate('https://tls.peet.ws/api/all');
56
+ const peetRaw = (await bt.executeJS('document.body.innerText'));
57
+ const peet = JSON.parse(peetRaw);
58
+ // Step 2: HTTP headers via httpbin.org/headers
59
+ await bt.navigate('https://httpbin.org/headers');
60
+ const hbRaw = (await bt.executeJS('document.body.innerText'));
61
+ const hb = JSON.parse(hbRaw);
62
+ const headers = hb.headers ?? {};
63
+ const userAgent = headers['User-Agent'] ?? null;
64
+ const secChUa = headers['Sec-Ch-Ua'] ?? null;
65
+ // Parse Chrome version from User-Agent: "...Chrome/125.0.0.0..."
66
+ const uaMatch = userAgent ? userAgent.match(/Chrome\/(\d+)/) : null;
67
+ const uaChromeVersion = uaMatch ? parseInt(uaMatch[1], 10) : null;
68
+ // Parse Chrome version from Sec-Ch-Ua: `"Google Chrome";v="146"`
69
+ const chuaMatch = secChUa ? secChUa.match(/"Google Chrome";v="(\d+)/) : null;
70
+ const secChUaChromeVersion = chuaMatch ? parseInt(chuaMatch[1], 10) : null;
71
+ const uaConsistent = uaChromeVersion != null &&
72
+ secChUaChromeVersion != null &&
73
+ uaChromeVersion === secChUaChromeVersion;
74
+ const tlsBlock = peet.tls ?? {};
75
+ const ciphers = tlsBlock.ciphers ?? [];
76
+ const extensions = tlsBlock.extensions ?? [];
77
+ const ja4 = tlsBlock.ja4 ?? null;
78
+ return {
79
+ capturedAt: new Date().toISOString(),
80
+ ip: peet.ip ? peet.ip.split(':')[0] ?? null : null,
81
+ tls: {
82
+ ja3: tlsBlock.ja3 ?? null,
83
+ ja3Hash: tlsBlock.ja3_hash ?? null,
84
+ ja4,
85
+ firstCipher: ciphers[0] ?? null,
86
+ cipherCount: ciphers.length || null,
87
+ firstExtension: extensions[0]?.name ?? null,
88
+ extensionCount: extensions.length || null,
89
+ tlsVersion: tlsBlock.tls_version_negotiated ?? null,
90
+ hasGreaseCipher: !!(ciphers[0] && /GREASE/i.test(ciphers[0])),
91
+ hasGreaseExtension: !!(extensions[0]?.name && /GREASE/i.test(extensions[0].name)),
92
+ isChromeLikeJa4: !!(ja4 && /^t13d/.test(ja4)),
93
+ },
94
+ http2: {
95
+ akamaiFingerprint: peet.http2?.akamai_fingerprint ?? null,
96
+ akamaiFingerprintHash: peet.http2?.akamai_fingerprint_hash ?? null,
97
+ sentFrames: peet.http2?.sent_frames?.map((f) => f.frame_type ?? '') ?? null,
98
+ },
99
+ headers: {
100
+ userAgent,
101
+ secChUa,
102
+ secChUaMobile: headers['Sec-Ch-Ua-Mobile'] ?? null,
103
+ secChUaPlatform: headers['Sec-Ch-Ua-Platform'] ?? null,
104
+ acceptLanguage: headers['Accept-Language'] ?? null,
105
+ acceptEncoding: headers['Accept-Encoding'] ?? null,
106
+ secFetchSite: headers['Sec-Fetch-Site'] ?? null,
107
+ secFetchMode: headers['Sec-Fetch-Mode'] ?? null,
108
+ secFetchDest: headers['Sec-Fetch-Dest'] ?? null,
109
+ secFetchUser: headers['Sec-Fetch-User'] ?? null,
110
+ upgradeInsecureRequests: headers['Upgrade-Insecure-Requests'] ?? null,
111
+ uaChromeVersion,
112
+ secChUaChromeVersion,
113
+ uaConsistent,
114
+ },
115
+ };
116
+ }
117
+ // ── checkIpReputation ──
118
+ /**
119
+ * Query the active session's egress IP and ASN, score it against known
120
+ * datacenter / residential patterns, and return a structured result.
121
+ *
122
+ * Uses the free ipinfo.io endpoint which doesn't require an API key for
123
+ * basic queries. Doesn't query commercial reputation services (those
124
+ * require API keys); for those, integrate at the caller's level.
125
+ */
126
+ export async function checkIpReputation(bt) {
127
+ await bt.navigate('https://ipinfo.io/json');
128
+ const raw = (await bt.executeJS('document.body.innerText'));
129
+ let parsed;
130
+ try {
131
+ parsed = JSON.parse(raw);
132
+ }
133
+ catch {
134
+ parsed = {};
135
+ }
136
+ const org = parsed.org ?? null;
137
+ const notes = [];
138
+ let isDatacenter = false;
139
+ let isResidential = false;
140
+ if (org) {
141
+ for (const { pattern, name } of DATACENTER_ASN_PATTERNS) {
142
+ if (pattern.test(org)) {
143
+ isDatacenter = true;
144
+ notes.push(`Matches datacenter ASN: ${name}`);
145
+ break;
146
+ }
147
+ }
148
+ if (!isDatacenter) {
149
+ for (const { pattern, name } of RESIDENTIAL_ASN_PATTERNS) {
150
+ if (pattern.test(org)) {
151
+ isResidential = true;
152
+ notes.push(`Matches residential ISP: ${name}`);
153
+ break;
154
+ }
155
+ }
156
+ }
157
+ if (!isDatacenter && !isResidential) {
158
+ notes.push('ASN not in known datacenter or residential lists — could be either');
159
+ }
160
+ }
161
+ else {
162
+ notes.push('Could not determine ASN');
163
+ }
164
+ return {
165
+ ip: parsed.ip ?? null,
166
+ hostname: parsed.hostname ?? null,
167
+ asn: org ? (org.match(/AS\d+/) ?? [])[0] ?? null : null,
168
+ org,
169
+ city: parsed.city ?? null,
170
+ region: parsed.region ?? null,
171
+ country: parsed.country ?? null,
172
+ loc: parsed.loc ?? null,
173
+ timezone: parsed.timezone ?? null,
174
+ isDatacenter,
175
+ isResidential,
176
+ notes,
177
+ };
178
+ }
179
+ // ── testAgainstAkamai ──
180
+ /**
181
+ * Visit an Akamai-protected URL and report the result with diagnosis.
182
+ * Recognizes the Akamai Access Denied error page format and extracts
183
+ * the reference number for triage.
184
+ */
185
+ export async function testAgainstAkamai(bt, url) {
186
+ const start = Date.now();
187
+ try {
188
+ await bt.navigate(url);
189
+ // Wait for the page to settle a moment so Akamai can render its block
190
+ // page if it's going to.
191
+ await new Promise((r) => setTimeout(r, 1500));
192
+ const info = (await bt.executeJS(`(() => ({
193
+ url: location.href,
194
+ title: document.title,
195
+ bodyPreview: (document.body ? document.body.innerText : '').slice(0, 600),
196
+ }))()`));
197
+ // Akamai Access Denied detection
198
+ const isBlocked = info.title === 'Access Denied' ||
199
+ /You don't have permission to access/i.test(info.bodyPreview) ||
200
+ /errors\.edgesuite\.net/i.test(info.bodyPreview);
201
+ // Extract Akamai reference number if present
202
+ const refMatch = info.bodyPreview.match(/Reference\s*#([0-9a-f.]+)/);
203
+ const akamaiReference = refMatch ? refMatch[1] ?? null : null;
204
+ let suggestion;
205
+ if (!isBlocked) {
206
+ suggestion = 'Page loaded successfully. No Akamai block detected.';
207
+ }
208
+ else {
209
+ suggestion = [
210
+ 'Akamai blocked the request at the edge. Diagnosis steps:',
211
+ '1. Run `bt.captureFingerprint()` and check `headers.uaConsistent`. If false, upgrade BlackTip to v0.2.0+.',
212
+ '2. Run `bt.checkIpReputation()`. If `isDatacenter: true`, switch to a residential network or proxy.',
213
+ '3. Test the same URL in your normal Chrome from the same machine. If that ALSO blocks, your IP is flagged — switch networks.',
214
+ '4. Try `bt.warmSession({sites: [...]})` before the target navigation.',
215
+ '5. Try with `userDataDir` set in BlackTipConfig for a persistent profile.',
216
+ `Akamai reference: ${akamaiReference ?? 'unknown'}`,
217
+ ].join('\n');
218
+ }
219
+ return {
220
+ url,
221
+ passed: !isBlocked,
222
+ finalUrl: info.url,
223
+ title: info.title,
224
+ akamaiReference,
225
+ bodyPreview: info.bodyPreview.slice(0, 300),
226
+ suggestion,
227
+ durationMs: Date.now() - start,
228
+ };
229
+ }
230
+ catch (err) {
231
+ return {
232
+ url,
233
+ passed: false,
234
+ finalUrl: url,
235
+ title: '',
236
+ akamaiReference: null,
237
+ bodyPreview: '',
238
+ suggestion: `Navigation threw: ${err instanceof Error ? err.message : String(err)}`,
239
+ durationMs: Date.now() - start,
240
+ };
241
+ }
242
+ }
243
+ // ── testAgainstAntiBot ──
244
+ //
245
+ // Generic anti-bot probe that recognises Akamai, DataDome, Cloudflare,
246
+ // PerimeterX/HUMAN, Imperva, Kasada and Arkose. Validated against the
247
+ // scoreboard in docs/anti-bot-validation.md.
248
+ const VENDOR_BLOCK_PATTERNS = [
249
+ { vendor: 'akamai', titleRe: /^Access Denied$/, bodyRe: /You don't have permission to access|errors\.edgesuite\.net/i },
250
+ { vendor: 'datadome', bodyRe: /geo\.captcha-delivery\.com|captcha-delivery\.com|please enable javascript and cookies to continue|datado\.me/i },
251
+ { vendor: 'cloudflare', titleRe: /^Just a moment\.\.\.$|^Attention Required! \| Cloudflare$/, bodyRe: /Checking your browser before accessing|cf-browser-verification|challenges\.cloudflare\.com|Sorry, you have been blocked/i },
252
+ { vendor: 'perimeterx', titleRe: /^Access to this page has been denied/, bodyRe: /Press (?:&|and) Hold to confirm you (?:are|are not) a human|px-captcha|perimeterx\.net|human\.com/i },
253
+ { vendor: 'imperva', bodyRe: /Request unsuccessful\. Incapsula incident ID|_Incapsula_Resource|Incapsula incident/i },
254
+ { vendor: 'kasada', bodyRe: /kpsdk|ips\.js\?|kasada/i },
255
+ { vendor: 'arkose', bodyRe: /client-api\.arkoselabs\.com|funcaptcha/i },
256
+ ];
257
+ const VENDOR_SCRIPT_SIGNAL_QUERY = `(() => {
258
+ const out = [];
259
+ const html = document.documentElement.outerHTML;
260
+ if (/datado\\.me|js\\.datadome\\.co/i.test(html)) out.push({vendor:'datadome', signal:'script'});
261
+ if (/perimeterx|px-cdn|px-captcha|human-security/i.test(html)) out.push({vendor:'perimeterx', signal:'script'});
262
+ if (/challenges\\.cloudflare\\.com|cdn-cgi\\/challenge-platform/i.test(html)) out.push({vendor:'cloudflare', signal:'script'});
263
+ if (/akam\\/|ak\\.bmpsdk|akamaihd\\.net\\/sensor/i.test(html)) out.push({vendor:'akamai', signal:'script'});
264
+ if (/x-kpsdk-/i.test(html)) out.push({vendor:'kasada', signal:'script'});
265
+ return JSON.stringify(out);
266
+ })()`;
267
+ // Cookie-name patterns for the vendor cookie checks. Run against the
268
+ // full cookie jar from BlackTip's cookies API (which includes httpOnly
269
+ // cookies that document.cookie can't see — cf_clearance, __cf_bm,
270
+ // _abck, datadome, etc. are all httpOnly).
271
+ const VENDOR_COOKIE_PATTERNS = [
272
+ { vendor: 'datadome', nameRe: /^(datadome|dd_cookie_test_|dd_s)/i },
273
+ { vendor: 'perimeterx', nameRe: /^_px[a-z0-9]*$|^_pxhd$/i },
274
+ { vendor: 'cloudflare', nameRe: /^(cf_clearance|__cf_bm|__cflb|_cfuvid)$/i },
275
+ { vendor: 'akamai', nameRe: /^(_abck|bm_sz|ak_bmsc|bm_sv|bm_mi|bm_so)$/i },
276
+ { vendor: 'imperva', nameRe: /^(visid_incap_|incap_ses_)/i },
277
+ ];
278
+ /**
279
+ * Visit a URL and report whether any major anti-bot vendor served a
280
+ * challenge or block. Recognises Akamai, DataDome, Cloudflare, PerimeterX/HUMAN,
281
+ * Imperva, Kasada and Arkose. Captures vendor signals (cookies, scripts) even
282
+ * on a passing page so you can verify a target is actually protected and
283
+ * BlackTip is sliding past it — not a false negative on an unprotected URL.
284
+ */
285
+ export async function testAgainstAntiBot(bt, url) {
286
+ const start = Date.now();
287
+ try {
288
+ await bt.navigate(url);
289
+ await new Promise((r) => setTimeout(r, 2500));
290
+ const info = (await bt.executeJS(`(() => ({
291
+ url: location.href,
292
+ title: document.title,
293
+ bodyPreview: (document.body ? document.body.innerText : '').slice(0, 800),
294
+ }))()`));
295
+ const detectedVendors = [];
296
+ for (const { vendor, titleRe, bodyRe } of VENDOR_BLOCK_PATTERNS) {
297
+ if ((titleRe && titleRe.test(info.title)) || (bodyRe && bodyRe.test(info.bodyPreview))) {
298
+ if (!detectedVendors.includes(vendor))
299
+ detectedVendors.push(vendor);
300
+ }
301
+ }
302
+ const scriptSignalsRaw = (await bt.executeJS(VENDOR_SCRIPT_SIGNAL_QUERY));
303
+ const vendorSignals = [];
304
+ try {
305
+ const scriptSignals = JSON.parse(scriptSignalsRaw);
306
+ vendorSignals.push(...scriptSignals);
307
+ }
308
+ catch { /* leave empty */ }
309
+ // Cookie signals — read via the cookies API so we see httpOnly cookies
310
+ // (cf_clearance, __cf_bm, _abck, datadome, etc. are all httpOnly and
311
+ // invisible to document.cookie). The current page's eTLD+1 is what we
312
+ // care about — we don't want to leak signals from prior navigations
313
+ // in the same session.
314
+ try {
315
+ const allCookies = await bt.cookies();
316
+ const currentHost = (() => {
317
+ try {
318
+ return new URL(info.url).hostname;
319
+ }
320
+ catch {
321
+ return null;
322
+ }
323
+ })();
324
+ const seen = new Set();
325
+ for (const c of allCookies) {
326
+ if (currentHost) {
327
+ // Match cookies whose domain is a parent or equal of the current host.
328
+ const cookieDomain = c.domain.replace(/^\./, '');
329
+ if (currentHost !== cookieDomain && !currentHost.endsWith('.' + cookieDomain))
330
+ continue;
331
+ }
332
+ for (const { vendor, nameRe } of VENDOR_COOKIE_PATTERNS) {
333
+ if (nameRe.test(c.name)) {
334
+ const key = vendor + ':cookie';
335
+ if (!seen.has(key)) {
336
+ vendorSignals.push({ vendor, signal: 'cookie' });
337
+ seen.add(key);
338
+ }
339
+ }
340
+ }
341
+ }
342
+ }
343
+ catch { /* cookies API may fail in edge cases — leave script signals */ }
344
+ const refMatch = info.bodyPreview.match(/Reference\s*#([0-9a-f.]+)/);
345
+ const akamaiReference = detectedVendors.includes('akamai') && refMatch ? refMatch[1] ?? null : null;
346
+ const passed = detectedVendors.length === 0;
347
+ const suggestion = passed
348
+ ? vendorSignals.length > 0
349
+ ? `Page loaded successfully. Vendor signals present (${vendorSignals.map((s) => s.vendor).join(', ')}) — target is protected and BlackTip is passing.`
350
+ : 'Page loaded successfully. No anti-bot vendor signals detected — the target may not be protected.'
351
+ : [
352
+ `Blocked by: ${detectedVendors.join(', ')}.`,
353
+ 'Diagnosis steps:',
354
+ '1. `bt.captureFingerprint()` — verify `headers.uaConsistent` is true.',
355
+ '2. `bt.checkIpReputation()` — if `isDatacenter: true`, switch to a residential proxy.',
356
+ '3. Test the same URL in your normal Chrome from this machine. If that also blocks, the IP is flagged.',
357
+ '4. `bt.warmSession({sites: [...]})` before retrying.',
358
+ '5. Set `userDataDir` in BlackTipConfig for a persistent profile.',
359
+ akamaiReference ? `Akamai reference: ${akamaiReference}` : '',
360
+ ].filter(Boolean).join('\n');
361
+ return {
362
+ url,
363
+ passed,
364
+ finalUrl: info.url,
365
+ title: info.title,
366
+ detectedVendors,
367
+ vendorSignals,
368
+ akamaiReference,
369
+ bodyPreview: info.bodyPreview.slice(0, 300),
370
+ suggestion,
371
+ durationMs: Date.now() - start,
372
+ };
373
+ }
374
+ catch (err) {
375
+ return {
376
+ url,
377
+ passed: false,
378
+ finalUrl: url,
379
+ title: '',
380
+ detectedVendors: [],
381
+ vendorSignals: [],
382
+ akamaiReference: null,
383
+ bodyPreview: '',
384
+ suggestion: `Navigation threw: ${err instanceof Error ? err.message : String(err)}`,
385
+ durationMs: Date.now() - start,
386
+ };
387
+ }
388
+ }
389
+ //# sourceMappingURL=diagnostics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"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"}