@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,227 @@
1
+ {
2
+ "donate": "Please consider donating to keep this API running. Visit https://tls.peet.ws",
3
+ "ip": "92.218.110.236:59822",
4
+ "http_version": "h2",
5
+ "method": "GET",
6
+ "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:133.0) Gecko/20100101 Firefox/133.0",
7
+ "tls": {
8
+ "ciphers": [
9
+ "TLS_AES_128_GCM_SHA256",
10
+ "TLS_CHACHA20_POLY1305_SHA256",
11
+ "TLS_AES_256_GCM_SHA384",
12
+ "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
13
+ "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
14
+ "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
15
+ "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
16
+ "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
17
+ "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
18
+ "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
19
+ "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
20
+ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
21
+ "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
22
+ "TLS_RSA_WITH_AES_128_GCM_SHA256",
23
+ "TLS_RSA_WITH_AES_256_GCM_SHA384",
24
+ "TLS_RSA_WITH_AES_128_CBC_SHA",
25
+ "TLS_RSA_WITH_AES_256_CBC_SHA"
26
+ ],
27
+ "extensions": [
28
+ {
29
+ "name": "server_name (0)",
30
+ "server_name": "tls.peet.ws"
31
+ },
32
+ {
33
+ "name": "extended_master_secret (23)",
34
+ "master_secret_data": "",
35
+ "extended_master_secret_data": ""
36
+ },
37
+ {
38
+ "name": "extensionRenegotiationInfo (boringssl) (65281)",
39
+ "data": "00"
40
+ },
41
+ {
42
+ "name": "supported_groups (10)",
43
+ "supported_groups": [
44
+ "X25519MLKEM768 (4588)",
45
+ "X25519 (29)",
46
+ "P-256 (23)",
47
+ "P-384 (24)",
48
+ "P-521 (25)",
49
+ "ffdhe2048 (256)",
50
+ "ffdhe3072 (257)"
51
+ ]
52
+ },
53
+ {
54
+ "name": "ec_point_formats (11)",
55
+ "elliptic_curves_point_formats": [
56
+ "0x00"
57
+ ]
58
+ },
59
+ {
60
+ "name": "session_ticket (35)",
61
+ "data": ""
62
+ },
63
+ {
64
+ "name": "application_layer_protocol_negotiation (16)",
65
+ "protocols": [
66
+ "h2",
67
+ "http/1.1"
68
+ ]
69
+ },
70
+ {
71
+ "name": "status_request (5)",
72
+ "status_request": {
73
+ "certificate_status_type": "OSCP (1)",
74
+ "responder_id_list_length": 0,
75
+ "request_extensions_length": 0
76
+ }
77
+ },
78
+ {
79
+ "name": "delegated_credentials (34)",
80
+ "signature_hash_algorithms": [
81
+ "ecdsa_secp256r1_sha256",
82
+ "ecdsa_secp384r1_sha384",
83
+ "ecdsa_secp521r1_sha512",
84
+ "ecdsa_sha1"
85
+ ]
86
+ },
87
+ {
88
+ "name": "key_share (51)",
89
+ "shared_keys": [
90
+ {
91
+ "X25519MLKEM768 (4588)": "0bfbcfdd99b65a5abdfc8324e1302227179ff629af1e21302ea377d6618c6c0b9adc06b6252a062749869cb706dcba8f5ef71bbb208285254a1935496570932cb13900e2252237aa2ed2ca060a2cd10b3cc5aac305ab8c1527c0e9ac217af317e314c404a770d936a9f1426a207acf217710966127f0e55f7344aa7c6a7ed4e128d6dabc567cc7a2ec8a825748244bc39d78717c25ce76c52a88bb392fd85e2fc35f4fbba6d9f5185922584009b70fdbbcfe89814f2063adc99ee4aaaf145b27ca2c7f0d341d40318d729c29db839a8a6a0c788c0f69586243284aafb8be9a816b02d845393987887bb74778168fe5c0b3500c9cd0619f025027e390b4c7b525c79e644a84354bc959768ca39788e355b9a7889a72b00b98b2888de59cf825afdaf6383f8362b74abba1a2b6d3219198d91df583ba48d381fe0004fdd5c6b7a037d64786ee7227d4a47e4c746738332fb6350a5dd9312fc38f309012f5e7870c1c5f53e39b0a609d86333700c510850898ed6c3e34053de620655e07349b515c67b234dc7860bd25b674613e8c755a83da5bc4602681eb7b7a653a54176ba48a6034080598c4602f806b2537b864e60402507d429c84de98009fc692ef5742dbc750a9fb4724994b04f80beed89ceef365b8652affa6c550da700ec11c620495e66435182c0c2adb2f22988193b68072b2c6b088466eba39796659cb19a037f515704a2338c574ca863b81db5696fa0cdd87282f1b9b7cabca1e1b3719c3a51d93beeda563e71c313b5ba3eab60716b4180ff72b5d768e1ef11a30b3b16ce7a0c3e96a9a2acc3d5707ec2a21f34b6866e0a27bc67ec326cb30d46e5a5426d8988126d86281a188e84ac9388cc700114497e213248459fcf09243e86992a08221abcf12aa4d5513495c5c444e1440db8cb0721289b461385197662200833894a999e286285c46fe06293d8bbdcf880612628d5c876d20f389070ac5bea7c451c79a47d7a0d7f89bb3411b3d3283e94a4053677073342d38c7927a760a33a00a8926ae0fbc0503052c935359f7e49189524cdce394f0ba47e315be61296a1a26bdde364008d66a8d84868e245e648b7c93d42972811a31cb47ac24ae060250571cb624b1918719591ee32074754aea34a55cc796a9076c6fa20129a43773b69fa0941845084afb8bb4ed8509c0db03572672f9d60d2102854d2787d6eaa0353378dc7788013139f0cc1e3029b731323a41e24bfc860f5c01a949a024597644a0084453bc7c57b7a6b268adf5c00e2200623b267324c46c1b2caff3445109d278249962b20a34ccf4b9543c346732b3442657b6a3c3326ca6a8ccbf1c758fef83b07fbc36e2c647c82b8f64887dfedc77f61a12dba88a08334ba4536186063b7c4c9f22056f88fb623768b72579cc82627b13f1985cec0120465a142756af71098237ae072bbe816317e1d213fd7408657bc3447b457f996da3e66e5473a15f9a72299a74d93c1acf4559a6211cde8c8b065b1227090dbb68215cea239f427608bb971551247eacbc1ff287c172183c98989c093d5e9c2643c80e5743514accb511e9265fc772d0bb11e29344bce91913c18bb12c807e45cd9d66bb1491762d2a0fbc850c3fcb1e87869615af5e648c623d1862131b8a90212df65b4c672775d0e56903cc3ceb942cdca1f71fa336b65af6899400a7dff5549976ad018a8b694e53"
92
+ },
93
+ {
94
+ "X25519 (29)": "e8e5bf70e4071c2f4987a554eb0241df993c727bf72365744cc308e2ad9ac300"
95
+ },
96
+ {
97
+ "P-256 (23)": "04b34a0cfadee6f73b21e4a16c65c616c7d6cd1ee53a8d5418938acf682ba8b05cfff08ea7d3d9948b3adeda6bac0bf9b0a1ac3a4dde6390b552c8b48b7b1320c3"
98
+ }
99
+ ]
100
+ },
101
+ {
102
+ "name": "supported_versions (43)",
103
+ "versions": [
104
+ "TLS 1.3",
105
+ "TLS 1.2"
106
+ ]
107
+ },
108
+ {
109
+ "name": "signature_algorithms (13)",
110
+ "signature_algorithms": [
111
+ "ecdsa_secp256r1_sha256",
112
+ "ecdsa_secp384r1_sha384",
113
+ "ecdsa_secp521r1_sha512",
114
+ "rsa_pss_rsae_sha256",
115
+ "rsa_pss_rsae_sha384",
116
+ "rsa_pss_rsae_sha512",
117
+ "rsa_pkcs1_sha256",
118
+ "rsa_pkcs1_sha384",
119
+ "rsa_pkcs1_sha512",
120
+ "ecdsa_sha1",
121
+ "rsa_pkcs1_sha1"
122
+ ]
123
+ },
124
+ {
125
+ "name": "psk_key_exchange_modes (45)",
126
+ "PSK_Key_Exchange_Mode": "PSK with (EC)DHE key establishment (psk_dhe_ke) (1)"
127
+ },
128
+ {
129
+ "name": "record_size_limit (28)",
130
+ "data": "0fa1"
131
+ },
132
+ {
133
+ "name": "compress_certificate (27)",
134
+ "algorithms": [
135
+ "zlib (1)",
136
+ "brotli (2)",
137
+ "zstd (3)"
138
+ ]
139
+ },
140
+ {
141
+ "name": "extensionEncryptedClientHello (boringssl) (65037)",
142
+ "data": "0000010001080020bf740cf3d11fdbdce1c03df9af65170e6311134064a91e08b45c86bfdffa394c00d0623f007f6bcea5ac3a1d3bade746d793c0d381889ed49d1232a1d0a75e0746ece6a5f2d7d6050e8d367c4c20dcaa9f0f42d32e8af36e326df3bce1cab73329dfe522be8d54f7ef553de6e172a073ae638e4cb1f027d69c67e2c9cb2491efbf07897e2a81f4118e449d30ad3757550f1c90de121362cf09bcb039ecf3bae41635799eecef72c9cb3d9faf5fd6a4d5da55963b795f1b16257d896c0f1218349ff3b6ac549fcc19a923cdb9726dbfc14b024d0a4eef79224e10c90b840f556f58966a63da59a886e62a985c87e03552a3c3"
143
+ }
144
+ ],
145
+ "tls_version_record": "771",
146
+ "tls_version_negotiated": "772",
147
+ "ja3": "771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-34-51-43-13-45-28-27-65037,4588-29-23-24-25-256-257,0",
148
+ "ja3_hash": "2d692a4485ca2f5f2b10ecb2d2909ad3",
149
+ "ja4": "t13d1716h2_5b57614c22b0_eeeea6562960",
150
+ "ja4_r": "t13d1716h2_002f,0035,009c,009d,1301,1302,1303,c009,c00a,c013,c014,c02b,c02c,c02f,c030,cca8,cca9_0005,000a,000b,000d,0017,001b,001c,0022,0023,002b,002d,0033,fe0d,ff01_0403,0503,0603,0804,0805,0806,0401,0501,0601,0203,0201",
151
+ "peetprint": "772-771|2-1.1|4588-29-23-24-25-256-257|1027-1283-1539-2052-2053-2054-1025-1281-1537-515-513|1|1-2-3|4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-156-157-47-53|0-10-11-13-16-23-27-28-34-35-43-45-5-51-65037-65281",
152
+ "peetprint_hash": "199f9cf4a47bfc51995a9f3942190094",
153
+ "client_random": "3841c1cad281ebd8d99db482413d3747d7558529bc794cde38cef3185df77e95",
154
+ "session_id": "e6d682d2d9f4f548c100a2ed301d99bcc80c5c44613a93740b31f90e461861cd"
155
+ },
156
+ "http2": {
157
+ "akamai_fingerprint": "1:65536;2:0;4:131072;5:16384|12517377|0|m,p,a,s",
158
+ "akamai_fingerprint_hash": "6ea73faa8fc5aac76bded7bd238f6433",
159
+ "sent_frames": [
160
+ {
161
+ "frame_type": "SETTINGS",
162
+ "length": 24,
163
+ "settings": [
164
+ "HEADER_TABLE_SIZE = 65536",
165
+ "ENABLE_PUSH = 0",
166
+ "INITIAL_WINDOW_SIZE = 131072",
167
+ "MAX_FRAME_SIZE = 16384"
168
+ ]
169
+ },
170
+ {
171
+ "frame_type": "WINDOW_UPDATE",
172
+ "length": 4,
173
+ "increment": 12517377
174
+ },
175
+ {
176
+ "frame_type": "HEADERS",
177
+ "stream_id": 1,
178
+ "length": 288,
179
+ "headers": [
180
+ ":method: GET",
181
+ ":path: /api/all",
182
+ ":authority: tls.peet.ws",
183
+ ":scheme: https",
184
+ "user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:133.0) Gecko/20100101 Firefox/133.0",
185
+ "accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
186
+ "accept-language: en-US,en;q=0.5",
187
+ "accept-encoding: gzip, deflate, br, zstd",
188
+ "upgrade-insecure-requests: 1",
189
+ "sec-fetch-dest: document",
190
+ "sec-fetch-mode: navigate",
191
+ "sec-fetch-site: none",
192
+ "sec-fetch-user: ?1",
193
+ "priority: u=0, i",
194
+ "te: trailers"
195
+ ],
196
+ "flags": [
197
+ "EndStream (0x1)",
198
+ "EndHeaders (0x4)",
199
+ "Priority (0x20)"
200
+ ],
201
+ "priority": {
202
+ "weight": 256,
203
+ "depends_on": 0,
204
+ "exclusive": 0
205
+ }
206
+ }
207
+ ]
208
+ },
209
+ "tcpip": {
210
+ "cap_length": 539,
211
+ "dst_port": 443,
212
+ "src_port": 59822,
213
+ "ip": {
214
+ "tos": 74,
215
+ "ttl": 44,
216
+ "ip_version": 4,
217
+ "dst_ip": "205.185.123.167",
218
+ "src_ip": "92.218.110.236"
219
+ },
220
+ "tcp": {
221
+ "ack": 1843036805,
222
+ "checksum": 42157,
223
+ "seq": 4211318257,
224
+ "window": 2061
225
+ }
226
+ }
227
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "source": "curl-impersonate",
3
+ "profile": "firefox-133",
4
+ "protocol": "tls",
5
+ "record": "client_hello",
6
+ "description": "TLS 1.3 ClientHello generated by curl-impersonate against tls.peet.ws impersonating firefox133. Captured via transparent SOCKS5 relay with correct SNI. Fields below are intentionally randomized per RFC 8446/8701 and must be masked during golden comparison (Cat 14).",
7
+ "randomizedFields": [
8
+ {
9
+ "byteOffset": 12,
10
+ "length": 32,
11
+ "reason": "random"
12
+ },
13
+ {
14
+ "byteOffset": 114,
15
+ "length": 1683,
16
+ "reason": "grease"
17
+ }
18
+ ],
19
+ "createdAt": "2026-08-04T00:28:49Z"
20
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "source": "curl-impersonate",
3
+ "profile": "safari-17",
4
+ "protocol": "http2",
5
+ "record": "settings",
6
+ "description": "First HTTP/2 SETTINGS frame sent by curl-impersonate safari170. HTTP/2 SETTINGS are not randomized, so the ignore-list is empty.",
7
+ "randomizedFields": [],
8
+ "createdAt": "2026-08-04T00:28:49Z"
9
+ }
@@ -0,0 +1,214 @@
1
+ {
2
+ "donate": "Please consider donating to keep this API running. Visit https://tls.peet.ws",
3
+ "ip": "92.218.110.236:63765",
4
+ "http_version": "h2",
5
+ "method": "GET",
6
+ "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15",
7
+ "tls": {
8
+ "ciphers": [
9
+ "TLS_GREASE (0x6A6A)",
10
+ "TLS_AES_128_GCM_SHA256",
11
+ "TLS_AES_256_GCM_SHA384",
12
+ "TLS_CHACHA20_POLY1305_SHA256",
13
+ "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
14
+ "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
15
+ "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
16
+ "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
17
+ "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
18
+ "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
19
+ "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
20
+ "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
21
+ "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
22
+ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
23
+ "TLS_RSA_WITH_AES_256_GCM_SHA384",
24
+ "TLS_RSA_WITH_AES_128_GCM_SHA256",
25
+ "TLS_RSA_WITH_AES_256_CBC_SHA",
26
+ "TLS_RSA_WITH_AES_128_CBC_SHA",
27
+ "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
28
+ "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
29
+ "TLS_RSA_WITH_3DES_EDE_CBC_SHA"
30
+ ],
31
+ "extensions": [
32
+ {
33
+ "name": "TLS_GREASE (0xaaaa)"
34
+ },
35
+ {
36
+ "name": "server_name (0)",
37
+ "server_name": "tls.peet.ws"
38
+ },
39
+ {
40
+ "name": "extended_master_secret (23)",
41
+ "master_secret_data": "",
42
+ "extended_master_secret_data": ""
43
+ },
44
+ {
45
+ "name": "extensionRenegotiationInfo (boringssl) (65281)",
46
+ "data": "00"
47
+ },
48
+ {
49
+ "name": "supported_groups (10)",
50
+ "supported_groups": [
51
+ "TLS_GREASE (0x5a5a)",
52
+ "X25519 (29)",
53
+ "P-256 (23)",
54
+ "P-384 (24)",
55
+ "P-521 (25)"
56
+ ]
57
+ },
58
+ {
59
+ "name": "ec_point_formats (11)",
60
+ "elliptic_curves_point_formats": [
61
+ "0x00"
62
+ ]
63
+ },
64
+ {
65
+ "name": "application_layer_protocol_negotiation (16)",
66
+ "protocols": [
67
+ "h2",
68
+ "http/1.1"
69
+ ]
70
+ },
71
+ {
72
+ "name": "status_request (5)",
73
+ "status_request": {
74
+ "certificate_status_type": "OSCP (1)",
75
+ "responder_id_list_length": 0,
76
+ "request_extensions_length": 0
77
+ }
78
+ },
79
+ {
80
+ "name": "signature_algorithms (13)",
81
+ "signature_algorithms": [
82
+ "ecdsa_secp256r1_sha256",
83
+ "rsa_pss_rsae_sha256",
84
+ "rsa_pkcs1_sha256",
85
+ "ecdsa_secp384r1_sha384",
86
+ "ecdsa_sha1",
87
+ "rsa_pss_rsae_sha384",
88
+ "rsa_pss_rsae_sha384",
89
+ "rsa_pkcs1_sha384",
90
+ "rsa_pss_rsae_sha512",
91
+ "rsa_pkcs1_sha512",
92
+ "rsa_pkcs1_sha1"
93
+ ]
94
+ },
95
+ {
96
+ "name": "signed_certificate_timestamp (18)"
97
+ },
98
+ {
99
+ "name": "key_share (51)",
100
+ "shared_keys": [
101
+ {
102
+ "TLS_GREASE (0x5a5a)": "00"
103
+ },
104
+ {
105
+ "X25519 (29)": "4abbb4b6658ce3fac151869628a7dabaefa5845a6c586ec45189d0aaaa308034"
106
+ }
107
+ ]
108
+ },
109
+ {
110
+ "name": "psk_key_exchange_modes (45)",
111
+ "PSK_Key_Exchange_Mode": "PSK with (EC)DHE key establishment (psk_dhe_ke) (1)"
112
+ },
113
+ {
114
+ "name": "supported_versions (43)",
115
+ "versions": [
116
+ "TLS_GREASE (0x6a6a)",
117
+ "TLS 1.3",
118
+ "TLS 1.2",
119
+ "TLS 1.1",
120
+ "TLS 1.0"
121
+ ]
122
+ },
123
+ {
124
+ "name": "compress_certificate (27)",
125
+ "algorithms": [
126
+ "zlib (1)"
127
+ ]
128
+ },
129
+ {
130
+ "name": "TLS_GREASE (0xfafa)"
131
+ },
132
+ {
133
+ "name": "padding (21)",
134
+ "padding_data_length": 390
135
+ }
136
+ ],
137
+ "tls_version_record": "771",
138
+ "tls_version_negotiated": "772",
139
+ "ja3": "771,4865-4866-4867-49196-49195-52393-49200-49199-52392-49162-49161-49172-49171-157-156-53-47-49160-49170-10,0-23-65281-10-11-16-5-13-18-51-45-43-27-21,29-23-24-25,0",
140
+ "ja3_hash": "773906b0efdefa24a7f2b8eb6985bf37",
141
+ "ja4": "t13d2014h2_a09f3c656075_874d27d7ca63",
142
+ "ja4_r": "t13d2014h2_000a,002f,0035,009c,009d,1301,1302,1303,c008,c009,c00a,c012,c013,c014,c02b,c02c,c02f,c030,cca8,cca9_0005,000a,000b,000d,0012,0017,001b,002b,002d,0033,ff01_0403,0804,0401,0503,0203,0805,0805,0501,0806,0601,0201",
143
+ "peetprint": "GREASE-772-771-770-769|2-1.1|GREASE-29-23-24-25|1027-2052-1025-1283-515-2053-2053-1281-2054-1537-513|1|1|GREASE-4865-4866-4867-49196-49195-52393-49200-49199-52392-49162-49161-49172-49171-157-156-53-47-49160-49170-10|0-10-11-13-16-18-21-23-27-43-45-5-51-65281-GREASE-GREASE",
144
+ "peetprint_hash": "b2bafdc69377086c3416be278fd21121",
145
+ "client_random": "c9fba7116934f8045e9311add6745a43debae0d727493b41be0fd5909f8074a2",
146
+ "session_id": "113d41a2e1318188ca74b730a72f9e11e127d72c9b2139b32156dffd507298d6"
147
+ },
148
+ "http2": {
149
+ "akamai_fingerprint": "2:0;4:4194304;3:100|10485760|0|m,s,p,a",
150
+ "akamai_fingerprint_hash": "959a7e813b79b909a1a0b00a38e8bba3",
151
+ "sent_frames": [
152
+ {
153
+ "frame_type": "SETTINGS",
154
+ "length": 18,
155
+ "settings": [
156
+ "ENABLE_PUSH = 0",
157
+ "INITIAL_WINDOW_SIZE = 4194304",
158
+ "MAX_CONCURRENT_STREAMS = 100"
159
+ ]
160
+ },
161
+ {
162
+ "frame_type": "WINDOW_UPDATE",
163
+ "length": 4,
164
+ "increment": 10485760
165
+ },
166
+ {
167
+ "frame_type": "HEADERS",
168
+ "stream_id": 1,
169
+ "length": 247,
170
+ "headers": [
171
+ ":method: GET",
172
+ ":scheme: https",
173
+ ":path: /api/all",
174
+ ":authority: tls.peet.ws",
175
+ "accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
176
+ "sec-fetch-site: none",
177
+ "accept-encoding: gzip, deflate, br",
178
+ "sec-fetch-mode: navigate",
179
+ "user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15",
180
+ "accept-language: en-US,en;q=0.9",
181
+ "sec-fetch-dest: document"
182
+ ],
183
+ "flags": [
184
+ "EndStream (0x1)",
185
+ "EndHeaders (0x4)",
186
+ "Priority (0x20)"
187
+ ],
188
+ "priority": {
189
+ "weight": 255,
190
+ "depends_on": 0,
191
+ "exclusive": 0
192
+ }
193
+ }
194
+ ]
195
+ },
196
+ "tcpip": {
197
+ "cap_length": 583,
198
+ "dst_port": 443,
199
+ "src_port": 63765,
200
+ "ip": {
201
+ "tos": 74,
202
+ "ttl": 44,
203
+ "ip_version": 4,
204
+ "dst_ip": "205.185.123.167",
205
+ "src_ip": "92.218.110.236"
206
+ },
207
+ "tcp": {
208
+ "ack": 4175661838,
209
+ "checksum": 2612,
210
+ "seq": 373147202,
211
+ "window": 2061
212
+ }
213
+ }
214
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "source": "curl-impersonate",
3
+ "profile": "safari-17",
4
+ "protocol": "tls",
5
+ "record": "client_hello",
6
+ "description": "TLS 1.3 ClientHello generated by curl-impersonate against tls.peet.ws impersonating safari170. Captured via transparent SOCKS5 relay with correct SNI. Fields below are intentionally randomized per RFC 8446/8701 and must be masked during golden comparison (Cat 14).",
7
+ "randomizedFields": [
8
+ {
9
+ "byteOffset": 12,
10
+ "length": 32,
11
+ "reason": "random"
12
+ },
13
+ {
14
+ "byteOffset": 122,
15
+ "length": 395,
16
+ "reason": "grease"
17
+ }
18
+ ],
19
+ "createdAt": "2026-08-04T00:28:49Z"
20
+ }
@@ -1,18 +1,34 @@
1
1
  /**
2
2
  * Benchmark suite for protocol operations.
3
3
  *
4
- * Typed stubs for now — real benchmarks require live servers and the
5
- * underlying protocol stacks.
4
+ * Benchmarks here measure operations that need no live server — capture
5
+ * parsing, fingerprint computation, golden comparison, and compression
6
+ * round-trips — so they run in CI. Live TLS-handshake / HTTP/2-request
7
+ * benchmarks (which require a functional protocol stack + loopback server)
8
+ * remain a future addition; see docs/TEST-SUITE.md (Phase 10) for the full
9
+ * roadmap.
10
+ *
11
+ * Each benchmark runs `iterations` repetitions, collects per-iteration
12
+ * timings in microseconds, and reports p50/p95/p99 latency plus average
13
+ * throughput.
6
14
  */
