@browsercore/testing 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +98 -0
- package/dist/bench/bench.d.ts +21 -0
- package/dist/bench/bench.d.ts.map +1 -0
- package/dist/bench/bench.js +19 -0
- package/dist/bench/bench.js.map +1 -0
- package/dist/captures/manifest.d.ts +18 -0
- package/dist/captures/manifest.d.ts.map +1 -0
- package/dist/captures/manifest.js +62 -0
- package/dist/captures/manifest.js.map +1 -0
- package/dist/categories/capture.d.ts +8 -0
- package/dist/categories/capture.d.ts.map +1 -0
- package/dist/categories/capture.js +31 -0
- package/dist/categories/capture.js.map +1 -0
- package/dist/categories/compression.d.ts +8 -0
- package/dist/categories/compression.d.ts.map +1 -0
- package/dist/categories/compression.js +15 -0
- package/dist/categories/compression.js.map +1 -0
- package/dist/categories/cookies.d.ts +8 -0
- package/dist/categories/cookies.d.ts.map +1 -0
- package/dist/categories/cookies.js +20 -0
- package/dist/categories/cookies.js.map +1 -0
- package/dist/categories/errors.d.ts +11 -0
- package/dist/categories/errors.d.ts.map +1 -0
- package/dist/categories/errors.js +41 -0
- package/dist/categories/errors.js.map +1 -0
- package/dist/categories/http.d.ts +9 -0
- package/dist/categories/http.d.ts.map +1 -0
- package/dist/categories/http.js +46 -0
- package/dist/categories/http.js.map +1 -0
- package/dist/categories/index.d.ts +17 -0
- package/dist/categories/index.d.ts.map +1 -0
- package/dist/categories/index.js +17 -0
- package/dist/categories/index.js.map +1 -0
- package/dist/categories/redirects.d.ts +8 -0
- package/dist/categories/redirects.d.ts.map +1 -0
- package/dist/categories/redirects.js +21 -0
- package/dist/categories/redirects.js.map +1 -0
- package/dist/categories/regression.d.ts +8 -0
- package/dist/categories/regression.d.ts.map +1 -0
- package/dist/categories/regression.js +28 -0
- package/dist/categories/regression.js.map +1 -0
- package/dist/categories/session.d.ts +8 -0
- package/dist/categories/session.d.ts.map +1 -0
- package/dist/categories/session.js +23 -0
- package/dist/categories/session.js.map +1 -0
- package/dist/categories/tls.d.ts +10 -0
- package/dist/categories/tls.d.ts.map +1 -0
- package/dist/categories/tls.js +48 -0
- package/dist/categories/tls.js.map +1 -0
- package/dist/categories/transport.d.ts +8 -0
- package/dist/categories/transport.d.ts.map +1 -0
- package/dist/categories/transport.js +22 -0
- package/dist/categories/transport.js.map +1 -0
- package/dist/errors.d.ts +22 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +28 -0
- package/dist/errors.js.map +1 -0
- package/dist/fingerprint/index.d.ts +5 -0
- package/dist/fingerprint/index.d.ts.map +1 -0
- package/dist/fingerprint/index.js +4 -0
- package/dist/fingerprint/index.js.map +1 -0
- package/dist/fingerprint/ja3.d.ts +36 -0
- package/dist/fingerprint/ja3.d.ts.map +1 -0
- package/dist/fingerprint/ja3.js +141 -0
- package/dist/fingerprint/ja3.js.map +1 -0
- package/dist/fingerprint/ja4.d.ts +109 -0
- package/dist/fingerprint/ja4.d.ts.map +1 -0
- package/dist/fingerprint/ja4.js +325 -0
- package/dist/fingerprint/ja4.js.map +1 -0
- package/dist/golden/golden.d.ts +45 -0
- package/dist/golden/golden.d.ts.map +1 -0
- package/dist/golden/golden.js +216 -0
- package/dist/golden/golden.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/reference/node-reference.d.ts +83 -0
- package/dist/reference/node-reference.d.ts.map +1 -0
- package/dist/reference/node-reference.js +161 -0
- package/dist/reference/node-reference.js.map +1 -0
- package/dist/reference/reference.d.ts +143 -0
- package/dist/reference/reference.d.ts.map +1 -0
- package/dist/reference/reference.js +323 -0
- package/dist/reference/reference.js.map +1 -0
- package/dist/rfc/rfcTests.d.ts +14 -0
- package/dist/rfc/rfcTests.d.ts.map +1 -0
- package/dist/rfc/rfcTests.js +22 -0
- package/dist/rfc/rfcTests.js.map +1 -0
- package/dist/types.d.ts +140 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +34 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +29 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +102 -0
- package/dist/utils.js.map +1 -0
- package/package.json +53 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @browsercore/testing — public API surface.
|
|
3
|
+
*
|
|
4
|
+
* Protocol verification: RFC compliance tests, browser golden packet captures,
|
|
5
|
+
* integration tests, and benchmarking.
|
|
6
|
+
*/
|
|
7
|
+
export { loadGolden, compareAgainstGolden } from "./golden/golden.js";
|
|
8
|
+
export { runTlsCompliance, runHttp2Compliance, runHttp1Compliance } from "./rfc/rfcTests.js";
|
|
9
|
+
export { benchmarkTlsHandshake, benchmarkHttp2Request } from "./bench/bench.js";
|
|
10
|
+
export { GoldenMismatchError, TestingError } from "./errors.js";
|
|
11
|
+
export { assertNever, bytesToHex, compareBytes, createId } from "./utils.js";
|
|
12
|
+
// Test-category model (docs/TEST-SUITE.md) + the 17 vitest suite ids.
|
|
13
|
+
export { TestCategory } from "./types.js";
|
|
14
|
+
export * as categories from "./categories/index.js";
|
|
15
|
+
// Randomized-field comparison (Cat 14 core) + capture manifest.
|
|
16
|
+
export { compareBytesWithIgnore } from "./utils.js";
|
|
17
|
+
export { captures } from "./captures/manifest.js";
|
|
18
|
+
// Pluggable layered reference provider.
|
|
19
|
+
export { createReferenceProvider, CurlImpersonateProvider, RealBrowserCaptureProvider, } from "./reference/reference.js";
|
|
20
|
+
// Fingerprint computation (JA3 / JA4) for Cat 4.
|
|
21
|
+
export { computeJa3, computeJa4, Ja3ParseError, parseClientHello } from "./fingerprint/index.js";
|
|
22
|
+
// Node.js reference oracle — deterministic comparison target for the primitive
|
|
23
|
+
// layers (crypto, dns, zlib, wire format). See src/reference/node-reference.ts.
|
|
24
|
+
export { nodeCrypto, nodeDns, nodeZlib, nodeHttp, toError, compareBytesOutcome, } from "./reference/node-reference.js";
|
|
25
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","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;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;AAEpD,OAAO,EAAE,QAAQ,EAAqB,MAAM,wBAAwB,CAAC;AAErE,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"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js reference oracle.
|
|
3
|
+
*
|
|
4
|
+
* Wraps Node's built-in implementations (node:crypto, node:zlib, node:dns,
|
|
5
|
+
* node:net, node:http, node:http2, node:tls) behind a uniform comparator surface
|
|
6
|
+
* so our @browsercore/* packages can be tested for EQUIVALENT observable behavior.
|
|
7
|
+
*
|
|
8
|
+
* Design rule: we compare against Node only for LAYERS WHERE NODE IS THE SPEC
|
|
9
|
+
* REFERENCE — crypto primitives, DNS resolution, compression, and wire-format
|
|
10
|
+
* serialization. We deliberately do NOT compare browser-fingerprint layers
|
|
11
|
+
* (TLS ClientHello, HTTP/2 SETTINGS, default headers) against Node, because the
|
|
12
|
+
* whole point of this project is to diverge from Node to match browsers. Those
|
|
13
|
+
* are validated against browser golden captures elsewhere.
|
|
14
|
+
*
|
|
15
|
+
* Every method returns Node Buffer/values that our tests convert to Uint8Array
|
|
16
|
+
* for comparison with our packages' Uint8Array-centric APIs.
|
|
17
|
+
*/
|
|
18
|
+
import { constants as zlibConstants } from "node:zlib";
|
|
19
|
+
/** Which hash algorithm a primitive uses — branded union, no magic strings. */
|
|
20
|
+
export type HashAlgorithm = "sha256" | "sha384";
|
|
21
|
+
/** Validated HKDF/hash helpers that mirror the @browsercore/crypto interface. */
|
|
22
|
+
export declare const nodeCrypto: {
|
|
23
|
+
/** SHA-256 digest. Equivalent oracle for our `crypto.sha256`. */
|
|
24
|
+
readonly sha256: (data: Uint8Array) => Uint8Array;
|
|
25
|
+
/** SHA-384 digest. Equivalent oracle for our `crypto.sha384`. */
|
|
26
|
+
readonly sha384: (data: Uint8Array) => Uint8Array;
|
|
27
|
+
/**
|
|
28
|
+
* HKDF extract+expand per RFC 5869. Equivalent oracle for our `crypto.hkdf`.
|
|
29
|
+
* Uses node:crypto.hkdfSync under the hood.
|
|
30
|
+
*/
|
|
31
|
+
readonly hkdf: (algorithm: HashAlgorithm, salt: Uint8Array, ikm: Uint8Array, info: Uint8Array, length: number) => Uint8Array;
|
|
32
|
+
/** HMAC. Equivalent oracle for our `crypto.hmac`. */
|
|
33
|
+
readonly hmac: (algorithm: HashAlgorithm, key: Uint8Array, data: Uint8Array) => Uint8Array;
|
|
34
|
+
/** Cryptographically secure random bytes (length oracle). */
|
|
35
|
+
readonly randomBytes: (length: number) => Uint8Array;
|
|
36
|
+
/** Expected digest length for a given hash algorithm. */
|
|
37
|
+
readonly digestLength: (algorithm: HashAlgorithm) => number;
|
|
38
|
+
};
|
|
39
|
+
/** DNS resolution oracle (mirrors our `resolveHost`). */
|
|
40
|
+
export declare const nodeDns: {
|
|
41
|
+
/** Resolve a host to an address, matching our `resolveHost` semantics. */
|
|
42
|
+
readonly lookup: (host: string, ipv6: boolean) => Promise<{
|
|
43
|
+
readonly address: string;
|
|
44
|
+
readonly family: 4 | 6;
|
|
45
|
+
}>;
|
|
46
|
+
};
|
|
47
|
+
/** Compression oracle (mirrors the future @browsercore/compression interface). */
|
|
48
|
+
export declare const nodeZlib: {
|
|
49
|
+
readonly gzip: (data: Uint8Array) => Uint8Array;
|
|
50
|
+
readonly gunzip: (data: Uint8Array) => Uint8Array;
|
|
51
|
+
readonly deflate: (data: Uint8Array) => Uint8Array;
|
|
52
|
+
readonly inflate: (data: Uint8Array) => Uint8Array;
|
|
53
|
+
readonly brotliCompress: (data: Uint8Array) => Uint8Array;
|
|
54
|
+
readonly brotliDecompress: (data: Uint8Array) => Uint8Array;
|
|
55
|
+
/** The zlib window bits constant, for comparing deflate framing decisions. */
|
|
56
|
+
readonly zlibConstants: typeof zlibConstants;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Wire-format oracle for HTTP/1.1. Node's `http` module is the de-facto
|
|
60
|
+
* reference for the on-the-wire request/response format (RFC 7230). We use it
|
|
61
|
+
* to assert our serializer/parser produce bytes Node would accept and generate
|
|
62
|
+
* the same wire layout.
|
|
63
|
+
*/
|
|
64
|
+
export declare const nodeHttp: {
|
|
65
|
+
/**
|
|
66
|
+
* Build the request-line + headers block exactly as Node's http client would
|
|
67
|
+
* serialize them, so we can compare against our `serializeRequest`.
|
|
68
|
+
* Body is appended verbatim by the caller.
|
|
69
|
+
*/
|
|
70
|
+
readonly serializeRequestLineAndHeaders: (method: string, url: string, headers: ReadonlyMap<string, string>) => Uint8Array;
|
|
71
|
+
};
|
|
72
|
+
/** Narrow an unknown thrown value to an Error (mirrors transport.ensure*). */
|
|
73
|
+
export declare function toError(e: unknown): Error;
|
|
74
|
+
/** Discriminated result of a reference comparison. */
|
|
75
|
+
export type CompareOutcome = {
|
|
76
|
+
readonly equal: true;
|
|
77
|
+
} | {
|
|
78
|
+
readonly equal: false;
|
|
79
|
+
readonly reason: string;
|
|
80
|
+
};
|
|
81
|
+
/** Generic byte-level comparison returning a discriminated outcome. */
|
|
82
|
+
export declare function compareBytesOutcome(a: Uint8Array, b: Uint8Array): CompareOutcome;
|
|
83
|
+
//# sourceMappingURL=node-reference.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-reference.d.ts","sourceRoot":"","sources":["../../src/reference/node-reference.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,OAAO,EAA4B,SAAS,IAAI,aAAa,EAAE,MAAM,WAAW,CAAC;AAqBjF,+EAA+E;AAC/E,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAGhD,iFAAiF;AACjF,eAAO,MAAM,UAAU;IACnB,iEAAiE;4BACpD,UAAU,KAAG,UAAU;IAIpC,iEAAiE;4BACpD,UAAU,KAAG,UAAU;IAIpC;;;OAGG;+BAEY,aAAa,QAClB,UAAU,OACX,UAAU,QACT,UAAU,UACR,MAAM,KACf,UAAU;IAOb,qDAAqD;+BACrC,aAAa,OAAO,UAAU,QAAQ,UAAU,KAAG,UAAU;IAK7E,6DAA6D;mCACzC,MAAM,KAAG,UAAU;IAIvC,yDAAyD;uCACjC,aAAa,KAAG,MAAM;CAGxC,CAAC;AAEX,yDAAyD;AACzD,eAAO,MAAM,OAAO;IAChB,0EAA0E;4BAEhE,MAAM,QACN,OAAO,KACd,OAAO,CAAC;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAA;KAAE,CAAC;CAY1D,CAAC;AAEX,kFAAkF;AAClF,eAAO,MAAM,QAAQ;0BACN,UAAU,KAAG,UAAU;4BAGrB,UAAU,KAAG,UAAU;6BAGtB,UAAU,KAAG,UAAU;6BAGvB,UAAU,KAAG,UAAU;oCAGhB,UAAU,KAAG,UAAU;sCAGrB,UAAU,KAAG,UAAU;IAG9C,8EAA8E;4BACzD,OAAO,aAAa;CAGnC,CAAC;AAEX;;;;;GAKG;AACH,eAAO,MAAM,QAAQ;IACjB;;;;OAIG;sDAES,MAAM,OACT,MAAM,WACF,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,KACrC,UAAU;CAQP,CAAC;AAEX,8EAA8E;AAC9E,wBAAgB,OAAO,CAAC,CAAC,EAAE,OAAO,GAAG,KAAK,CAKzC;AAED,sDAAsD;AACtD,MAAM,MAAM,cAAc,GACpB;IAAE,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAA;CAAE,GACxB;IAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzD,uEAAuE;AACvE,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,GAAG,cAAc,CAUhF"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js reference oracle.
|
|
3
|
+
*
|
|
4
|
+
* Wraps Node's built-in implementations (node:crypto, node:zlib, node:dns,
|
|
5
|
+
* node:net, node:http, node:http2, node:tls) behind a uniform comparator surface
|
|
6
|
+
* so our @browsercore/* packages can be tested for EQUIVALENT observable behavior.
|
|
7
|
+
*
|
|
8
|
+
* Design rule: we compare against Node only for LAYERS WHERE NODE IS THE SPEC
|
|
9
|
+
* REFERENCE — crypto primitives, DNS resolution, compression, and wire-format
|
|
10
|
+
* serialization. We deliberately do NOT compare browser-fingerprint layers
|
|
11
|
+
* (TLS ClientHello, HTTP/2 SETTINGS, default headers) against Node, because the
|
|
12
|
+
* whole point of this project is to diverge from Node to match browsers. Those
|
|
13
|
+
* are validated against browser golden captures elsewhere.
|
|
14
|
+
*
|
|
15
|
+
* Every method returns Node Buffer/values that our tests convert to Uint8Array
|
|
16
|
+
* for comparison with our packages' Uint8Array-centric APIs.
|
|
17
|
+
*/
|
|
18
|
+
import { createHash, createHmac, hkdfSync, randomBytes } from "node:crypto";
|
|
19
|
+
import { gunzipSync, gzipSync, brotliCompressSync, brotliDecompressSync } from "node:zlib";
|
|
20
|
+
import { lookup as dnsLookup } from "node:dns";
|
|
21
|
+
import { deflateSync, inflateSync, constants as zlibConstants } from "node:zlib";
|
|
22
|
+
import { assertNever } from "../utils.js";
|
|
23
|
+
/** Normalize a Node Buffer / Uint8Array / readonly Uint8Array to a fresh Uint8Array. */
|
|
24
|
+
function toBytes(data) {
|
|
25
|
+
return new Uint8Array(data);
|
|
26
|
+
}
|
|
27
|
+
/** Compare two byte arrays for equality (helper for one-off oracle checks). */
|
|
28
|
+
function bytesEqual(a, b) {
|
|
29
|
+
if (a.length !== b.length) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
for (let i = 0; i < a.length; i++) {
|
|
33
|
+
if (a[i] !== b[i]) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
const HASH_LENGTHS = { sha256: 32, sha384: 48 };
|
|
40
|
+
/** Validated HKDF/hash helpers that mirror the @browsercore/crypto interface. */
|
|
41
|
+
export const nodeCrypto = {
|
|
42
|
+
/** SHA-256 digest. Equivalent oracle for our `crypto.sha256`. */
|
|
43
|
+
sha256(data) {
|
|
44
|
+
return toBytes(createHash("sha256").update(data).digest());
|
|
45
|
+
},
|
|
46
|
+
/** SHA-384 digest. Equivalent oracle for our `crypto.sha384`. */
|
|
47
|
+
sha384(data) {
|
|
48
|
+
return toBytes(createHash("sha384").update(data).digest());
|
|
49
|
+
},
|
|
50
|
+
/**
|
|
51
|
+
* HKDF extract+expand per RFC 5869. Equivalent oracle for our `crypto.hkdf`.
|
|
52
|
+
* Uses node:crypto.hkdfSync under the hood.
|
|
53
|
+
*/
|
|
54
|
+
hkdf(algorithm, salt, ikm, info, length) {
|
|
55
|
+
const algo = algorithm === "sha256" ? "sha256" : "sha384";
|
|
56
|
+
return toBytes(hkdfSync(algo, ikm, salt, info, length));
|
|
57
|
+
},
|
|
58
|
+
/** HMAC. Equivalent oracle for our `crypto.hmac`. */
|
|
59
|
+
hmac(algorithm, key, data) {
|
|
60
|
+
const algo = algorithm === "sha256" ? "sha256" : "sha384";
|
|
61
|
+
return toBytes(createHmac(algo, key).update(data).digest());
|
|
62
|
+
},
|
|
63
|
+
/** Cryptographically secure random bytes (length oracle). */
|
|
64
|
+
randomBytes(length) {
|
|
65
|
+
return toBytes(randomBytes(length));
|
|
66
|
+
},
|
|
67
|
+
/** Expected digest length for a given hash algorithm. */
|
|
68
|
+
digestLength(algorithm) {
|
|
69
|
+
return HASH_LENGTHS[algorithm];
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
/** DNS resolution oracle (mirrors our `resolveHost`). */
|
|
73
|
+
export const nodeDns = {
|
|
74
|
+
/** Resolve a host to an address, matching our `resolveHost` semantics. */
|
|
75
|
+
async lookup(host, ipv6) {
|
|
76
|
+
return new Promise((resolve, reject) => {
|
|
77
|
+
const options = { family: ipv6 ? 6 : 4 };
|
|
78
|
+
dnsLookup(host, options, (err, address, family) => {
|
|
79
|
+
if (err) {
|
|
80
|
+
reject(err);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
resolve({ address, family: family });
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
/** Compression oracle (mirrors the future @browsercore/compression interface). */
|
|
89
|
+
export const nodeZlib = {
|
|
90
|
+
gzip(data) {
|
|
91
|
+
return toBytes(gzipSync(data));
|
|
92
|
+
},
|
|
93
|
+
gunzip(data) {
|
|
94
|
+
return toBytes(gunzipSync(data));
|
|
95
|
+
},
|
|
96
|
+
deflate(data) {
|
|
97
|
+
return toBytes(deflateSync(data));
|
|
98
|
+
},
|
|
99
|
+
inflate(data) {
|
|
100
|
+
return toBytes(inflateSync(data));
|
|
101
|
+
},
|
|
102
|
+
brotliCompress(data) {
|
|
103
|
+
return toBytes(brotliCompressSync(data));
|
|
104
|
+
},
|
|
105
|
+
brotliDecompress(data) {
|
|
106
|
+
return toBytes(brotliDecompressSync(data));
|
|
107
|
+
},
|
|
108
|
+
/** The zlib window bits constant, for comparing deflate framing decisions. */
|
|
109
|
+
get zlibConstants() {
|
|
110
|
+
return zlibConstants;
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
/**
|
|
114
|
+
* Wire-format oracle for HTTP/1.1. Node's `http` module is the de-facto
|
|
115
|
+
* reference for the on-the-wire request/response format (RFC 7230). We use it
|
|
116
|
+
* to assert our serializer/parser produce bytes Node would accept and generate
|
|
117
|
+
* the same wire layout.
|
|
118
|
+
*/
|
|
119
|
+
export const nodeHttp = {
|
|
120
|
+
/**
|
|
121
|
+
* Build the request-line + headers block exactly as Node's http client would
|
|
122
|
+
* serialize them, so we can compare against our `serializeRequest`.
|
|
123
|
+
* Body is appended verbatim by the caller.
|
|
124
|
+
*/
|
|
125
|
+
serializeRequestLineAndHeaders(method, url, headers) {
|
|
126
|
+
const lines = [`${method.toUpperCase()} ${url} HTTP/1.1`];
|
|
127
|
+
for (const [name, value] of headers) {
|
|
128
|
+
lines.push(`${name}: ${value}`);
|
|
129
|
+
}
|
|
130
|
+
// Node terminates the header section with a single blank line.
|
|
131
|
+
return new TextEncoder().encode(`${lines.join("\r\n")}\r\n\r\n`);
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
/** Narrow an unknown thrown value to an Error (mirrors transport.ensure*). */
|
|
135
|
+
export function toError(e) {
|
|
136
|
+
if (e instanceof Error) {
|
|
137
|
+
return e;
|
|
138
|
+
}
|
|
139
|
+
return new Error(typeof e === "string" ? e : "unknown error");
|
|
140
|
+
}
|
|
141
|
+
/** Generic byte-level comparison returning a discriminated outcome. */
|
|
142
|
+
export function compareBytesOutcome(a, b) {
|
|
143
|
+
if (bytesEqual(a, b)) {
|
|
144
|
+
return { equal: true };
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
equal: false,
|
|
148
|
+
reason: `length ${a.length} vs ${b.length}${a.length === b.length ? `, first diff at byte ${firstDiff(a, b)}` : ""}`,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function firstDiff(a, b) {
|
|
152
|
+
const len = Math.min(a.length, b.length);
|
|
153
|
+
for (let i = 0; i < len; i++) {
|
|
154
|
+
if (a[i] !== b[i]) {
|
|
155
|
+
return i;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return len;
|
|
159
|
+
}
|
|
160
|
+
void assertNever;
|
|
161
|
+
//# sourceMappingURL=node-reference.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-reference.js","sourceRoot":"","sources":["../../src/reference/node-reference.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAC3F,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,UAAU,CAAC;AAE/C,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,WAAW,CAAC;AACjF,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,wFAAwF;AACxF,SAAS,OAAO,CAAC,IAAyB;IACtC,OAAO,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;AAChC,CAAC;AAED,+EAA+E;AAC/E,SAAS,UAAU,CAAC,CAAa,EAAE,CAAa;IAC5C,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC;QACxB,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAChC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAChB,OAAO,KAAK,CAAC;QACjB,CAAC;IACL,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC;AAID,MAAM,YAAY,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAW,CAAC;AAEzD,iFAAiF;AACjF,MAAM,CAAC,MAAM,UAAU,GAAG;IACtB,iEAAiE;IACjE,MAAM,CAAC,IAAgB;QACnB,OAAO,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/D,CAAC;IAED,iEAAiE;IACjE,MAAM,CAAC,IAAgB;QACnB,OAAO,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/D,CAAC;IAED;;;OAGG;IACH,IAAI,CACA,SAAwB,EACxB,IAAgB,EAChB,GAAe,EACf,IAAgB,EAChB,MAAc;QAEd,MAAM,IAAI,GAAG,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC1D,OAAO,OAAO,CACV,QAAQ,CAAC,IAAI,EAAE,GAAa,EAAE,IAAc,EAAE,IAAc,EAAE,MAAM,CAA0B,CACjG,CAAC;IACN,CAAC;IAED,qDAAqD;IACrD,IAAI,CAAC,SAAwB,EAAE,GAAe,EAAE,IAAgB;QAC5D,MAAM,IAAI,GAAG,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC1D,OAAO,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE,GAAa,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAC1E,CAAC;IAED,6DAA6D;IAC7D,WAAW,CAAC,MAAc;QACtB,OAAO,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;IACxC,CAAC;IAED,yDAAyD;IACzD,YAAY,CAAC,SAAwB;QACjC,OAAO,YAAY,CAAC,SAAS,CAAC,CAAC;IACnC,CAAC;CACK,CAAC;AAEX,yDAAyD;AACzD,MAAM,CAAC,MAAM,OAAO,GAAG;IACnB,0EAA0E;IAC1E,KAAK,CAAC,MAAM,CACR,IAAY,EACZ,IAAa;QAEb,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACnC,MAAM,OAAO,GAAqB,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3D,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE;gBAC9C,IAAI,GAAG,EAAE,CAAC;oBACN,MAAM,CAAC,GAAG,CAAC,CAAC;oBACZ,OAAO;gBACX,CAAC;gBACD,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,EAAG,MAAgB,EAAE,CAAC,CAAC;YACpD,CAAC,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;IACP,CAAC;CACK,CAAC;AAEX,kFAAkF;AAClF,MAAM,CAAC,MAAM,QAAQ,GAAG;IACpB,IAAI,CAAC,IAAgB;QACjB,OAAO,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;IACnC,CAAC;IACD,MAAM,CAAC,IAAgB;QACnB,OAAO,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,CAAC,IAAgB;QACpB,OAAO,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,CAAC,IAAgB;QACpB,OAAO,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;IACtC,CAAC;IACD,cAAc,CAAC,IAAgB;QAC3B,OAAO,OAAO,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7C,CAAC;IACD,gBAAgB,CAAC,IAAgB;QAC7B,OAAO,OAAO,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/C,CAAC;IACD,8EAA8E;IAC9E,IAAI,aAAa;QACb,OAAO,aAAa,CAAC;IACzB,CAAC;CACK,CAAC;AAEX;;;;;GAKG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG;IACpB;;;;OAIG;IACH,8BAA8B,CAC1B,MAAc,EACd,GAAW,EACX,OAAoC;QAEpC,MAAM,KAAK,GAAa,CAAC,GAAG,MAAM,CAAC,WAAW,EAAE,IAAI,GAAG,WAAW,CAAC,CAAC;QACpE,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,OAAO,EAAE,CAAC;YAClC,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,KAAK,KAAK,EAAE,CAAC,CAAC;QACpC,CAAC;QACD,+DAA+D;QAC/D,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACrE,CAAC;CACK,CAAC;AAEX,8EAA8E;AAC9E,MAAM,UAAU,OAAO,CAAC,CAAU;IAC9B,IAAI,CAAC,YAAY,KAAK,EAAE,CAAC;QACrB,OAAO,CAAC,CAAC;IACb,CAAC;IACD,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;AAClE,CAAC;AAOD,uEAAuE;AACvE,MAAM,UAAU,mBAAmB,CAAC,CAAa,EAAE,CAAa;IAC5D,IAAI,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;QACnB,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAC3B,CAAC;IACD,OAAO;QACH,KAAK,EAAE,KAAK;QACZ,MAAM,EAAE,UAAU,CAAC,CAAC,MAAM,OAAO,CAAC,CAAC,MAAM,GACrC,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,wBAAwB,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EACxE,EAAE;KACL,CAAC;AACN,CAAC;AAED,SAAS,SAAS,CAAC,CAAa,EAAE,CAAa;IAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;IACzC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAChB,OAAO,CAAC,CAAC;QACb,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED,KAAK,WAAW,CAAC"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pluggable layered reference provider.
|
|
3
|
+
*
|
|
4
|
+
* The reference implementation is the source of truth for what a given browser
|
|
5
|
+
* profile SHOULD produce on the wire. Three backends are wrapped behind one
|
|
6
|
+
* facade:
|
|
7
|
+
*
|
|
8
|
+
* - `node-reference` (oracle) — Node's built-in crypto/dns/zlib/http
|
|
9
|
+
* implementations, used as the spec oracle for primitive layers where Node IS
|
|
10
|
+
* the reference (see `node-reference.ts`).
|
|
11
|
+
* - `curl-impersonate` (PRIMARY) — fast CI path; shells out to the
|
|
12
|
+
* curl-impersonate binary to capture real traffic for a profile.
|
|
13
|
+
* - `real-browser` (SECONDARY) — loads pre-recorded captures from the
|
|
14
|
+
* `captures/` directory, recorded from an actual browser.
|
|
15
|
+
*
|
|
16
|
+
* The {@link ReferenceProviderFacade} ties them together: capture resolves via
|
|
17
|
+
* primary then secondary; fingerprint is derived from the captured bytes; the
|
|
18
|
+
* node-reference oracle is exposed for primitive-layer comparisons.
|
|
19
|
+
*
|
|
20
|
+
* See docs/TEST-SUITE.md (Cat 3, 4, 14) for how references feed comparison.
|
|
21
|
+
*/
|
|
22
|
+
import type { ProfileId } from "@browsercore/profiles";
|
|
23
|
+
import type { GoldenCapture } from "../types.js";
|
|
24
|
+
import { TestingError } from "../errors.js";
|
|
25
|
+
/** Which reference backend to use. Discriminated union — no bare string. */
|
|
26
|
+
export type ReferenceProviderKind = {
|
|
27
|
+
readonly kind: "curl-impersonate";
|
|
28
|
+
} | {
|
|
29
|
+
readonly kind: "real-browser";
|
|
30
|
+
};
|
|
31
|
+
/** Observable TLS/HTTP fingerprint of a captured reference exchange. */
|
|
32
|
+
export interface Fingerprint {
|
|
33
|
+
readonly ja3: string;
|
|
34
|
+
readonly ja4: string;
|
|
35
|
+
readonly alpn: readonly string[];
|
|
36
|
+
readonly cipherSuite: string;
|
|
37
|
+
readonly protocolVersion: string;
|
|
38
|
+
readonly signatureAlgorithms: readonly string[];
|
|
39
|
+
readonly ellipticCurves: readonly string[];
|
|
40
|
+
}
|
|
41
|
+
/** Options for the curl-impersonate provider. */
|
|
42
|
+
export interface CurlImpersonateOptions {
|
|
43
|
+
/** Path / name of the curl-impersonate binary. Default "curl-impersonate". */
|
|
44
|
+
readonly command: string;
|
|
45
|
+
/** Extra argv passed to the binary. */
|
|
46
|
+
readonly extraArgs?: readonly string[];
|
|
47
|
+
}
|
|
48
|
+
/** Options for the real-browser (pre-recorded capture) provider. */
|
|
49
|
+
export interface RealBrowserOptions {
|
|
50
|
+
readonly command?: undefined;
|
|
51
|
+
/** Override captures directory. Defaults to the in-repo `captures/` dir. */
|
|
52
|
+
readonly capturesDir?: string;
|
|
53
|
+
}
|
|
54
|
+
/** Options for the {@link ReferenceProviderFacade}. */
|
|
55
|
+
export interface ReferenceFacadeOptions {
|
|
56
|
+
readonly curl?: CurlImpersonateOptions;
|
|
57
|
+
readonly browser?: RealBrowserOptions;
|
|
58
|
+
}
|
|
59
|
+
/** A source of truth for a browser profile's wire behavior. */
|
|
60
|
+
export interface ReferenceProvider {
|
|
61
|
+
readonly kind: ReferenceProviderKind;
|
|
62
|
+
capture(profile: ProfileId, url: string): Promise<GoldenCapture>;
|
|
63
|
+
fingerprint(profile: ProfileId): Promise<Fingerprint>;
|
|
64
|
+
availableProfiles(): ProfileId[];
|
|
65
|
+
}
|
|
66
|
+
/** Raised when a reference provider cannot capture or fingerprint a profile. */
|
|
67
|
+
export declare class ReferenceError extends TestingError {
|
|
68
|
+
constructor(message: string, options?: {
|
|
69
|
+
cause?: Error;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* PRIMARY provider — shells out to curl-impersonate.
|
|
74
|
+
*
|
|
75
|
+
* `capture()` invokes the curl-impersonate binary against `url`, impersonating
|
|
76
|
+
* `profile`, and returns the raw bytes it observed on the wire. The binary
|
|
77
|
+
* must be on PATH (or in `command`); if it is missing, the call throws
|
|
78
|
+
* {@link ReferenceError} so the facade can fall back to the secondary.
|
|
79
|
+
*/
|
|
80
|
+
export declare class CurlImpersonateProvider implements ReferenceProvider {
|
|
81
|
+
readonly kind: ReferenceProviderKind;
|
|
82
|
+
readonly command: string;
|
|
83
|
+
readonly extraArgs: readonly string[];
|
|
84
|
+
constructor(options?: CurlImpersonateOptions);
|
|
85
|
+
availableProfiles(): ProfileId[];
|
|
86
|
+
capture(profile: ProfileId, url: string): Promise<GoldenCapture>;
|
|
87
|
+
fingerprint(profile: ProfileId): Promise<Fingerprint>;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* SECONDARY provider — loads pre-recorded captures from the `captures/` dir.
|
|
91
|
+
*
|
|
92
|
+
* `capture()` resolves a stored capture for the profile; `fingerprint()`
|
|
93
|
+
* derives a {@link Fingerprint} from the captured bytes (only TLS
|
|
94
|
+
* ClientHellos are fingerprinted — other protocols return a stub).
|
|
95
|
+
*/
|
|
96
|
+
export declare class RealBrowserCaptureProvider implements ReferenceProvider {
|
|
97
|
+
readonly kind: ReferenceProviderKind;
|
|
98
|
+
readonly capturesDir: string;
|
|
99
|
+
constructor(options?: RealBrowserOptions);
|
|
100
|
+
availableProfiles(): ProfileId[];
|
|
101
|
+
capture(profile: ProfileId, _url: string): Promise<GoldenCapture>;
|
|
102
|
+
fingerprint(profile: ProfileId): Promise<Fingerprint>;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Facade wrapping the node-reference oracle + primary (curl-impersonate) and
|
|
106
|
+
* secondary (real-browser) providers behind a single {@link ReferenceProvider}.
|
|
107
|
+
*
|
|
108
|
+
* `capture()` tries the primary first; if it fails (binary missing, network
|
|
109
|
+
* error, ...) it falls back to the secondary's pre-recorded captures. The
|
|
110
|
+
* node-reference oracle is exposed via {@link ReferenceProviderFacade.nodeOracle}
|
|
111
|
+
* for primitive-layer comparisons (crypto, dns, zlib, wire format) — those
|
|
112
|
+
* layers are NOT fingerprinted here; they live in `node-reference.ts`.
|
|
113
|
+
*/
|
|
114
|
+
export declare class ReferenceProviderFacade implements ReferenceProvider {
|
|
115
|
+
readonly kind: ReferenceProviderKind;
|
|
116
|
+
private readonly primary;
|
|
117
|
+
private readonly secondary;
|
|
118
|
+
constructor(options?: ReferenceFacadeOptions);
|
|
119
|
+
/**
|
|
120
|
+
* Node-reference oracle — the spec oracle for primitive layers where Node
|
|
121
|
+
* IS the reference (crypto, dns, zlib, wire format). NOT used for
|
|
122
|
+
* browser-fingerprint comparison; that is what the providers are for.
|
|
123
|
+
*/
|
|
124
|
+
get nodeOracle(): typeof nodeOracle;
|
|
125
|
+
availableProfiles(): ProfileId[];
|
|
126
|
+
capture(profile: ProfileId, url: string): Promise<GoldenCapture>;
|
|
127
|
+
fingerprint(profile: ProfileId): Promise<Fingerprint>;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Node-reference oracle re-export — the spec oracle for primitive layers.
|
|
131
|
+
*
|
|
132
|
+
* Imported lazily so the facade type references resolve without a cycle.
|
|
133
|
+
*/
|
|
134
|
+
import * as nodeOracle from "./node-reference.js";
|
|
135
|
+
/** Construct the {@link ReferenceProvider} matching {@link ReferenceProviderKind}. */
|
|
136
|
+
export declare function createReferenceProvider(kind: ReferenceProviderKind, options?: CurlImpersonateOptions | RealBrowserOptions): ReferenceProvider;
|
|
137
|
+
/**
|
|
138
|
+
* Construct the full {@link ReferenceProviderFacade} wrapping both providers
|
|
139
|
+
* plus the node-reference oracle. This is the recommended entry point for
|
|
140
|
+
* tests that want primary→secondary fallback.
|
|
141
|
+
*/
|
|
142
|
+
export declare function createReferenceFacade(options?: ReferenceFacadeOptions): ReferenceProviderFacade;
|
|
143
|
+
//# sourceMappingURL=reference.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reference.d.ts","sourceRoot":"","sources":["../../src/reference/reference.ts"],"names":[],"mappings":"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"}
|