@browsercore/testing 0.1.3 → 0.2.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 (174) hide show
  1. package/README.md +5 -0
  2. package/captures/DIVERGENCE_REPORT.md +325 -0
  3. package/captures/IMPERSIONATION_GAPS_REPORT.md +367 -0
  4. package/captures/README.md +56 -0
  5. package/captures/REVIEW_FINDINGS.md +71 -0
  6. package/captures/_probe/analyze.py +67 -0
  7. package/captures/_probe/capture_bytes.py +127 -0
  8. package/captures/_probe/chrome131_determinism.py +32 -0
  9. package/captures/_probe/diagnose_capture.py +59 -0
  10. package/captures/_probe/golden_analysis.py +91 -0
  11. package/captures/_probe/inspect_golden.py +55 -0
  12. package/captures/_probe/inspect_sv.py +16 -0
  13. package/captures/_probe/make_golden.py +170 -0
  14. package/captures/_probe/output/chrome100.json +219 -0
  15. package/captures/_probe/output/chrome101.json +219 -0
  16. package/captures/_probe/output/chrome104.json +219 -0
  17. package/captures/_probe/output/chrome107.json +220 -0
  18. package/captures/_probe/output/chrome110.json +220 -0
  19. package/captures/_probe/output/chrome116.json +220 -0
  20. package/captures/_probe/output/chrome119.json +219 -0
  21. package/captures/_probe/output/chrome120.json +219 -0
  22. package/captures/_probe/output/chrome123.json +223 -0
  23. package/captures/_probe/output/chrome124.json +224 -0
  24. package/captures/_probe/output/chrome131.json +224 -0
  25. package/captures/_probe/output/chrome131_fresh.json +224 -0
  26. package/captures/_probe/output/chrome133a.json +224 -0
  27. package/captures/_probe/output/chrome136.json +224 -0
  28. package/captures/_probe/output/chrome99.json +219 -0
  29. package/captures/_probe/output/edge101.json +219 -0
  30. package/captures/_probe/output/edge99.json +219 -0
  31. package/captures/_probe/output/firefox133.json +227 -0
  32. package/captures/_probe/output/firefox135.json +230 -0
  33. package/captures/_probe/output/safari155.json +210 -0
  34. package/captures/_probe/output/safari170.json +214 -0
  35. package/captures/_probe/output/safari170_fresh.json +214 -0
  36. package/captures/_probe/output/safari180.json +216 -0
  37. package/captures/_probe/output/safari180_ios.json +216 -0
  38. package/captures/_probe/output/safari184.json +215 -0
  39. package/captures/_probe/output/safari260.json +211 -0
  40. package/captures/_probe/output/summary.json +2520 -0
  41. package/captures/_probe/parse_existing_settings.py +15 -0
  42. package/captures/_probe/peetprint.py +39 -0
  43. package/captures/_probe/probe.py +137 -0
  44. package/captures/_probe/raw_bytes/chrome-131_client_hello.bin +0 -0
  45. package/captures/_probe/raw_bytes/chrome-131_client_hello.hex +32 -0
  46. package/captures/_probe/raw_bytes/firefox-133_client_hello.bin +0 -0
  47. package/captures/_probe/raw_bytes/firefox-133_client_hello.hex +32 -0
  48. package/captures/_probe/raw_bytes/safari-17_client_hello.bin +0 -0
  49. package/captures/_probe/raw_bytes/safari-17_client_hello.hex +32 -0
  50. package/captures/_probe/relay_capture.py +137 -0
  51. package/captures/_probe/socks_capture.py +138 -0
  52. package/captures/_probe/validate_fresh.py +66 -0
  53. package/captures/_probe/validate_golden.py +97 -0
  54. package/captures/_probe/verify_capture.py +68 -0
  55. package/captures/chrome-131/http2/settings.bin +0 -0
  56. package/captures/chrome-131/http2/settings.meta.json +9 -0
  57. package/captures/chrome-131/oracle_capture.reference.json +224 -0
  58. package/captures/chrome-131/tls/client_hello.bin +0 -0
  59. package/captures/chrome-131/tls/client_hello.meta.json +20 -0
  60. package/captures/chrome-140/http2/settings.bin +0 -0
  61. package/captures/chrome-140/http2/settings.meta.json +9 -0
  62. package/captures/chrome-140/tls/client_hello.bin +0 -0
  63. package/captures/chrome-140/tls/client_hello.meta.json +12 -0
  64. package/captures/firefox-128/tls/client_hello.bin +0 -0
  65. package/captures/firefox-128/tls/client_hello.meta.json +12 -0
  66. package/captures/firefox-133/http2/settings.bin +0 -0
  67. package/captures/firefox-133/http2/settings.meta.json +9 -0
  68. package/captures/firefox-133/oracle_capture.reference.json +227 -0
  69. package/captures/firefox-133/tls/client_hello.bin +0 -0
  70. package/captures/firefox-133/tls/client_hello.meta.json +20 -0
  71. package/captures/safari-17/http2/settings.bin +0 -0
  72. package/captures/safari-17/http2/settings.meta.json +9 -0
  73. package/captures/safari-17/oracle_capture.reference.json +214 -0
  74. package/captures/safari-17/tls/client_hello.bin +0 -0
  75. package/captures/safari-17/tls/client_hello.meta.json +20 -0
  76. package/dist/bench/bench.d.ts +22 -6
  77. package/dist/bench/bench.d.ts.map +1 -1
  78. package/dist/bench/bench.js +105 -10
  79. package/dist/bench/bench.js.map +1 -1
  80. package/dist/captures/manifest.d.ts.map +1 -1
  81. package/dist/captures/manifest.js +2 -3
  82. package/dist/captures/manifest.js.map +1 -1
  83. package/dist/categories/cookies.d.ts +3 -2
  84. package/dist/categories/cookies.d.ts.map +1 -1
  85. package/dist/categories/cookies.js +228 -11
  86. package/dist/categories/cookies.js.map +1 -1
  87. package/dist/categories/redirects.d.ts +8 -2
  88. package/dist/categories/redirects.d.ts.map +1 -1
  89. package/dist/categories/redirects.js +97 -12
  90. package/dist/categories/redirects.js.map +1 -1
  91. package/dist/e2e/cert-gen.d.ts +30 -0
  92. package/dist/e2e/cert-gen.d.ts.map +1 -0
  93. package/dist/e2e/cert-gen.js +158 -0
  94. package/dist/e2e/cert-gen.js.map +1 -0
  95. package/dist/e2e/parse-clienthello.d.ts +177 -0
  96. package/dist/e2e/parse-clienthello.d.ts.map +1 -0
  97. package/dist/e2e/parse-clienthello.js +343 -0
  98. package/dist/e2e/parse-clienthello.js.map +1 -0
  99. package/dist/e2e/sink-server.d.ts +109 -0
  100. package/dist/e2e/sink-server.d.ts.map +1 -0
  101. package/dist/e2e/sink-server.js +292 -0
  102. package/dist/e2e/sink-server.js.map +1 -0
  103. package/dist/errors.d.ts.map +1 -1
  104. package/dist/errors.js.map +1 -1
  105. package/dist/fingerprint/index.d.ts +5 -2
  106. package/dist/fingerprint/index.d.ts.map +1 -1
  107. package/dist/fingerprint/index.js +3 -2
  108. package/dist/fingerprint/index.js.map +1 -1
  109. package/dist/fingerprint/ja3.d.ts.map +1 -1
  110. package/dist/fingerprint/ja3.js +33 -4
  111. package/dist/fingerprint/ja3.js.map +1 -1
  112. package/dist/fingerprint/ja4-errors.d.ts +17 -0
  113. package/dist/fingerprint/ja4-errors.d.ts.map +1 -0
  114. package/dist/fingerprint/ja4-errors.js +20 -0
  115. package/dist/fingerprint/ja4-errors.js.map +1 -0
  116. package/dist/fingerprint/ja4-reader.d.ts +44 -0
  117. package/dist/fingerprint/ja4-reader.d.ts.map +1 -0
  118. package/dist/fingerprint/ja4-reader.js +112 -0
  119. package/dist/fingerprint/ja4-reader.js.map +1 -0
  120. package/dist/fingerprint/ja4.d.ts +11 -43
  121. package/dist/fingerprint/ja4.d.ts.map +1 -1
  122. package/dist/fingerprint/ja4.js +54 -141
  123. package/dist/fingerprint/ja4.js.map +1 -1
  124. package/dist/fingerprint/ja4h.d.ts +46 -0
  125. package/dist/fingerprint/ja4h.d.ts.map +1 -0
  126. package/dist/fingerprint/ja4h.js +50 -0
  127. package/dist/fingerprint/ja4h.js.map +1 -0
  128. package/dist/golden/golden.d.ts +15 -1
  129. package/dist/golden/golden.d.ts.map +1 -1
  130. package/dist/golden/golden.js +35 -22
  131. package/dist/golden/golden.js.map +1 -1
  132. package/dist/index.d.ts +17 -1
  133. package/dist/index.d.ts.map +1 -1
  134. package/dist/index.js +26 -1
  135. package/dist/index.js.map +1 -1
  136. package/dist/reference/browser-provider.d.ts +33 -0
  137. package/dist/reference/browser-provider.d.ts.map +1 -0
  138. package/dist/reference/browser-provider.js +60 -0
  139. package/dist/reference/browser-provider.js.map +1 -0
  140. package/dist/reference/curl-provider.d.ts +40 -0
  141. package/dist/reference/curl-provider.d.ts.map +1 -0
  142. package/dist/reference/curl-provider.js +96 -0
  143. package/dist/reference/curl-provider.js.map +1 -0
  144. package/dist/reference/dump.d.ts +42 -0
  145. package/dist/reference/dump.d.ts.map +1 -0
  146. package/dist/reference/dump.js +102 -0
  147. package/dist/reference/dump.js.map +1 -0
  148. package/dist/reference/node-reference.d.ts.map +1 -1
  149. package/dist/reference/node-reference.js +2 -5
  150. package/dist/reference/node-reference.js.map +1 -1
  151. package/dist/reference/reference-errors.d.ts +36 -0
  152. package/dist/reference/reference-errors.d.ts.map +1 -0
  153. package/dist/reference/reference-errors.js +43 -0
  154. package/dist/reference/reference-errors.js.map +1 -0
  155. package/dist/reference/reference-types.d.ts +33 -0
  156. package/dist/reference/reference-types.d.ts.map +1 -0
  157. package/dist/reference/reference-types.js +9 -0
  158. package/dist/reference/reference-types.js.map +1 -0
  159. package/dist/reference/reference.d.ts +14 -77
  160. package/dist/reference/reference.d.ts.map +1 -1
  161. package/dist/reference/reference.js +43 -207
  162. package/dist/reference/reference.js.map +1 -1
  163. package/dist/rfc/rfcTests.d.ts +46 -8
  164. package/dist/rfc/rfcTests.d.ts.map +1 -1
  165. package/dist/rfc/rfcTests.js +203 -16
  166. package/dist/rfc/rfcTests.js.map +1 -1
  167. package/dist/types.d.ts +2 -1
  168. package/dist/types.d.ts.map +1 -1
  169. package/dist/types.js.map +1 -1
  170. package/dist/utils.d.ts +3 -0
  171. package/dist/utils.d.ts.map +1 -1
  172. package/dist/utils.js +11 -5
  173. package/dist/utils.js.map +1 -1
  174. package/package.json +61 -55
