@browsercore/testing 0.1.3 → 0.2.1
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/README.md +5 -0
- package/bin/coverage-md.mjs +99 -0
- package/captures/DIVERGENCE_REPORT.md +325 -0
- package/captures/IMPERSIONATION_GAPS_REPORT.md +367 -0
- package/captures/README.md +56 -0
- package/captures/REVIEW_FINDINGS.md +71 -0
- package/captures/_probe/analyze.py +67 -0
- package/captures/_probe/capture_bytes.py +127 -0
- package/captures/_probe/chrome131_determinism.py +32 -0
- package/captures/_probe/diagnose_capture.py +59 -0
- package/captures/_probe/golden_analysis.py +91 -0
- package/captures/_probe/inspect_golden.py +55 -0
- package/captures/_probe/inspect_sv.py +16 -0
- package/captures/_probe/make_golden.py +170 -0
- package/captures/_probe/output/chrome100.json +219 -0
- package/captures/_probe/output/chrome101.json +219 -0
- package/captures/_probe/output/chrome104.json +219 -0
- package/captures/_probe/output/chrome107.json +220 -0
- package/captures/_probe/output/chrome110.json +220 -0
- package/captures/_probe/output/chrome116.json +220 -0
- package/captures/_probe/output/chrome119.json +219 -0
- package/captures/_probe/output/chrome120.json +219 -0
- package/captures/_probe/output/chrome123.json +223 -0
- package/captures/_probe/output/chrome124.json +224 -0
- package/captures/_probe/output/chrome131.json +224 -0
- package/captures/_probe/output/chrome131_fresh.json +224 -0
- package/captures/_probe/output/chrome133a.json +224 -0
- package/captures/_probe/output/chrome136.json +224 -0
- package/captures/_probe/output/chrome99.json +219 -0
- package/captures/_probe/output/edge101.json +219 -0
- package/captures/_probe/output/edge99.json +219 -0
- package/captures/_probe/output/firefox133.json +227 -0
- package/captures/_probe/output/firefox135.json +230 -0
- package/captures/_probe/output/safari155.json +210 -0
- package/captures/_probe/output/safari170.json +214 -0
- package/captures/_probe/output/safari170_fresh.json +214 -0
- package/captures/_probe/output/safari180.json +216 -0
- package/captures/_probe/output/safari180_ios.json +216 -0
- package/captures/_probe/output/safari184.json +215 -0
- package/captures/_probe/output/safari260.json +211 -0
- package/captures/_probe/output/summary.json +2520 -0
- package/captures/_probe/parse_existing_settings.py +15 -0
- package/captures/_probe/peetprint.py +39 -0
- package/captures/_probe/probe.py +137 -0
- package/captures/_probe/raw_bytes/chrome-131_client_hello.bin +0 -0
- package/captures/_probe/raw_bytes/chrome-131_client_hello.hex +32 -0
- package/captures/_probe/raw_bytes/firefox-133_client_hello.bin +0 -0
- package/captures/_probe/raw_bytes/firefox-133_client_hello.hex +32 -0
- package/captures/_probe/raw_bytes/safari-17_client_hello.bin +0 -0
- package/captures/_probe/raw_bytes/safari-17_client_hello.hex +32 -0
- package/captures/_probe/relay_capture.py +137 -0
- package/captures/_probe/socks_capture.py +138 -0
- package/captures/_probe/validate_fresh.py +66 -0
- package/captures/_probe/validate_golden.py +97 -0
- package/captures/_probe/verify_capture.py +68 -0
- package/captures/chrome-131/http2/settings.bin +0 -0
- package/captures/chrome-131/http2/settings.meta.json +9 -0
- package/captures/chrome-131/oracle_capture.reference.json +224 -0
- package/captures/chrome-131/tls/client_hello.bin +0 -0
- package/captures/chrome-131/tls/client_hello.meta.json +20 -0
- package/captures/chrome-140/http2/settings.bin +0 -0
- package/captures/chrome-140/http2/settings.meta.json +9 -0
- package/captures/chrome-140/tls/client_hello.bin +0 -0
- package/captures/chrome-140/tls/client_hello.meta.json +12 -0
- package/captures/firefox-128/tls/client_hello.bin +0 -0
- package/captures/firefox-128/tls/client_hello.meta.json +12 -0
- package/captures/firefox-133/http2/settings.bin +0 -0
- package/captures/firefox-133/http2/settings.meta.json +9 -0
- package/captures/firefox-133/oracle_capture.reference.json +227 -0
- package/captures/firefox-133/tls/client_hello.bin +0 -0
- package/captures/firefox-133/tls/client_hello.meta.json +20 -0
- package/captures/safari-17/http2/settings.bin +0 -0
- package/captures/safari-17/http2/settings.meta.json +9 -0
- package/captures/safari-17/oracle_capture.reference.json +214 -0
- package/captures/safari-17/tls/client_hello.bin +0 -0
- package/captures/safari-17/tls/client_hello.meta.json +20 -0
- package/captures/test-malformed/http2/settings.bin +0 -0
- package/captures/test-malformed/http2/settings.meta.json +9 -0
- package/captures/test-no-extensions/tls/client_hello.bin +0 -0
- package/captures/test-no-extensions/tls/client_hello.meta.json +9 -0
- package/captures/test-wrong-type/http2/settings.bin +0 -0
- package/captures/test-wrong-type/http2/settings.meta.json +9 -0
- package/dist/bench/bench.d.ts +24 -6
- package/dist/bench/bench.d.ts.map +1 -1
- package/dist/bench/bench.js +110 -10
- package/dist/bench/bench.js.map +1 -1
- package/dist/captures/manifest.d.ts.map +1 -1
- package/dist/captures/manifest.js +7 -7
- package/dist/captures/manifest.js.map +1 -1
- package/dist/categories/compression.d.ts +6 -4
- package/dist/categories/compression.d.ts.map +1 -1
- package/dist/categories/compression.js +56 -6
- package/dist/categories/compression.js.map +1 -1
- package/dist/categories/cookies.d.ts +3 -2
- package/dist/categories/cookies.d.ts.map +1 -1
- package/dist/categories/cookies.js +216 -11
- package/dist/categories/cookies.js.map +1 -1
- package/dist/categories/redirects.d.ts +8 -2
- package/dist/categories/redirects.d.ts.map +1 -1
- package/dist/categories/redirects.js +97 -12
- package/dist/categories/redirects.js.map +1 -1
- package/dist/e2e/cert-gen.d.ts +31 -0
- package/dist/e2e/cert-gen.d.ts.map +1 -0
- package/dist/e2e/cert-gen.js +158 -0
- package/dist/e2e/cert-gen.js.map +1 -0
- package/dist/e2e/parse-clienthello.d.ts +177 -0
- package/dist/e2e/parse-clienthello.d.ts.map +1 -0
- package/dist/e2e/parse-clienthello.js +343 -0
- package/dist/e2e/parse-clienthello.js.map +1 -0
- package/dist/e2e/sink-server.d.ts +109 -0
- package/dist/e2e/sink-server.d.ts.map +1 -0
- package/dist/e2e/sink-server.js +292 -0
- package/dist/e2e/sink-server.js.map +1 -0
- package/dist/e2e/traffic-server.d.ts +48 -0
- package/dist/e2e/traffic-server.d.ts.map +1 -0
- package/dist/e2e/traffic-server.js +162 -0
- package/dist/e2e/traffic-server.js.map +1 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/fingerprint/index.d.ts +5 -2
- package/dist/fingerprint/index.d.ts.map +1 -1
- package/dist/fingerprint/index.js +3 -2
- package/dist/fingerprint/index.js.map +1 -1
- package/dist/fingerprint/ja3.d.ts.map +1 -1
- package/dist/fingerprint/ja3.js +33 -4
- package/dist/fingerprint/ja3.js.map +1 -1
- package/dist/fingerprint/ja4-errors.d.ts +17 -0
- package/dist/fingerprint/ja4-errors.d.ts.map +1 -0
- package/dist/fingerprint/ja4-errors.js +20 -0
- package/dist/fingerprint/ja4-errors.js.map +1 -0
- package/dist/fingerprint/ja4-reader.d.ts +44 -0
- package/dist/fingerprint/ja4-reader.d.ts.map +1 -0
- package/dist/fingerprint/ja4-reader.js +112 -0
- package/dist/fingerprint/ja4-reader.js.map +1 -0
- package/dist/fingerprint/ja4.d.ts +11 -43
- package/dist/fingerprint/ja4.d.ts.map +1 -1
- package/dist/fingerprint/ja4.js +54 -141
- package/dist/fingerprint/ja4.js.map +1 -1
- package/dist/fingerprint/ja4h.d.ts +46 -0
- package/dist/fingerprint/ja4h.d.ts.map +1 -0
- package/dist/fingerprint/ja4h.js +50 -0
- package/dist/fingerprint/ja4h.js.map +1 -0
- package/dist/golden/golden.d.ts +15 -1
- package/dist/golden/golden.d.ts.map +1 -1
- package/dist/golden/golden.js +42 -28
- package/dist/golden/golden.js.map +1 -1
- package/dist/index.d.ts +21 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +36 -1
- package/dist/index.js.map +1 -1
- package/dist/node-provider.d.ts +42 -0
- package/dist/node-provider.d.ts.map +1 -0
- package/dist/node-provider.js +53 -0
- package/dist/node-provider.js.map +1 -0
- package/dist/provider.d.ts +52 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +14 -0
- package/dist/provider.js.map +1 -0
- package/dist/reference/browser-provider.d.ts +33 -0
- package/dist/reference/browser-provider.d.ts.map +1 -0
- package/dist/reference/browser-provider.js +61 -0
- package/dist/reference/browser-provider.js.map +1 -0
- package/dist/reference/curl-provider.d.ts +40 -0
- package/dist/reference/curl-provider.d.ts.map +1 -0
- package/dist/reference/curl-provider.js +96 -0
- package/dist/reference/curl-provider.js.map +1 -0
- package/dist/reference/dump.d.ts +42 -0
- package/dist/reference/dump.d.ts.map +1 -0
- package/dist/reference/dump.js +102 -0
- package/dist/reference/dump.js.map +1 -0
- package/dist/reference/node-crypto-provider.d.ts +31 -0
- package/dist/reference/node-crypto-provider.d.ts.map +1 -0
- package/dist/reference/node-crypto-provider.js +199 -0
- package/dist/reference/node-crypto-provider.js.map +1 -0
- package/dist/reference/node-reference.d.ts.map +1 -1
- package/dist/reference/node-reference.js +2 -5
- package/dist/reference/node-reference.js.map +1 -1
- package/dist/reference/reference-errors.d.ts +36 -0
- package/dist/reference/reference-errors.d.ts.map +1 -0
- package/dist/reference/reference-errors.js +43 -0
- package/dist/reference/reference-errors.js.map +1 -0
- package/dist/reference/reference-types.d.ts +33 -0
- package/dist/reference/reference-types.d.ts.map +1 -0
- package/dist/reference/reference-types.js +9 -0
- package/dist/reference/reference-types.js.map +1 -0
- package/dist/reference/reference.d.ts +14 -77
- package/dist/reference/reference.d.ts.map +1 -1
- package/dist/reference/reference.js +43 -207
- package/dist/reference/reference.js.map +1 -1
- package/dist/rfc/rfcTests.d.ts +46 -8
- package/dist/rfc/rfcTests.d.ts.map +1 -1
- package/dist/rfc/rfcTests.js +203 -16
- package/dist/rfc/rfcTests.js.map +1 -1
- package/dist/types.d.ts +2 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils.d.ts +3 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +11 -5
- package/dist/utils.js.map +1 -1
- package/package.json +68 -55
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JA4H — the HTTP-layer sibling of JA4 (docs/TEST-SUITE.md, Category 4).
|
|
3
|
+
*
|
|
4
|
+
* JA4H fingerprints an HTTP request from its method, version, header order,
|
|
5
|
+
* cookies, and Accept-Language prefix. It is the HTTP analogue computed by the
|
|
6
|
+
* reference provider to fingerprint a captured request. Independent of the TLS
|
|
7
|
+
* ClientHello parsing in `ja4.ts`.
|
|
8
|
+
*
|
|
9
|
+
* Reference: https://github.com/FoxIO-LLC/JA4
|
|
10
|
+
*/
|
|
11
|
+
/** The four computed JA4H parts. */
|
|
12
|
+
export interface Ja4hFingerprint {
|
|
13
|
+
readonly a: string;
|
|
14
|
+
readonly b: string;
|
|
15
|
+
readonly c: string;
|
|
16
|
+
readonly d: string;
|
|
17
|
+
/** Canonical `JA4H_a_JA4H_b_JA4H_c_JA4H_d` tag. */
|
|
18
|
+
readonly tag: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* HTTP request fields needed to compute a JA4H fingerprint.
|
|
22
|
+
*
|
|
23
|
+
* Only the parts JA4H inspects are required — method, HTTP version, header
|
|
24
|
+
* names (in order), cookies, and the Accept-Language prefix.
|
|
25
|
+
*/
|
|
26
|
+
export interface Ja4hRequest {
|
|
27
|
+
readonly method: "GET" | "POST" | "HEAD" | "PUT" | "DELETE" | "OPTIONS" | "PATCH";
|
|
28
|
+
readonly httpVersion: "1.1" | "2" | "3";
|
|
29
|
+
/** Header names in the order they are serialized (lowercased). */
|
|
30
|
+
readonly headerNames: readonly string[];
|
|
31
|
+
/** Cookie name=value pairs (as they appear in the Cookie header). */
|
|
32
|
+
readonly cookies: readonly string[];
|
|
33
|
+
/** The Accept-Language header value (only its 4-char prefix is used). */
|
|
34
|
+
readonly acceptLanguage?: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Compute the four-part JA4H HTTP fingerprint from a captured request.
|
|
38
|
+
*
|
|
39
|
+
* Format: `JA4H_a_JA4H_b_JA4H_c_JA4H_d` where:
|
|
40
|
+
* - JA4H_a: `{method:02s}{version}{has_cookies:d}{has_referer:d}{header_count:02d}{lang_prefix}`
|
|
41
|
+
* - JA4H_b: sorted cookie name=value pairs, SHA-256, first 12 hex.
|
|
42
|
+
* - JA4H_c: sorted cookie names, SHA-256, first 12 hex.
|
|
43
|
+
* - JA4H_d: sorted header names (excluding cookie), SHA-256, first 12 hex.
|
|
44
|
+
*/
|
|
45
|
+
export declare function computeJa4h(request: Ja4hRequest): Ja4hFingerprint;
|
|
46
|
+
//# sourceMappingURL=ja4h.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ja4h.d.ts","sourceRoot":"","sources":["../../src/fingerprint/ja4h.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,oCAAoC;AACpC,MAAM,WAAW,eAAe;IAC5B,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,mDAAmD;IACnD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACxB;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IACxB,QAAQ,CAAC,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;IAClF,QAAQ,CAAC,WAAW,EAAE,KAAK,GAAG,GAAG,GAAG,GAAG,CAAC;IACxC,kEAAkE;IAClE,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,qEAAqE;IACrE,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,yEAAyE;IACzE,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CACpC;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,eAAe,CAkCjE"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JA4H — the HTTP-layer sibling of JA4 (docs/TEST-SUITE.md, Category 4).
|
|
3
|
+
*
|
|
4
|
+
* JA4H fingerprints an HTTP request from its method, version, header order,
|
|
5
|
+
* cookies, and Accept-Language prefix. It is the HTTP analogue computed by the
|
|
6
|
+
* reference provider to fingerprint a captured request. Independent of the TLS
|
|
7
|
+
* ClientHello parsing in `ja4.ts`.
|
|
8
|
+
*
|
|
9
|
+
* Reference: https://github.com/FoxIO-LLC/JA4
|
|
10
|
+
*/
|
|
11
|
+
import { sha256First12 } from "./ja4.js";
|
|
12
|
+
/**
|
|
13
|
+
* Compute the four-part JA4H HTTP fingerprint from a captured request.
|
|
14
|
+
*
|
|
15
|
+
* Format: `JA4H_a_JA4H_b_JA4H_c_JA4H_d` where:
|
|
16
|
+
* - JA4H_a: `{method:02s}{version}{has_cookies:d}{has_referer:d}{header_count:02d}{lang_prefix}`
|
|
17
|
+
* - JA4H_b: sorted cookie name=value pairs, SHA-256, first 12 hex.
|
|
18
|
+
* - JA4H_c: sorted cookie names, SHA-256, first 12 hex.
|
|
19
|
+
* - JA4H_d: sorted header names (excluding cookie), SHA-256, first 12 hex.
|
|
20
|
+
*/
|
|
21
|
+
export function computeJa4h(request) {
|
|
22
|
+
const methodCode = request.method.slice(0, 2).toLowerCase();
|
|
23
|
+
const versionCode = request.httpVersion === "1.1"
|
|
24
|
+
? "11"
|
|
25
|
+
: request.httpVersion === "2"
|
|
26
|
+
? "02"
|
|
27
|
+
: request.httpVersion === "3"
|
|
28
|
+
? "03"
|
|
29
|
+
: "00";
|
|
30
|
+
const hasCookies = request.cookies.length > 0 ? "c" : "n";
|
|
31
|
+
const hasReferer = request.headerNames.some((h) => h === "referer") ? "r" : "n";
|
|
32
|
+
const headerCount = request.headerNames.length.toString().padStart(2, "0");
|
|
33
|
+
const langPrefix = (request.acceptLanguage ?? "").slice(0, 4).padEnd(4, "0").toLowerCase();
|
|
34
|
+
const a = `${methodCode}${versionCode}${hasCookies}${hasReferer}${headerCount}${langPrefix}`;
|
|
35
|
+
const sortedCookies = [...request.cookies].sort().join(",");
|
|
36
|
+
const b = sortedCookies.length > 0 ? sha256First12(sortedCookies) : "000000000000";
|
|
37
|
+
const sortedCookieNames = [...request.cookies]
|
|
38
|
+
.map((c) => c.split("=")[0] ?? "")
|
|
39
|
+
.filter((n) => n.length > 0)
|
|
40
|
+
.sort()
|
|
41
|
+
.join(",");
|
|
42
|
+
const c = sortedCookieNames.length > 0 ? sha256First12(sortedCookieNames) : "000000000000";
|
|
43
|
+
const sortedHeaders = [...request.headerNames]
|
|
44
|
+
.filter((h) => h !== "cookie")
|
|
45
|
+
.sort()
|
|
46
|
+
.join(",");
|
|
47
|
+
const d = sortedHeaders.length > 0 ? sha256First12(sortedHeaders) : "000000000000";
|
|
48
|
+
return { a, b, c, d, tag: `${a}_${b}_${c}_${d}` };
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=ja4h.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ja4h.js","sourceRoot":"","sources":["../../src/fingerprint/ja4h.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AA6BzC;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW,CAAC,OAAoB;IAC5C,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IAC5D,MAAM,WAAW,GACb,OAAO,CAAC,WAAW,KAAK,KAAK;QACzB,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,OAAO,CAAC,WAAW,KAAK,GAAG;YAC3B,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,OAAO,CAAC,WAAW,KAAK,GAAG;gBAC3B,CAAC,CAAC,IAAI;gBACN,CAAC,CAAC,IAAI,CAAC;IACnB,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IAC1D,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IAChF,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC3E,MAAM,UAAU,GAAG,CAAC,OAAO,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;IAE3F,MAAM,CAAC,GAAG,GAAG,UAAU,GAAG,WAAW,GAAG,UAAU,GAAG,UAAU,GAAG,WAAW,GAAG,UAAU,EAAE,CAAC;IAE7F,MAAM,aAAa,GAAG,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC5D,MAAM,CAAC,GAAG,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC;IAEnF,MAAM,iBAAiB,GAAG,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;SACzC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACjC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;SAC3B,IAAI,EAAE;SACN,IAAI,CAAC,GAAG,CAAC,CAAC;IACf,MAAM,CAAC,GAAG,iBAAiB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC;IAE3F,MAAM,aAAa,GAAG,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC;SACzC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,QAAQ,CAAC;SAC7B,IAAI,EAAE;SACN,IAAI,CAAC,GAAG,CAAC,CAAC;IACf,MAAM,CAAC,GAAG,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC;IAEnF,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;AACtD,CAAC"}
|
package/dist/golden/golden.d.ts
CHANGED
|
@@ -11,7 +11,21 @@
|
|
|
11
11
|
* (e.g. `chrome-140/tls/client_hello`) that maps directly onto the
|
|
12
12
|
* `captures/<profile>/<protocol>/<record>.{bin,meta.json}` layout.
|
|
13
13
|
*/
|
|
14
|
-
import type { CaptureId, CaptureMeta, ComparisonResult, ComparisonResultWithIgnore, GoldenCapture } from "../types.js";
|
|
14
|
+
import type { CaptureId, CaptureMeta, ComparisonResult, ComparisonResultWithIgnore, GoldenCapture, RandomizedField } from "../types.js";
|
|
15
|
+
/**
|
|
16
|
+
* Validate unknown JSON data as a {@link CaptureMeta} (no zod dep here).
|
|
17
|
+
*
|
|
18
|
+
* Exported for unit testing the validation branches — the error paths are only
|
|
19
|
+
* reachable with a malformed sidecar, which the on-disk captures are not.
|
|
20
|
+
*/
|
|
21
|
+
export declare function parseCaptureMeta(raw: unknown, captureId: string): CaptureMeta;
|
|
22
|
+
/**
|
|
23
|
+
* Validate unknown data as a list of {@link RandomizedField}.
|
|
24
|
+
*
|
|
25
|
+
* Exported for unit testing the validation branches — the error paths are only
|
|
26
|
+
* reachable with a malformed sidecar, which the on-disk captures are not.
|
|
27
|
+
*/
|
|
28
|
+
export declare function parseRandomizedFields(raw: unknown, captureId: string): readonly RandomizedField[];
|
|
15
29
|
/**
|
|
16
30
|
* Load a golden capture by id.
|
|
17
31
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"golden.d.ts","sourceRoot":"","sources":["../../src/golden/golden.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;
|
|
1
|
+
{"version":3,"file":"golden.d.ts","sourceRoot":"","sources":["../../src/golden/golden.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EACR,SAAS,EACT,WAAW,EACX,gBAAgB,EAChB,0BAA0B,EAC1B,aAAa,EACb,eAAe,EAClB,MAAM,aAAa,CAAC;AAkCrB;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,WAAW,CAgD7E;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,eAAe,EAAE,CA+BjG;AAYD;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,SAAS,EAAE,SAAS,GAAG,aAAa,CA2B9D;AAoBD,0DAA0D;AAC1D,wBAAgB,eAAe,CAAC,SAAS,EAAE,SAAS,GAAG,WAAW,CAUjE;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,GAAG,gBAAgB,CAa/F;AAED;;;;;;;;GAQG;AACH,wBAAgB,8BAA8B,CAC1C,MAAM,EAAE,UAAU,EAClB,SAAS,EAAE,SAAS,GACrB,0BAA0B,CAe5B"}
|
package/dist/golden/golden.js
CHANGED
|
@@ -11,17 +11,17 @@
|
|
|
11
11
|
* (e.g. `chrome-140/tls/client_hello`) that maps directly onto the
|
|
12
12
|
* `captures/<profile>/<protocol>/<record>.{bin,meta.json}` layout.
|
|
13
13
|
*/
|
|
14
|
-
import { readFileSync } from "node:fs";
|
|
15
|
-
import { fileURLToPath } from "node:url";
|
|
16
|
-
import { dirname, join } from "node:path";
|
|
17
14
|
import { compareBytes, compareBytesWithIgnore } from "../utils.js";
|
|
18
15
|
import { GoldenMismatchError, TestingError } from "../errors.js";
|
|
19
|
-
|
|
16
|
+
import { createFileSystem, createPath } from "../node-provider.js";
|
|
17
|
+
const fileSystem = createFileSystem();
|
|
18
|
+
const path = createPath();
|
|
19
|
+
const here = import.meta.dirname;
|
|
20
20
|
// src/golden -> package root -> captures/
|
|
21
|
-
const capturesDir = join(here, "..", "..", "captures");
|
|
21
|
+
const capturesDir = path.join(here, "..", "..", "captures");
|
|
22
22
|
/** Parse a CaptureId of the form `${profile}/${protocol}/${record}`. */
|
|
23
23
|
function parseCaptureId(captureId) {
|
|
24
|
-
const EXPECTED_FORMAT =
|
|
24
|
+
const EXPECTED_FORMAT = `\${profile}/\${protocol}/\${record}`;
|
|
25
25
|
const parts = captureId.split("/");
|
|
26
26
|
if (parts.length !== 3) {
|
|
27
27
|
throw new TestingError(`Malformed CaptureId "${captureId}" — expected ${EXPECTED_FORMAT}`);
|
|
@@ -32,39 +32,44 @@ function parseCaptureId(captureId) {
|
|
|
32
32
|
}
|
|
33
33
|
return { profile, protocol, record };
|
|
34
34
|
}
|
|
35
|
-
/**
|
|
36
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Validate unknown JSON data as a {@link CaptureMeta} (no zod dep here).
|
|
37
|
+
*
|
|
38
|
+
* Exported for unit testing the validation branches — the error paths are only
|
|
39
|
+
* reachable with a malformed sidecar, which the on-disk captures are not.
|
|
40
|
+
*/
|
|
41
|
+
export function parseCaptureMeta(raw, captureId) {
|
|
37
42
|
if (typeof raw !== "object" || raw === null) {
|
|
38
43
|
throw new TestingError(`Capture meta for ${captureId} is not an object`);
|
|
39
44
|
}
|
|
40
45
|
const obj = raw;
|
|
41
|
-
const source = obj
|
|
46
|
+
const source = obj["source"];
|
|
42
47
|
if (source !== "curl-impersonate" && source !== "real-browser") {
|
|
43
48
|
throw new TestingError(`Capture meta for ${captureId} has invalid source: ${String(source)}`);
|
|
44
49
|
}
|
|
45
|
-
const protocol = obj
|
|
50
|
+
const protocol = obj["protocol"];
|
|
46
51
|
if (protocol !== "tls" && protocol !== "http2" && protocol !== "http1" && protocol !== "tcp") {
|
|
47
52
|
throw new TestingError(`Capture meta for ${captureId} has invalid protocol: ${String(protocol)}`);
|
|
48
53
|
}
|
|
49
|
-
const record = obj
|
|
54
|
+
const record = obj["record"];
|
|
50
55
|
if (record !== "client_hello" &&
|
|
51
56
|
record !== "settings" &&
|
|
52
57
|
record !== "headers" &&
|
|
53
58
|
record !== "server_hello") {
|
|
54
59
|
throw new TestingError(`Capture meta for ${captureId} has invalid record: ${String(record)}`);
|
|
55
60
|
}
|
|
56
|
-
const description = obj
|
|
61
|
+
const description = obj["description"];
|
|
57
62
|
if (typeof description !== "string") {
|
|
58
63
|
throw new TestingError(`Capture meta for ${captureId} has non-string description`);
|
|
59
64
|
}
|
|
60
|
-
const createdAt = obj
|
|
65
|
+
const createdAt = obj["createdAt"];
|
|
61
66
|
if (typeof createdAt !== "string") {
|
|
62
67
|
throw new TestingError(`Capture meta for ${captureId} has non-string createdAt`);
|
|
63
68
|
}
|
|
64
|
-
const randomizedFields = parseRandomizedFields(obj
|
|
69
|
+
const randomizedFields = parseRandomizedFields(obj["randomizedFields"], captureId);
|
|
65
70
|
return {
|
|
66
71
|
source,
|
|
67
|
-
profile: obj
|
|
72
|
+
profile: obj["profile"],
|
|
68
73
|
protocol,
|
|
69
74
|
record,
|
|
70
75
|
description,
|
|
@@ -72,8 +77,13 @@ function parseCaptureMeta(raw, captureId) {
|
|
|
72
77
|
createdAt,
|
|
73
78
|
};
|
|
74
79
|
}
|
|
75
|
-
/**
|
|
76
|
-
|
|
80
|
+
/**
|
|
81
|
+
* Validate unknown data as a list of {@link RandomizedField}.
|
|
82
|
+
*
|
|
83
|
+
* Exported for unit testing the validation branches — the error paths are only
|
|
84
|
+
* reachable with a malformed sidecar, which the on-disk captures are not.
|
|
85
|
+
*/
|
|
86
|
+
export function parseRandomizedFields(raw, captureId) {
|
|
77
87
|
if (!Array.isArray(raw)) {
|
|
78
88
|
throw new TestingError(`Capture meta for ${captureId} has non-array randomizedFields`);
|
|
79
89
|
}
|
|
@@ -82,15 +92,15 @@ function parseRandomizedFields(raw, captureId) {
|
|
|
82
92
|
throw new TestingError(`Capture meta for ${captureId} randomizedFields[${i}] is not an object`);
|
|
83
93
|
}
|
|
84
94
|
const obj = item;
|
|
85
|
-
const byteOffset = obj
|
|
95
|
+
const byteOffset = obj["byteOffset"];
|
|
86
96
|
if (typeof byteOffset !== "number" || byteOffset < 0) {
|
|
87
97
|
throw new TestingError(`Capture meta for ${captureId} randomizedFields[${i}].byteOffset invalid`);
|
|
88
98
|
}
|
|
89
|
-
const length = obj
|
|
99
|
+
const length = obj["length"];
|
|
90
100
|
if (typeof length !== "number" || length < 0) {
|
|
91
101
|
throw new TestingError(`Capture meta for ${captureId} randomizedFields[${i}].length invalid`);
|
|
92
102
|
}
|
|
93
|
-
const reason = obj
|
|
103
|
+
const reason = obj["reason"];
|
|
94
104
|
if (reason !== "ephemeral_key" && reason !== "nonce" && reason !== "grease" && reason !== "random") {
|
|
95
105
|
throw new TestingError(`Capture meta for ${captureId} randomizedFields[${i}].reason invalid: ${String(reason)}`);
|
|
96
106
|
}
|
|
@@ -100,7 +110,7 @@ function parseRandomizedFields(raw, captureId) {
|
|
|
100
110
|
/** Resolve a CaptureId to its on-disk `.bin` + `.meta.json` paths. */
|
|
101
111
|
function resolveCapturePaths(captureId) {
|
|
102
112
|
const { profile, protocol, record } = parseCaptureId(captureId);
|
|
103
|
-
const base = join(capturesDir, profile, protocol, record);
|
|
113
|
+
const base = path.join(capturesDir, profile, protocol, record);
|
|
104
114
|
return { binPath: `${base}.bin`, metaPath: `${base}.meta.json` };
|
|
105
115
|
}
|
|
106
116
|
/**
|
|
@@ -115,7 +125,7 @@ export function loadGolden(captureId) {
|
|
|
115
125
|
const { binPath, metaPath } = resolveCapturePaths(captureId);
|
|
116
126
|
let bytes;
|
|
117
127
|
try {
|
|
118
|
-
bytes = readFileSync(binPath);
|
|
128
|
+
bytes = fileSystem.readFileSync(binPath);
|
|
119
129
|
}
|
|
120
130
|
catch (e) {
|
|
121
131
|
const cause = e instanceof Error ? e : new Error(String(e));
|
|
@@ -123,7 +133,7 @@ export function loadGolden(captureId) {
|
|
|
123
133
|
}
|
|
124
134
|
let meta;
|
|
125
135
|
try {
|
|
126
|
-
meta = JSON.parse(
|
|
136
|
+
meta = JSON.parse(fileSystem.readFileText(metaPath));
|
|
127
137
|
}
|
|
128
138
|
catch (e) {
|
|
129
139
|
const cause = e instanceof Error ? e : new Error(String(e));
|
|
@@ -141,14 +151,18 @@ export function loadGolden(captureId) {
|
|
|
141
151
|
/** Derive a {@link CaptureSource} from a profile id (best-effort). */
|
|
142
152
|
function parseSource(profile) {
|
|
143
153
|
const p = String(profile);
|
|
144
|
-
if (p.startsWith("chrome"))
|
|
154
|
+
if (p.startsWith("chrome")) {
|
|
145
155
|
return "chrome-140";
|
|
146
|
-
|
|
156
|
+
}
|
|
157
|
+
if (p.startsWith("firefox")) {
|
|
147
158
|
return "firefox-135";
|
|
148
|
-
|
|
159
|
+
}
|
|
160
|
+
if (p.startsWith("safari")) {
|
|
149
161
|
return "safari-18";
|
|
150
|
-
|
|
162
|
+
}
|
|
163
|
+
if (p.startsWith("edge")) {
|
|
151
164
|
return "edge-140";
|
|
165
|
+
}
|
|
152
166
|
return "chrome-140";
|
|
153
167
|
}
|
|
154
168
|
/** Load the {@link CaptureMeta} sidecar for a capture. */
|
|
@@ -156,7 +170,7 @@ export function loadCaptureMeta(captureId) {
|
|
|
156
170
|
const { metaPath } = resolveCapturePaths(captureId);
|
|
157
171
|
let raw;
|
|
158
172
|
try {
|
|
159
|
-
raw = JSON.parse(
|
|
173
|
+
raw = JSON.parse(fileSystem.readFileText(metaPath));
|
|
160
174
|
}
|
|
161
175
|
catch (e) {
|
|
162
176
|
const cause = e instanceof Error ? e : new Error(String(e));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"golden.js","sourceRoot":"","sources":["../../src/golden/golden.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;
|
|
1
|
+
{"version":3,"file":"golden.js","sourceRoot":"","sources":["../../src/golden/golden.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Protocol verification: RFC compliance tests, browser golden packet captures,
|
|
5
5
|
* integration tests, and benchmarking.
|
|
6
6
|
*/
|
|
7
|
+
import type { CaptureEntry } from "./captures/manifest.js";
|
|
7
8
|
export { loadGolden, compareAgainstGolden } from "./golden/golden.js";
|
|
8
9
|
export { runTlsCompliance, runHttp2Compliance, runHttp1Compliance } from "./rfc/rfcTests.js";
|
|
9
10
|
export { benchmarkTlsHandshake, benchmarkHttp2Request } from "./bench/bench.js";
|
|
@@ -15,11 +16,30 @@ export type { SpecTestCase, TestCategoryId, TestRun, TestStatus } from "./types.
|
|
|
15
16
|
export * as categories from "./categories/index.js";
|
|
16
17
|
export { compareBytesWithIgnore } from "./utils.js";
|
|
17
18
|
export type { CaptureMeta, ComparisonResultWithIgnore, RandomizedField } from "./types.js";
|
|
18
|
-
export {
|
|
19
|
+
export type { CaptureEntry } from "./captures/manifest.js";
|
|
20
|
+
/**
|
|
21
|
+
* Lazily load the golden-capture manifest.
|
|
22
|
+
*
|
|
23
|
+
* The manifest reads each `.bin` from disk, so it is NOT eagerly evaluated at
|
|
24
|
+
* import time (an eager re-export would force every consumer to pay the read
|
|
25
|
+
* cost — and crash if the tarball lacked `captures/`). Call this only when you
|
|
26
|
+
* actually need the captures.
|
|
27
|
+
*/
|
|
28
|
+
export declare function loadCaptures(): Promise<readonly CaptureEntry[]>;
|
|
19
29
|
export { createReferenceProvider, CurlImpersonateProvider, RealBrowserCaptureProvider, } from "./reference/reference.js";
|
|
20
30
|
export type { CurlImpersonateOptions, Fingerprint, RealBrowserOptions, ReferenceProvider, ReferenceProviderKind, } from "./reference/reference.js";
|
|
21
31
|
export { computeJa3, computeJa4, Ja3ParseError, parseClientHello } from "./fingerprint/index.js";
|
|
22
32
|
export type { Ja3Segments } from "./fingerprint/index.js";
|
|
23
33
|
export { nodeCrypto, nodeDns, nodeZlib, nodeHttp, toError, compareBytesOutcome, } from "./reference/node-reference.js";
|
|
24
34
|
export type { CompareOutcome, HashAlgorithm } from "./reference/node-reference.js";
|
|
35
|
+
export { TlsSinkServer } from "./e2e/sink-server.js";
|
|
36
|
+
export type { ClientHelloCapture, HandshakeResult } from "./e2e/sink-server.js";
|
|
37
|
+
export { generateSelfSignedCert } from "./e2e/cert-gen.js";
|
|
38
|
+
export type { SelfSignedCert } from "./e2e/cert-gen.js";
|
|
39
|
+
export { TrafficServer } from "./e2e/traffic-server.js";
|
|
40
|
+
export type { TrafficResult } from "./e2e/traffic-server.js";
|
|
41
|
+
export { parseClientHelloWire, peekRecordHeader, findExtension, parseSniHostname, parseAlpnProtocols, parseSupportedVersions, decodeSni, decodeAlpn, decodeSupportedVersions, isGrease, nonGreaseCipherSuites, nonGreaseExtensionTypes, EXT, GREASE_SENTINELS, ClientHelloParseError, } from "./e2e/parse-clienthello.js";
|
|
42
|
+
export type { ParsedClientHello, ParsedExtension, ExtensionTypeValue, } from "./e2e/parse-clienthello.js";
|
|
43
|
+
export { createFileSystem, createPath, NodeFileSystemProvider, NodePathProvider } from "./node-provider.js";
|
|
44
|
+
export type { FileSystemProvider, PathProvider } from "./provider.js";
|
|
25
45
|
//# 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;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC7F,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAEhF,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhE,YAAY,EACR,UAAU,EACV,SAAS,EACT,eAAe,EACf,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,QAAQ,EACR,UAAU,EACV,UAAU,GACb,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAG7E,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACpF,OAAO,KAAK,UAAU,MAAM,uBAAuB,CAAC;AAGpD,OAAO,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AACpD,YAAY,EAAE,WAAW,EAAE,0BAA0B,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC3F,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC7F,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAEhF,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhE,YAAY,EACR,UAAU,EACV,SAAS,EACT,eAAe,EACf,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,QAAQ,EACR,UAAU,EACV,UAAU,GACb,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAG7E,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACpF,OAAO,KAAK,UAAU,MAAM,uBAAuB,CAAC;AAGpD,OAAO,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AACpD,YAAY,EAAE,WAAW,EAAE,0BAA0B,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC3F,YAAY,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D;;;;;;;GAOG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,SAAS,YAAY,EAAE,CAAC,CAGrE;AAGD,OAAO,EACH,uBAAuB,EACvB,uBAAuB,EACvB,0BAA0B,GAC7B,MAAM,0BAA0B,CAAC;AAClC,YAAY,EACR,sBAAsB,EACtB,WAAW,EACX,kBAAkB,EAClB,iBAAiB,EACjB,qBAAqB,GACxB,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AACjG,YAAY,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAI1D,OAAO,EACH,UAAU,EACV,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,mBAAmB,GACtB,MAAM,+BAA+B,CAAC;AACvC,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAOnF,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,YAAY,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAGhF,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAOxD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,YAAY,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAO7D,OAAO,EACH,oBAAoB,EACpB,gBAAgB,EAChB,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,sBAAsB,EACtB,SAAS,EACT,UAAU,EACV,uBAAuB,EACvB,QAAQ,EACR,qBAAqB,EACrB,uBAAuB,EACvB,GAAG,EACH,gBAAgB,EAChB,qBAAqB,GACxB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EACR,iBAAiB,EACjB,eAAe,EACf,kBAAkB,GACrB,MAAM,4BAA4B,CAAC;AAKpC,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC5G,YAAY,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,18 @@ export { TestCategory } from "./types.js";
|
|
|
14
14
|
export * as categories from "./categories/index.js";
|
|
15
15
|
// Randomized-field comparison (Cat 14 core) + capture manifest.
|
|
16
16
|
export { compareBytesWithIgnore } from "./utils.js";
|
|
17
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Lazily load the golden-capture manifest.
|
|
19
|
+
*
|
|
20
|
+
* The manifest reads each `.bin` from disk, so it is NOT eagerly evaluated at
|
|
21
|
+
* import time (an eager re-export would force every consumer to pay the read
|
|
22
|
+
* cost — and crash if the tarball lacked `captures/`). Call this only when you
|
|
23
|
+
* actually need the captures.
|
|
24
|
+
*/
|
|
25
|
+
export async function loadCaptures() {
|
|
26
|
+
const manifest = await import("./captures/manifest.js");
|
|
27
|
+
return manifest.captures;
|
|
28
|
+
}
|
|
18
29
|
// Pluggable layered reference provider.
|
|
19
30
|
export { createReferenceProvider, CurlImpersonateProvider, RealBrowserCaptureProvider, } from "./reference/reference.js";
|
|
20
31
|
// Fingerprint computation (JA3 / JA4) for Cat 4.
|
|
@@ -22,4 +33,28 @@ export { computeJa3, computeJa4, Ja3ParseError, parseClientHello } from "./finge
|
|
|
22
33
|
// Node.js reference oracle — deterministic comparison target for the primitive
|
|
23
34
|
// layers (crypto, dns, zlib, wire format). See src/reference/node-reference.ts.
|
|
24
35
|
export { nodeCrypto, nodeDns, nodeZlib, nodeHttp, toError, compareBytesOutcome, } from "./reference/node-reference.js";
|
|
36
|
+
// E2E TLS sink server — live handshake verification oracle (plan
|
|
37
|
+
// docs/E2E_TEST_PLAN.md). The sink performs a real TLS 1.3 handshake and
|
|
38
|
+
// returns the raw ClientHello bytes it saw on the wire, plus parsed handshake
|
|
39
|
+
// inspection (protocol, cipher, ALPN, SNI). Reusable by any package that
|
|
40
|
+
// wants to verify its TLS stack against a real server.
|
|
41
|
+
export { TlsSinkServer } from "./e2e/sink-server.js";
|
|
42
|
+
// Self-signed ECDSA P-256 certificate generator (runtime, no openssl).
|
|
43
|
+
export { generateSelfSignedCert } from "./e2e/cert-gen.js";
|
|
44
|
+
// E2E HTTPS traffic server — local TLS termination oracle for the traffic
|
|
45
|
+
// gate. Spins up a real HTTPS server with a throwaway self-signed cert and
|
|
46
|
+
// returns a known body + marker header so a client test can assert the full
|
|
47
|
+
// request path (TCP → TLS → HTTP) worked. Reusable by any package that wants
|
|
48
|
+
// to verify its HTTP stack against a real local server.
|
|
49
|
+
export { TrafficServer } from "./e2e/traffic-server.js";
|
|
50
|
+
// Minimal ClientHello wire parser — structural assertions for the e2e matrix.
|
|
51
|
+
// (Distinct from the JA3 `parseClientHello` re-exported from fingerprint.)
|
|
52
|
+
// Note: `parseClientHello` is intentionally NOT re-exported here to avoid
|
|
53
|
+
// colliding with the JA3 `parseClientHello` from ./fingerprint/index.js.
|
|
54
|
+
// Consumers of the e2e parser import it directly from ./e2e/parse-clienthello.js.
|
|
55
|
+
export { parseClientHelloWire, peekRecordHeader, findExtension, parseSniHostname, parseAlpnProtocols, parseSupportedVersions, decodeSni, decodeAlpn, decodeSupportedVersions, isGrease, nonGreaseCipherSuites, nonGreaseExtensionTypes, EXT, GREASE_SENTINELS, ClientHelloParseError, } from "./e2e/parse-clienthello.js";
|
|
56
|
+
// Provider interfaces + Node-backed factories (Rule 21 — runtime independence).
|
|
57
|
+
// Higher layers depend on the interfaces; the factories return the production
|
|
58
|
+
// defaults. Tests inject fakes through the interfaces.
|
|
59
|
+
export { createFileSystem, createPath, NodeFileSystemProvider, NodePathProvider } from "./node-provider.js";
|
|
25
60
|
//# 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;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC7F,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAEhF,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAchE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE7E,sEAAsE;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,OAAO,KAAK,UAAU,MAAM,uBAAuB,CAAC;AAEpD,gEAAgE;AAChE,OAAO,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAIpD;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY;IAC9B,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,wBAAwB,CAAC,CAAC;IACxD,OAAO,QAAQ,CAAC,QAAQ,CAAC;AAC7B,CAAC;AAED,wCAAwC;AACxC,OAAO,EACH,uBAAuB,EACvB,uBAAuB,EACvB,0BAA0B,GAC7B,MAAM,0BAA0B,CAAC;AASlC,iDAAiD;AACjD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAGjG,+EAA+E;AAC/E,gFAAgF;AAChF,OAAO,EACH,UAAU,EACV,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,mBAAmB,GACtB,MAAM,+BAA+B,CAAC;AAGvC,iEAAiE;AACjE,yEAAyE;AACzE,8EAA8E;AAC9E,yEAAyE;AACzE,uDAAuD;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGrD,uEAAuE;AACvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAG3D,0EAA0E;AAC1E,2EAA2E;AAC3E,4EAA4E;AAC5E,6EAA6E;AAC7E,wDAAwD;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAGxD,8EAA8E;AAC9E,2EAA2E;AAC3E,0EAA0E;AAC1E,yEAAyE;AACzE,kFAAkF;AAClF,OAAO,EACH,oBAAoB,EACpB,gBAAgB,EAChB,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,sBAAsB,EACtB,SAAS,EACT,UAAU,EACV,uBAAuB,EACvB,QAAQ,EACR,qBAAqB,EACrB,uBAAuB,EACvB,GAAG,EACH,gBAAgB,EAChB,qBAAqB,GACxB,MAAM,4BAA4B,CAAC;AAOpC,gFAAgF;AAChF,8EAA8E;AAC9E,uDAAuD;AACvD,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js-backed implementations of the testing package's provider interfaces.
|
|
3
|
+
*
|
|
4
|
+
* This is the ONLY module in @browsercore/testing that may import `node:fs`
|
|
5
|
+
* and `node:path` directly — the provider boundary that isolates the runtime
|
|
6
|
+
* per Rule 21 (CODING_STANDARDS.md "Runtime independence & dependency
|
|
7
|
+
* injection"). Every other module depends on the
|
|
8
|
+
* {@link FileSystemProvider}/{@link PathProvider} interfaces and never touches
|
|
9
|
+
* the runtime built-ins.
|
|
10
|
+
*
|
|
11
|
+
* Higher layers call the provider methods here, never `node:fs`/`node:path`
|
|
12
|
+
* directly, so the backend stays replaceable. Factories return a fresh
|
|
13
|
+
* instance so consumers can call `createFileSystem().readFileSync(...)` without
|
|
14
|
+
* threading a provider through every constructor.
|
|
15
|
+
*/
|
|
16
|
+
import type { FileSystemProvider, PathProvider } from "./provider.js";
|
|
17
|
+
/**
|
|
18
|
+
* `node:fs`-backed implementation of {@link FileSystemProvider}.
|
|
19
|
+
*
|
|
20
|
+
* Production code calls the factory (`createFileSystem()`) — it never
|
|
21
|
+
* constructs this class directly. Tests inject a fake provider through the
|
|
22
|
+
* `FileSystemProvider` interface.
|
|
23
|
+
*/
|
|
24
|
+
export declare class NodeFileSystemProvider implements FileSystemProvider {
|
|
25
|
+
readFileSync(path: string): Uint8Array;
|
|
26
|
+
readFileText(path: string): string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* `node:path`-backed implementation of {@link PathProvider}.
|
|
30
|
+
*
|
|
31
|
+
* Production code calls the factory (`createPath()`) — it never constructs
|
|
32
|
+
* this class directly. Tests inject a fake provider through the
|
|
33
|
+
* `PathProvider` interface.
|
|
34
|
+
*/
|
|
35
|
+
export declare class NodePathProvider implements PathProvider {
|
|
36
|
+
join(...parts: string[]): string;
|
|
37
|
+
}
|
|
38
|
+
/** Factory that returns the default {@link FileSystemProvider}. */
|
|
39
|
+
export declare function createFileSystem(): FileSystemProvider;
|
|
40
|
+
/** Factory that returns the default {@link PathProvider}. */
|
|
41
|
+
export declare function createPath(): PathProvider;
|
|
42
|
+
//# sourceMappingURL=node-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-provider.d.ts","sourceRoot":"","sources":["../src/node-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAEtE;;;;;;GAMG;AACH,qBAAa,sBAAuB,YAAW,kBAAkB;IACtD,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAE5C;IAEM,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAExC;CACJ;AAED;;;;;;GAMG;AACH,qBAAa,gBAAiB,YAAW,YAAY;IAC1C,IAAI,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAEtC;CACJ;AAED,mEAAmE;AACnE,wBAAgB,gBAAgB,IAAI,kBAAkB,CAErD;AAED,6DAA6D;AAC7D,wBAAgB,UAAU,IAAI,YAAY,CAEzC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js-backed implementations of the testing package's provider interfaces.
|
|
3
|
+
*
|
|
4
|
+
* This is the ONLY module in @browsercore/testing that may import `node:fs`
|
|
5
|
+
* and `node:path` directly — the provider boundary that isolates the runtime
|
|
6
|
+
* per Rule 21 (CODING_STANDARDS.md "Runtime independence & dependency
|
|
7
|
+
* injection"). Every other module depends on the
|
|
8
|
+
* {@link FileSystemProvider}/{@link PathProvider} interfaces and never touches
|
|
9
|
+
* the runtime built-ins.
|
|
10
|
+
*
|
|
11
|
+
* Higher layers call the provider methods here, never `node:fs`/`node:path`
|
|
12
|
+
* directly, so the backend stays replaceable. Factories return a fresh
|
|
13
|
+
* instance so consumers can call `createFileSystem().readFileSync(...)` without
|
|
14
|
+
* threading a provider through every constructor.
|
|
15
|
+
*/
|
|
16
|
+
import { readFileSync } from "node:fs";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
/**
|
|
19
|
+
* `node:fs`-backed implementation of {@link FileSystemProvider}.
|
|
20
|
+
*
|
|
21
|
+
* Production code calls the factory (`createFileSystem()`) — it never
|
|
22
|
+
* constructs this class directly. Tests inject a fake provider through the
|
|
23
|
+
* `FileSystemProvider` interface.
|
|
24
|
+
*/
|
|
25
|
+
export class NodeFileSystemProvider {
|
|
26
|
+
readFileSync(path) {
|
|
27
|
+
return readFileSync(path);
|
|
28
|
+
}
|
|
29
|
+
readFileText(path) {
|
|
30
|
+
return readFileSync(path, "utf8");
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* `node:path`-backed implementation of {@link PathProvider}.
|
|
35
|
+
*
|
|
36
|
+
* Production code calls the factory (`createPath()`) — it never constructs
|
|
37
|
+
* this class directly. Tests inject a fake provider through the
|
|
38
|
+
* `PathProvider` interface.
|
|
39
|
+
*/
|
|
40
|
+
export class NodePathProvider {
|
|
41
|
+
join(...parts) {
|
|
42
|
+
return join(...parts);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/** Factory that returns the default {@link FileSystemProvider}. */
|
|
46
|
+
export function createFileSystem() {
|
|
47
|
+
return new NodeFileSystemProvider();
|
|
48
|
+
}
|
|
49
|
+
/** Factory that returns the default {@link PathProvider}. */
|
|
50
|
+
export function createPath() {
|
|
51
|
+
return new NodePathProvider();
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=node-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-provider.js","sourceRoot":"","sources":["../src/node-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC;;;;;;GAMG;AACH,MAAM,OAAO,sBAAsB;IACxB,YAAY,CAAC,IAAY;QAC5B,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IAEM,YAAY,CAAC,IAAY;QAC5B,OAAO,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACtC,CAAC;CACJ;AAED;;;;;;GAMG;AACH,MAAM,OAAO,gBAAgB;IAClB,IAAI,CAAC,GAAG,KAAe;QAC1B,OAAO,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;IAC1B,CAAC;CACJ;AAED,mEAAmE;AACnE,MAAM,UAAU,gBAAgB;IAC5B,OAAO,IAAI,sBAAsB,EAAE,CAAC;AACxC,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,UAAU;IACtB,OAAO,IAAI,gBAAgB,EAAE,CAAC;AAClC,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider interfaces for the @browsercore/testing package.
|
|
3
|
+
*
|
|
4
|
+
* Protocol/test infrastructure layers isolate runtime-specific dependencies
|
|
5
|
+
* (node:fs, node:path) behind these interfaces per Rule 21
|
|
6
|
+
* (CODING_STANDARDS.md "Runtime independence & dependency injection").
|
|
7
|
+
* Higher layers depend on the interfaces — never on a concrete provider — so
|
|
8
|
+
* the backend is replaceable (in-memory fake for tests, Deno/Bun, ...).
|
|
9
|
+
*
|
|
10
|
+
* @see {@link NodeFileSystemProvider} for the default `node:fs` backend.
|
|
11
|
+
* @see {@link NodePathProvider} for the default `node:path` backend.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* File-system read operations.
|
|
15
|
+
*
|
|
16
|
+
* Higher layers depend on this interface, never on `node:fs` directly, so the
|
|
17
|
+
* backend is replaceable. All methods take and return `Uint8Array` or
|
|
18
|
+
* `string` — never Node `Buffer` — so the interface is portable across
|
|
19
|
+
* runtimes.
|
|
20
|
+
*/
|
|
21
|
+
export interface FileSystemProvider {
|
|
22
|
+
/**
|
|
23
|
+
* Read a file's raw bytes.
|
|
24
|
+
*
|
|
25
|
+
* @param path Absolute or relative file path.
|
|
26
|
+
* @returns The file's contents as raw bytes.
|
|
27
|
+
*/
|
|
28
|
+
readFileSync(path: string): Uint8Array;
|
|
29
|
+
/**
|
|
30
|
+
* Read a file's text content (utf8).
|
|
31
|
+
*
|
|
32
|
+
* @param path Absolute or relative file path.
|
|
33
|
+
* @returns The file's contents as a utf8 string.
|
|
34
|
+
*/
|
|
35
|
+
readFileText(path: string): string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Path manipulation operations.
|
|
39
|
+
*
|
|
40
|
+
* Higher layers depend on this interface, never on `node:path` directly, so
|
|
41
|
+
* path resolution is replaceable and testable against synthetic layouts.
|
|
42
|
+
*/
|
|
43
|
+
export interface PathProvider {
|
|
44
|
+
/**
|
|
45
|
+
* Join path segments into a single path.
|
|
46
|
+
*
|
|
47
|
+
* @param parts Path segments to join.
|
|
48
|
+
* @returns The joined path.
|
|
49
|
+
*/
|
|
50
|
+
join(...parts: string[]): string;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;;;;GAOG;AACH,MAAM,WAAW,kBAAkB;IAC/B;;;;;OAKG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC;IAEvC;;;;;OAKG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CACtC;AAED;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IACzB;;;;;OAKG;IACH,IAAI,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;CACpC"}
|
package/dist/provider.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider interfaces for the @browsercore/testing package.
|
|
3
|
+
*
|
|
4
|
+
* Protocol/test infrastructure layers isolate runtime-specific dependencies
|
|
5
|
+
* (node:fs, node:path) behind these interfaces per Rule 21
|
|
6
|
+
* (CODING_STANDARDS.md "Runtime independence & dependency injection").
|
|
7
|
+
* Higher layers depend on the interfaces — never on a concrete provider — so
|
|
8
|
+
* the backend is replaceable (in-memory fake for tests, Deno/Bun, ...).
|
|
9
|
+
*
|
|
10
|
+
* @see {@link NodeFileSystemProvider} for the default `node:fs` backend.
|
|
11
|
+
* @see {@link NodePathProvider} for the default `node:path` backend.
|
|
12
|
+
*/
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=provider.js.map
|