@browsercore/testing 0.1.2 → 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.
- package/README.md +5 -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/dist/bench/bench.d.ts +22 -6
- package/dist/bench/bench.d.ts.map +1 -1
- package/dist/bench/bench.js +105 -10
- package/dist/bench/bench.js.map +1 -1
- package/dist/captures/manifest.d.ts.map +1 -1
- package/dist/captures/manifest.js +2 -3
- package/dist/captures/manifest.js.map +1 -1
- package/dist/categories/compression.d.ts +5 -2
- package/dist/categories/compression.d.ts.map +1 -1
- package/dist/categories/compression.js +54 -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 +228 -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 +30 -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/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 +35 -22
- package/dist/golden/golden.js.map +1 -1
- package/dist/index.d.ts +17 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -1
- package/dist/index.js.map +1 -1
- 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 +60 -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-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 +61 -50
|
@@ -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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"}
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/errors.js.map
CHANGED
|
@@ -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,
|
|
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,
|
|
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,
|
|
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;
|
|
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"}
|
package/dist/fingerprint/ja3.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,+DAA+D;AAC/D,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;AAED,qDAAqD;AACrD,SAAS,MAAM,CAAC,GAAe,EAAE,MAAc;IAC3C,
|
|
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"}
|