@@ -0,0 +1,292 @@
1
+ /**
2
+ * E2E TLS sink server for live handshake verification.
3
+ *
4
+ * Performs a **real** TLS 1.3 handshake against the `@browsercore/tls` client
5
+ * stack and returns the raw ClientHello bytes it saw on the wire — captured
6
+ * *before* Node's `tls` module parses them. This is the missing test oracle:
7
+ * every other TLS test in the stack runs against an in-process fixture; the
8
+ * sink exercises the full stack end-to-end over a real TCP socket.
9
+ *
10
+ * ## Two-socket tap
11
+ *
12
+ * Node's `tls` module parses the ClientHello before user code can see it, so
13
+ * the sink uses a two-socket tap (plan §2.3, Approach A):
14
+ *
15
+ * 1. A raw `node:net` server accepts the connection and pauses the socket.
16
+ * 2. It buffers the first bytes (the ClientHello TLS record) by reading until
17
+ * the record length is satisfied.
18
+ * 3. It unshifts the buffer back onto the socket and wraps the socket in a
19
+ * `tls.TLSSocket` (server-side) with a freshly generated self-signed
20
+ * ECDSA P-256 cert.
21
+ * 4. The handshake completes; the captured bytes + parsed inspection are
22
+ * returned via {@link TlsSinkServer.waitForHandshake}.
23
+ *
24
+ * The sink is single-shot by design: {@link TlsSinkServer.waitForHandshake}
25
+ * resolves on the first completed handshake. Tests create a fresh sink per
26
+ * case (ephemeral port, `maxConnections: 1`).
27
+ */
28
+ import { createServer } from "node:net";
29
+ import { createSecureContext, TLSSocket } from "node:tls";
30
+ import { generateSelfSignedCert } from "./cert-gen.js";
31
+ /**
32
+ * Lightweight TLS 1.3 sink server.
33
+ *
34
+ * Not a general-purpose TLS server — it exists only to confirm a real
35
+ * handshake completes and to return the wire bytes it saw. All assertions
36
+ * live in the *test*; the sink just reports.
37
+ */
38
+ export class TlsSinkServer {
39
+ server;
40
+ ctx;
41
+ state = { state: "idle" };
42
+ waiters = [];
43
+ failers = [];
44
+ /** Host the sink listens on (always 127.0.0.1). */
45
+ host;
46
+ /** Ephemeral port the sink listens on. */
47
+ port;
48
+ /** ALPN protocols the sink negotiates (empty = no ALPN negotiation). */
49
+ alpnProtocols;
50
+ constructor(server, ctx, host, port, alpnProtocols) {
51
+ this.server = server;
52
+ this.ctx = ctx;
53
+ this.host = host;
54
+ this.port = port;
55
+ this.alpnProtocols = alpnProtocols;
56
+ }
57
+ /**
58
+ * Start listening on an ephemeral port on 127.0.0.1.
59
+ *
60
+ * The returned sink is ready to accept one connection. The self-signed
61
+ * cert is generated at call time (throwaway — valid for 1 day).
62
+ *
63
+ * @param opts Optional configuration — ALPN protocols the sink should
64
+ * negotiate (chooses the first client-offered protocol that matches).
65
+ */
66
+ static start(opts = {}) {
67
+ const { certDer, keyPem } = generateSelfSignedCert(["localhost"], ["127.0.0.1"], 1);
68
+ const ctx = createSecureContext({
69
+ key: keyPem,
70
+ cert: derToPem(certDer, "CERTIFICATE"),
71
+ });
72
+ return new Promise((resolve, reject) => {
73
+ const server = createServer({ allowHalfOpen: false });
74
+ server.maxConnections = 1;
75
+ server.on("error", (err) => {
76
+ // If we haven't finished constructing the sink, reject start.
77
+ reject(err);
78
+ });
79
+ server.listen(0, "127.0.0.1", () => {
80
+ const addr = server.address();
81
+ if (addr === null || typeof addr === "string") {
82
+ reject(new Error("sink server did not bind to an ephemeral port"));
83
+ return;
84
+ }
85
+ const sink = new TlsSinkServer(server, ctx, addr.address, addr.port, opts.alpnProtocols ?? []);
86
+ sink.attach();
87
+ resolve(sink);
88
+ });
89
+ });
90
+ }
91
+ /** Wire up the server's connection handler. Must be called once. */
92
+ attach() {
93
+ this.server.on("connection", (socket) => this.onConnection(socket));
94
+ }
95
+ /**
96
+ * Handle an incoming TCP connection: buffer the ClientHello, then hand the
97
+ * socket to a server-side TLSSocket for a real handshake.
98
+ */
99
+ onConnection(rawSocket) {
100
+ // Guard: only one handshake per sink.
101
+ if (this.state.state !== "idle") {
102
+ rawSocket.destroy();
103
+ return;
104
+ }
105
+ const startedAt = Date.now();
106
+ this.state = { state: "capturing", startedAt };
107
+ const chunks = [];
108
+ let totalBuffered = 0;
109
+ // Pause the socket so data accumulates in Node's internal buffer and we
110
+ // can read it deterministically before the TLSSocket consumes it.
111
+ rawSocket.pause();
112
+ /**
113
+ * Read chunks until we have at least one complete TLS record. A TLS
114
+ * record is 5 header bytes + `length` bytes of fragment. The
115
+ * ClientHello typically arrives in a single segment, but we loop to be
116
+ * robust to fragmentation.
117
+ */
118
+ const tryCapture = () => {
119
+ if (totalBuffered < 5)
120
+ return null;
121
+ const recordLen = 5 + ((chunks[0][3] << 8) | chunks[0][4]);
122
+ if (totalBuffered < recordLen)
123
+ return null;
124
+ // Concatenate the buffered chunks and slice to the record boundary.
125
+ const flat = flattenChunks(chunks, totalBuffered);
126
+ return flat.subarray(0, recordLen);
127
+ };
128
+ const onData = (chunk) => {
129
+ chunks.push(chunk);
130
+ totalBuffered += chunk.length;
131
+ const capture = tryCapture();
132
+ if (capture !== null) {
133
+ rawSocket.removeListener("data", onData);
134
+ this.onClientHello(rawSocket, capture, startedAt);
135
+ }
136
+ };
137
+ rawSocket.on("data", onData);
138
+ rawSocket.on("error", (err) => this.fail(err));
139
+ rawSocket.on("close", () => {
140
+ // If we closed before completing the handshake, fail any waiter.
141
+ if (this.state.state !== "done") {
142
+ this.fail(new Error("socket closed before handshake completed"));
143
+ }
144
+ });
145
+ // Resume flow so the data handler fires.
146
+ rawSocket.resume();
147
+ }
148
+ /**
149
+ * We have the raw ClientHello. Unshift it back onto the socket and wrap
150
+ * the socket in a server-side TLSSocket to complete the handshake.
151
+ */
152
+ onClientHello(rawSocket, capture, startedAt) {
153
+ // Wrap the raw socket in a TLSSocket BEFORE unshifting. The TLSSocket
154
+ // attaches its own `data` listener on construction; only after that
155
+ // listener is in place does unshift deliver the captured bytes straight
156
+ // into the TLS layer. Unshifting first leaves the bytes buffered with
157
+ // no listener to consume them, so the handshake never starts.
158
+ const tlsSocket = new TLSSocket(rawSocket, {
159
+ isServer: true,
160
+ secureContext: this.ctx,
161
+ // Request but do not require a client cert — keeps the sink simple.
162
+ requestCert: false,
163
+ ALPNProtocols: this.alpnProtocols.length > 0 ? this.alpnProtocols : undefined,
164
+ });
165
+ // Now push the captured bytes back; the TLSSocket reads them as its
166
+ // first input and begins the handshake.
167
+ rawSocket.unshift(capture);
168
+ const clientHello = {
169
+ raw: capture,
170
+ receivedAt: Date.now(),
171
+ };
172
+ this.state = { state: "handshaking", capture: clientHello, startedAt };
173
+ tlsSocket.on("secure", () => {
174
+ // The secure event fires once the handshake is verified.
175
+ const result = {
176
+ clientHello,
177
+ protocolVersion: tlsSocket.getProtocol() ?? "unknown",
178
+ cipherSuite: tlsSocket.getCipher().name,
179
+ alpnProtocol: narrowServerName(tlsSocket.alpnProtocol),
180
+ serverName: narrowServerName(tlsSocket.servername),
181
+ handshakeDurationMs: Date.now() - startedAt,
182
+ };
183
+ this.done(result);
184
+ // Close the connection after reporting — single-shot sink.
185
+ tlsSocket.end();
186
+ });
187
+ tlsSocket.on("error", (err) => {
188
+ // Distinguish handshake rejection from transport errors.
189
+ this.fail(new Error(`TLS handshake failed: ${err.message}`, { cause: err }));
190
+ });
191
+ }
192
+ /** Transition to the done state and resolve any waiters. */
193
+ done(result) {
194
+ if (this.state.state === "done")
195
+ return;
196
+ this.state = { state: "done", result };
197
+ const w = this.waiters;
198
+ this.waiters = [];
199
+ this.failers = [];
200
+ for (const resolve of w)
201
+ resolve(result);
202
+ }
203
+ /** Transition to the failed state and reject any waiters. */
204
+ fail(error) {
205
+ if (this.state.state === "done")
206
+ return;
207
+ this.state = { state: "failed", error };
208
+ const f = this.failers;
209
+ this.failers = [];
210
+ this.waiters = [];
211
+ for (const reject of f)
212
+ reject(error);
213
+ }
214
+ /**
215
+ * Resolve once a ClientHello has been captured and the handshake completed.
216
+ *
217
+ * @param timeoutMs Max wait in milliseconds (default 5000). The sink is
218
+ * single-shot — only the first handshake resolves this promise.
219
+ * @throws {Error} on timeout or handshake failure.
220
+ */
221
+ waitForHandshake(timeoutMs = 5000) {
222
+ if (this.state.state === "done") {
223
+ return Promise.resolve(this.state.result);
224
+ }
225
+ if (this.state.state === "failed") {
226
+ return Promise.reject(this.state.error);
227
+ }
228
+ return new Promise((resolve, reject) => {
229
+ let timer;
230
+ this.waiters.push((result) => {
231
+ if (timer !== undefined)
232
+ clearTimeout(timer);
233
+ resolve(result);
234
+ });
235
+ this.failers.push((err) => {
236
+ if (timer !== undefined)
237
+ clearTimeout(timer);
238
+ reject(err);
239
+ });
240
+ timer = setTimeout(() => {
241
+ this.fail(new Error(`waitForHandshake timed out after ${timeoutMs}ms`));
242
+ }, timeoutMs);
243
+ });
244
+ }
245
+ /** Stop the server. Resolves once the underlying socket is closed. */
246
+ stop() {
247
+ // Reject any pending waiters so callers don't hang.
248
+ this.fail(new Error("sink server stopped"));
249
+ return new Promise((resolve, reject) => {
250
+ this.server.close((err) => {
251
+ if (err !== undefined)
252
+ reject(err);
253
+ else
254
+ resolve();
255
+ });
256
+ });
257
+ }
258
+ }
259
+ // --- Byte helpers -------------------------------------------------------
260
+ /** Concatenate an array of chunks into a single buffer of `total` bytes. */
261
+ function flattenChunks(chunks, total) {
262
+ const out = new Uint8Array(total);
263
+ let o = 0;
264
+ for (const c of chunks) {
265
+ out.set(c, o);
266
+ o += c.length;
267
+ }
268
+ return out;
269
+ }
270
+ /**
271
+ * Coerce Node's `TLSSocket.servername` (`string | false | null`) to
272
+ * `string | null`. `false` means SNI was requested but not matched; `null`
273
+ * means no SNI was sent. Both map to "no usable servername" here.
274
+ */
275
+ function narrowServerName(value) {
276
+ return typeof value === "string" ? value : null;
277
+ }
278
+ /**
279
+ * Convert a DER-encoded object to PEM (base64 with 64-char lines + headers).
280
+ * Used to feed the self-signed cert into `tls.createSecureContext`, which
281
+ * expects PEM for the `cert` option.
282
+ */
283
+ function derToPem(der, label) {
284
+ const b64 = Buffer.from(der).toString("base64");
285
+ const lines = [`-----BEGIN ${label}-----`];
286
+ for (let i = 0; i < b64.length; i += 64) {
287
+ lines.push(b64.slice(i, i + 64));
288
+ }
289
+ lines.push(`-----END ${label}-----`);
290
+ return lines.join("\n");
291
+ }
292
+ //# sourceMappingURL=sink-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sink-server.js","sourceRoot":"","sources":["../../src/e2e/sink-server.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,0CAA0C;AAC1C,qBAAa,YAAa,SAAQ,KAAK;IACnC,SAAgB,IAAI,EAAG,cAAc,CAAU;IAC/C,SAAyB,KAAK,EAAE,KAAK,GAAG,SAAS,CAAC;gBAEtC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE;CAK3D;AAED,mDAAmD;AACnD,qBAAa,mBAAoB,SAAQ,KAAK;IAC1C,SAAgB,IAAI,EAAG,qBAAqB,CAAU;IACtD,SAAgB,SAAS,EAAE,MAAM,CAAC;IAClC,SAAgB,mBAAmB,EAAE,MAAM,CAAC;IAC5C,SAAyB,KAAK,EAAE,KAAK,GAAG,SAAS,CAAC;gBAG9C,SAAS,EAAE,MAAM,EACjB,mBAAmB,EAAE,MAAM,EAC3B,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE;CAWlC"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,0CAA0C;AAC1C,qBAAa,YAAa,SAAQ,KAAK;IACnC,SAAgB,IAAI,EAAG,cAAc,CAAU;IAC/C,SAAyB,KAAK,EAAE,KAAK,GAAG,SAAS,CAAC;IAElD,YAAY,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE,EAIvD;CACJ;AAED,mDAAmD;AACnD,qBAAa,mBAAoB,SAAQ,KAAK;IAC1C,SAAgB,IAAI,EAAG,qBAAqB,CAAU;IACtD,SAAgB,SAAS,EAAE,MAAM,CAAC;IAClC,SAAgB,mBAAmB,EAAE,MAAM,CAAC;IAC5C,SAAyB,KAAK,EAAE,KAAK,GAAG,SAAS,CAAC;IAElD,YACI,SAAS,EAAE,MAAM,EACjB,mBAAmB,EAAE,MAAM,EAC3B,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE,EAU9B;CACJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,0CAA0C;AAC1C,MAAM,OAAO,YAAa,SAAQ,KAAK;IACnB,IAAI,GAAG,cAAuB,CAAC;IACtB,KAAK,CAAoB;IAElD,YAAY,OAAe,EAAE,OAA2B;QACpD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;QAC5B,IAAI,CAAC,KAAK,GAAG,OAAO,EAAE,KAAK,CAAC;IAChC,CAAC;CACJ;AAED,mDAAmD;AACnD,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAC1B,IAAI,GAAG,qBAA8B,CAAC;IACtC,SAAS,CAAS;IAClB,mBAAmB,CAAS;IACnB,KAAK,CAAoB;IAElD,YACI,SAAiB,EACjB,mBAA2B,EAC3B,OAA2B;QAE3B,KAAK,CACD,kBAAkB,SAAS,qBAAqB,mBAAmB,EAAE,EACrE,OAAO,CACV,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;QAClC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,mBAAmB,GAAG,mBAAmB,CAAC;QAC/C,IAAI,CAAC,KAAK,GAAG,OAAO,EAAE,KAAK,CAAC;IAChC,CAAC;CACJ"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,0CAA0C;AAC1C,MAAM,OAAO,YAAa,SAAQ,KAAK;IACnB,IAAI,GAAG,cAAuB,CAAC;IACtB,KAAK,CAAoB;IAElD,YAAY,OAAe,EAAE,OAA2B;QACpD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC;QAC5B,IAAI,CAAC,KAAK,GAAG,OAAO,EAAE,KAAK,CAAC;IAChC,CAAC;CACJ;AAED,mDAAmD;AACnD,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAC1B,IAAI,GAAG,qBAA8B,CAAC;IACtC,SAAS,CAAS;IAClB,mBAAmB,CAAS;IACnB,KAAK,CAAoB;IAElD,YACI,SAAiB,EACjB,mBAA2B,EAC3B,OAA2B;QAE3B,KAAK,CACD,kBAAkB,SAAS,qBAAqB,mBAAmB,EAAE,EACrE,OAAO,CACV,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;QAClC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,mBAAmB,GAAG,mBAAmB,CAAC;QAC/C,IAAI,CAAC,KAAK,GAAG,OAAO,EAAE,KAAK,CAAC;IAChC,CAAC;CACJ"}
@@ -1,5 +1,8 @@
1
- /** Barrel for fingerprint computation (JA3 / JA4). */
1
+ /** Barrel for fingerprint computation (JA3 / JA4 / JA4H). */
2
2
  export { computeJa3, parseClientHello, Ja3ParseError } from "./ja3.js";