7
15
  import type { BenchStats } from "../types.js";
8
- /** Benchmark a single TLS handshake over `iterations` runs. */
9
- export declare function benchmarkTlsHandshake(_iterations: number, _options?: {
16
+ /**
17
+ * Benchmark the TLS ClientHello fingerprint computation (JA3 + JA4) over
18
+ * `iterations` runs against the chrome-140 golden capture. Reports
19
+ * p50/p95/p99 latency in milliseconds.
20
+ */
21
+ export declare function benchmarkTlsHandshake(iterations: number, _options?: {
10
22
  host?: string;
11
23
  port?: number;
12
24
  profile?: string;
13
25
  }): BenchStats;
14
- /** Benchmark an HTTP/2 request round-trip over `iterations` runs. */
15
- export declare function benchmarkHttp2Request(_iterations: number, _options?: {
26
+ /**
27
+ * Benchmark the golden-comparison path (load + byte-compare) over
28
+ * `iterations` runs. This exercises the Category 14 packet-capture
29
+ * comparison core. Reports p50/p95/p99 latency in milliseconds.
30
+ */
31
+ export declare function benchmarkHttp2Request(iterations: number, _options?: {
16
32
  host?: string;
17
33
  port?: number;
18
34
  path?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"bench.d.ts","sourceRoot":"","sources":["../../src/bench/bench.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,+DAA+D;AAC/D,wBAAgB,qBAAqB,CACjC,WAAW,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9D,UAAU,CAIZ;AAED,qEAAqE;AACrE,wBAAgB,qBAAqB,CACjC,WAAW,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7E,UAAU,CAIZ"}
1
+ {"version":3,"file":"bench.d.ts","sourceRoot":"","sources":["../../src/bench/bench.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAoD9C;;;;GAIG;AACH,wBAAgB,qBAAqB,CACjC,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9D,UAAU,CAqBZ;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACjC,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7E,UAAU,CA0BZ"}
@@ -1,19 +1,114 @@
1
1
  /**
2
2
  * Benchmark suite for protocol operations.
3
3
  *
4
- * Typed stubs for now — real benchmarks require live servers and the
5
- * underlying protocol stacks.
4
+ * Benchmarks here measure operations that need no live server — capture
5
+ * parsing, fingerprint computation, golden comparison, and compression
6
+ * round-trips — so they run in CI. Live TLS-handshake / HTTP/2-request
7
+ * benchmarks (which require a functional protocol stack + loopback server)
8
+ * remain a future addition; see docs/TEST-SUITE.md (Phase 10) for the full
9
+ * roadmap.
10
+ *
11
+ * Each benchmark runs `iterations` repetitions, collects per-iteration
12
+ * timings in microseconds, and reports p50/p95/p99 latency plus average
13
+ * throughput.
14
+ */
15
+ import { readFileSync } from "node:fs";
16
+ import { join } from "node:path";
17
+ import { computeJa3, computeJa4 } from "../fingerprint/index.js";
18
+ import { compareBytes } from "../utils.js";
19
+ import { compression } from "@browsercore/compression";
20
+ const here = import.meta.dirname;
21
+ // src/bench -> package root -> captures/
22
+ const capturesDir = join(here, "..", "..", "captures");
23
+ /** Deterministic payload: byte[i] = i % 256 (reproducible, never random). */
24
+ function detBuffer(length) {
25
+ const bytes = new Uint8Array(length);
26
+ for (let i = 0; i < length; i++) {
27
+ bytes[i] = i % 256;
28
+ }
29
+ return bytes;
30
+ }
31
+ /** High-resolution timestamp in microseconds (browser-safe fallback). */
32
+ function nowUs() {
33
+ return performance.now() * 1000;
34
+ }
35
+ /**
36
+ * Run `fn` for `iterations` repetitions and return the per-iteration timings
37
+ * in microseconds, sorted ascending. The warm-up iteration is discarded to
38
+ * eliminate first-call JIT / cache effects.
6
39
  */
7
- /** Benchmark a single TLS handshake over `iterations` runs. */
8
- export function benchmarkTlsHandshake(_iterations, _options) {
9
- void _iterations;
40
+ function measure(iterations, fn) {
41
+ // Warm-up.
42
+ fn();
43
+ const timings = [];
44
+ for (let i = 0; i < iterations; i++) {
45
+ const start = nowUs();
46
+ fn();
47
+ timings.push(nowUs() - start);
48
+ }
49
+ timings.sort((a, b) => a - b);
50
+ return timings;
51
+ }
52
+ /** Read a percentile (0..1) from a sorted array of samples. */
53
+ function percentile(sorted, p) {
54
+ if (sorted.length === 0) {
55
+ return 0;
56
+ }
57
+ const idx = Math.min(sorted.length - 1, Math.ceil(p * sorted.length) - 1);
58
+ return sorted[Math.max(0, idx)] ?? 0;
59
+ }
60
+ /**
61
+ * Benchmark the TLS ClientHello fingerprint computation (JA3 + JA4) over
62
+ * `iterations` runs against the chrome-140 golden capture. Reports
63
+ * p50/p95/p99 latency in milliseconds.
64
+ */
65
+ export function benchmarkTlsHandshake(iterations, _options) {
10
66
  void _options;
11
- throw new Error("not implemented — see PLAN.md");
67
+ // Load the chrome-140 golden capture once; the benchmark measures the
68
+ // parsing + fingerprinting cost, not the live handshake (which needs a
69
+ // functional TLS stack + loopback server — future work).
70
+ const bytes = readFileSync(join(capturesDir, "chrome-140", "tls", "client_hello.bin"));
71
+ const iters = Math.max(1, iterations);
72
+ const timings = measure(iters, () => {
73
+ computeJa3(bytes);
74
+ computeJa4(bytes);
75
+ });
76
+ const avgMs = timings.reduce((a, b) => a + b, 0) / timings.length / 1000;
77
+ return {
78
+ iterations: iters,
79
+ avgMs,
80
+ p50: percentile(timings, 0.5) / 1000,
81
+ p95: percentile(timings, 0.95) / 1000,
82
+ p99: percentile(timings, 0.99) / 1000,
83
+ };
12
84
  }
13
- /** Benchmark an HTTP/2 request round-trip over `iterations` runs. */
14
- export function benchmarkHttp2Request(_iterations, _options) {
15
- void _iterations;
85
+ /**
86
+ * Benchmark the golden-comparison path (load + byte-compare) over
87
+ * `iterations` runs. This exercises the Category 14 packet-capture
88
+ * comparison core. Reports p50/p95/p99 latency in milliseconds.
89
+ */
90
+ export function benchmarkHttp2Request(iterations, _options) {
16
91
  void _options;
17
- throw new Error("not implemented — see PLAN.md");
92
+ // The HTTP/2 SETTINGS frame is small and deterministic — ideal for
93
+ // repeated comparison benchmarking without a live server.
94
+ const bytes = readFileSync(join(capturesDir, "chrome-140", "http2", "settings.bin"));
95
+ const iters = Math.max(1, iterations);
96
+ // Pre-compress a payload for the compression round-trip sub-benchmark.
97
+ const payload = detBuffer(8192);
98
+ const compressed = compression.gzip(payload);
99
+ const timings = measure(iters, () => {
100
+ // Golden comparison path: compare the SETTINGS bytes against themselves.
101
+ compareBytes(bytes, bytes);
102
+ // Compression round-trip: representative of per-request work.
103
+ compression.gunzip(compressed);
104
+ });
105
+ const avgMs = timings.reduce((a, b) => a + b, 0) / timings.length / 1000;
106
+ return {
107
+ iterations: iters,
108
+ avgMs,
109
+ p50: percentile(timings, 0.5) / 1000,
110
+ p95: percentile(timings, 0.95) / 1000,
111
+ p99: percentile(timings, 0.99) / 1000,
112
+ };
18
113
  }
19
114
  //# sourceMappingURL=bench.js.map