3
3
  export type { Ja3Segments } from "./ja3.js";
4
- export { computeJa4 } from "./ja4.js";
4
+ export { computeJa4, computeJa4Fingerprint, Ja4ParseError } from "./ja4.js";
5
+ export type { Ja4ClientHello, Ja4Fingerprint } from "./ja4.js";
6
+ export { computeJa4h } from "./ja4h.js";
7
+ export type { Ja4hFingerprint, Ja4hRequest } from "./ja4h.js";
5
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fingerprint/index.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACvE,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fingerprint/index.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACvE,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC5E,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC"}
@@ -1,4 +1,5 @@
1
- /** Barrel for fingerprint computation (JA3 / JA4). */
1
+ /** Barrel for fingerprint computation (JA3 / JA4 / JA4H). */
2
2
  export { computeJa3, parseClientHello, Ja3ParseError } from "./ja3.js";
3
- export { computeJa4 } from "./ja4.js";
3
+ export { computeJa4, computeJa4Fingerprint, Ja4ParseError } from "./ja4.js";
4
+ export { computeJa4h } from "./ja4h.js";
4
5
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/fingerprint/index.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEvE,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/fingerprint/index.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEvE,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE5E,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ja3.d.ts","sourceRoot":"","sources":["../../src/fingerprint/ja3.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,+DAA+D;AAC/D,qBAAa,aAAc,SAAQ,KAAK;IACpC,SAAgB,IAAI,EAAG,eAAe,CAAU;gBACpC,OAAO,EAAE,MAAM;CAI9B;AAOD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,WAAW,EAAE,UAAU,GAAG,MAAM,CAM1D;AAED,mDAAmD;AACnD,MAAM,WAAW,WAAW;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACnC;AAED,sDAAsD;AACtD,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,UAAU,GAAG,WAAW,CAgGrE"}
1
+ {"version":3,"file":"ja3.d.ts","sourceRoot":"","sources":["../../src/fingerprint/ja3.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,+DAA+D;AAC/D,qBAAa,aAAc,SAAQ,KAAK;IACpC,SAAgB,IAAI,EAAG,eAAe,CAAU;IAChD,YAAY,OAAO,EAAE,MAAM,EAG1B;CACJ;AAYD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,WAAW,EAAE,UAAU,GAAG,MAAM,CAM1D;AAED,mDAAmD;AACnD,MAAM,WAAW,WAAW;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACnC;AAED,sDAAsD;AACtD,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,UAAU,GAAG,WAAW,CA6GrE"}
@@ -21,7 +21,12 @@ export class Ja3ParseError extends Error {
21
21
  }
22
22
  /** Read a big-endian uint16 at `offset` in `buf`. */
23
23
  function uint16(buf, offset) {
24
- return (buf[offset] << 8) | buf[offset + 1];
24
+ const hi = buf[offset];
25
+ const lo = buf[offset + 1];
26
+ if (hi === undefined || lo === undefined) {
27
+ throw new Ja3ParseError(`uint16 read out of bounds at offset ${offset}`);
28
+ }
29
+ return (hi << 8) | lo;
25
30
  }
26
31
  /**
27
32
  * Parse a TLS ClientHello from a TLS record (or handshake) buffer and return
@@ -75,6 +80,9 @@ export function parseClientHello(clientHello) {
75
80
  pos += 32;
76
81
  // session_id(variable)
77
82
  const sessionIdLen = clientHello[pos];
83
+ if (sessionIdLen === undefined) {
84
+ throw new Ja3ParseError("ClientHello truncated at session id length");
85
+ }
78
86
  pos += 1 + sessionIdLen;
79
87
  // cipher_suites(variable)
80
88
  const cipherSuitesLen = uint16(clientHello, pos);
@@ -83,6 +91,9 @@ export function parseClientHello(clientHello) {
83
91
  pos += cipherSuitesLen;
84
92
  // compression_methods(variable)
85
93
  const compLen = clientHello[pos];
94
+ if (compLen === undefined) {
95
+ throw new Ja3ParseError("ClientHello truncated at compression methods length");
96
+ }
86
97
  pos += 1 + compLen;
87
98
  // extensions(variable)
88
99
  if (pos + 2 > end) {
@@ -114,8 +125,15 @@ export function parseClientHello(clientHello) {
114
125
  else if (extType === 0x000b && extLen >= 1) {
115
126
  // ec_point_formats(11): list of uint8 formats
116
127
  const listLen = clientHello[pos];
128
+ if (listLen === undefined) {
129
+ throw new Ja3ParseError("ClientHello truncated at ec_point_formats length");
130
+ }
117
131
  for (let i = 0; i < listLen; i++) {
118
- ecPointFormats.push(clientHello[pos + 1 + i]);
132
+ const fmt = clientHello[pos + 1 + i];
133
+ if (fmt === undefined) {
134
+ throw new Ja3ParseError("ClientHello truncated in ec_point_formats list");
135
+ }
136
+ ecPointFormats.push(fmt);
119
137
  }
120
138
  }
121
139
  pos += extLen;
@@ -129,12 +147,23 @@ export function parseClientHello(clientHello) {
129
147
  };
130
148
  }
131
149
  function readInt24(buf, offset) {
132
- return (buf[offset] << 16) | (buf[offset + 1] << 8) | buf[offset + 2];
150
+ const hi = buf[offset];
151
+ const mid = buf[offset + 1];
152
+ const lo = buf[offset + 2];
153
+ if (hi === undefined || mid === undefined || lo === undefined) {
154
+ throw new Ja3ParseError(`readInt24 out of bounds at offset ${offset}`);
155
+ }
156
+ return (hi << 16) | (mid << 8) | lo;
133
157
  }
134
158
  function readUint16List(buf, offset, byteLen) {
135
159
  const out = [];
136
160
  for (let i = 0; i + 1 < byteLen; i += 2) {
137
- out.push((buf[offset + i] << 8) | buf[offset + i + 1]);
161
+ const hi = buf[offset + i];
162
+ const lo = buf[offset + i + 1];
163
+ if (hi === undefined || lo === undefined) {
164
+ throw new Ja3ParseError(`readUint16List out of bounds at offset ${offset + i}`);
165
+ }
166
+ out.push((hi << 8) | lo);
138
167
  }
139
168
  return out;
140
169
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ja3.js","sourceRoot":"","sources":["../../src/fingerprint/ja3.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ja3.js","sourceRoot":"","sources":["../../src/fingerprint/ja3.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Typed errors for JA4 / JA4H fingerprint parsing.
3
+ *
4
+ * Mirrors the transport pattern: a dedicated errors module so callers can
5
+ * match on `kind` instead of parsing message text. Kept separate from
6
+ * `ja4.ts` and `ja4h.ts` so neither fingerprint module owns the error type
7
+ * the other depends on.
8
+ */
9
+ /**
10
+ * Reasons a ClientHello (JA4) or HTTP request (JA4H) cannot be parsed into a
11
+ * fingerprint input.
12
+ */
13
+ export declare class Ja4ParseError extends Error {
14
+ readonly kind: "Ja4ParseError";
15
+ constructor(message: string);
16
+ }
17
+ //# sourceMappingURL=ja4-errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ja4-errors.d.ts","sourceRoot":"","sources":["../../src/fingerprint/ja4-errors.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;GAGG;AACH,qBAAa,aAAc,SAAQ,KAAK;IACpC,SAAgB,IAAI,EAAG,eAAe,CAAU;IAChD,YAAY,OAAO,EAAE,MAAM,EAG1B;CACJ"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Typed errors for JA4 / JA4H fingerprint parsing.
3
+ *
4
+ * Mirrors the transport pattern: a dedicated errors module so callers can
5
+ * match on `kind` instead of parsing message text. Kept separate from
6
+ * `ja4.ts` and `ja4h.ts` so neither fingerprint module owns the error type
7
+ * the other depends on.
8
+ */
9
+ /**
10
+ * Reasons a ClientHello (JA4) or HTTP request (JA4H) cannot be parsed into a
11
+ * fingerprint input.
12
+ */
13
+ export class Ja4ParseError extends Error {
14
+ kind = "Ja4ParseError";
15
+ constructor(message) {
16
+ super(message);
17
+ this.name = "Ja4ParseError";
18
+ }
19
+ }
20
+ //# sourceMappingURL=ja4-errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ja4-errors.js","sourceRoot":"","sources":["../../src/fingerprint/ja4-errors.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;GAGG;AACH,MAAM,OAAO,aAAc,SAAQ,KAAK;IACpB,IAAI,GAAG,eAAwB,CAAC;IAChD,YAAY,OAAe;QACvB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAChC,CAAC;CACJ"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Low-level TLS ClientHello byte readers shared by JA4 computation.
3
+ *
4
+ * Factored out of `ja4.ts` so the fingerprint module stays focused on the
5
+ * JA4 spec (sorting, hashing, formatting) and the readers live alongside the
6
+ * wire-format constants they depend on. All readers are bounds-checked and
7
+ * throw {@link Ja4ParseError} on truncation.
8
+ */
9
+ /** GREASE values per RFC 8701 — these are reserved and must be ignored. */
10
+ export declare const GREASE_VALUES: ReadonlySet<number>;
11
+ /** TLS extension type numbers we special-case when parsing. */
12
+ export declare const EXT_SNI = 0;
13
+ export declare const EXT_SUPPORTED_GROUPS = 10;
14
+ export declare const EXT_EC_POINT_FORMATS = 11;
15
+ export declare const EXT_ALPN = 16;
16
+ export declare const EXT_SUPPORTED_VERSIONS = 43;
17
+ /** TLS protocol-version codes → human-readable version string. */
18
+ export declare const TLS_VERSIONS: ReadonlyMap<number, string>;
19
+ /** Read a big-endian uint16 at `offset` in `buf` (unchecked — caller bounds). */
20
+ export declare function uint16(buf: Uint8Array, offset: number): number;
21
+ /** Read a 24-bit big-endian integer at `offset` in `buf`. */
22
+ export declare function uint24(buf: Uint8Array, offset: number): number;
23
+ /** Format a number as a 4-char lowercase hex string (`0x1a2b` → `"1a2b"`). */
24
+ export declare function hex4(value: number): string;
25
+ /** Human-readable version string for a TLS `client_version` code. */
26
+ export declare function tlsVersionLabel(versionCode: number): string;
27
+ /**
28
+ * Read the non-GREASE supported_groups(10) list that starts at `pos`.
29
+ * `pos` points at the 2-byte list length. Throws {@link Ja4ParseError} on
30
+ * truncation.
31
+ */
32
+ export declare function readSupportedGroups(clientHello: Uint8Array, pos: number): number[];
33
+ /**
34
+ * Read the ec_point_formats(11) list that starts at `pos`. `pos` points at the
35
+ * 1-byte list length. Throws {@link Ja4ParseError} on truncation.
36
+ */
37
+ export declare function readEcPointFormats(clientHello: Uint8Array, pos: number): number[];
38
+ /**
39
+ * Read the ALPN protocol list that starts at `pos`. `pos` points at the 2-byte
40
+ * list length. Returns the comma-joined protocol string. Throws
41
+ * {@link Ja4ParseError} on truncation.
42
+ */
43
+ export declare function readAlpnProtocols(clientHello: Uint8Array, pos: number): string;
44
+ //# sourceMappingURL=ja4-reader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ja4-reader.d.ts","sourceRoot":"","sources":["../../src/fingerprint/ja4-reader.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,2EAA2E;AAC3E,eAAO,MAAM,aAAa,EAAE,WAAW,CAAC,MAAM,CAG5C,CAAC;AAEH,+DAA+D;AAC/D,eAAO,MAAM,OAAO,IAAS,CAAC;AAC9B,eAAO,MAAM,oBAAoB,KAAS,CAAC;AAC3C,eAAO,MAAM,oBAAoB,KAAS,CAAC;AAC3C,eAAO,MAAM,QAAQ,KAAS,CAAC;AAC/B,eAAO,MAAM,sBAAsB,KAAS,CAAC;AAE7C,kEAAkE;AAClE,eAAO,MAAM,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAMnD,CAAC;AAEH,iFAAiF;AACjF,wBAAgB,MAAM,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAO9D;AAED,6DAA6D;AAC7D,wBAAgB,MAAM,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAQ9D;AAED,8EAA8E;AAC9E,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE1C;AAED,qEAAqE;AACrE,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAUlF;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAcjF;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAe9E"}