@jshookmcp/jshook 0.2.9 → 0.3.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 (187) hide show
  1. package/README.md +2 -2
  2. package/README.zh.md +2 -2
  3. package/dist/{AntiCheatDetector-BNk-EoBt.mjs → AntiCheatDetector-CqGDXmfc.mjs} +159 -53
  4. package/dist/{CodeInjector-Cq8q01kp.mjs → CodeInjector-BdjRfNx7.mjs} +5 -5
  5. package/dist/{ConsoleMonitor-CPVQW1Y-.mjs → ConsoleMonitor-DykL3IAw.mjs} +85 -17
  6. package/dist/{DetailedDataManager-BQQcxh64.mjs → DetailedDataManager-HT49OrvF.mjs} +1 -1
  7. package/dist/{ExtensionManager-CWYgw0YW.mjs → ExtensionManager-BDMsY2Dz.mjs} +15 -8
  8. package/dist/{HardwareBreakpoint-B9gZCdFP.mjs → HardwareBreakpoint-Cc2AFq1Y.mjs} +3 -3
  9. package/dist/{HeapAnalyzer-BLDH0dCv.mjs → HeapAnalyzer-DruMgsgj.mjs} +20 -20
  10. package/dist/{HookGeneratorBuilders.core.generators.storage-CtcdK78Q.mjs → HookGeneratorBuilders.core.generators.storage-CTbB4Lcx.mjs} +1 -74
  11. package/dist/{InstrumentationSession-CvPC7Jwy.mjs → InstrumentationSession-DLH0vd-z.mjs} +2 -2
  12. package/dist/{MemoryController-CbVdCIJF.mjs → MemoryController-CMtviNW_.mjs} +3 -3
  13. package/dist/{MemoryScanSession-BsDZbLYm.mjs → MemoryScanSession-ITgb_NMi.mjs} +2 -2
  14. package/dist/{MemoryScanner-Bcpml6II.mjs → MemoryScanner-CiL7Z3ey.mjs} +12 -9
  15. package/dist/{NativeMemoryManager.impl-dZtA1ZGn.mjs → NativeMemoryManager.impl-D9Lkovvn.mjs} +13 -10
  16. package/dist/{NativeMemoryManager.utils-B-FjA2mJ.mjs → NativeMemoryManager.utils-BBlAixF5.mjs} +1 -1
  17. package/dist/{PEAnalyzer-D1lzJ_VG.mjs → PEAnalyzer-DMQ44gen.mjs} +15 -15
  18. package/dist/{PageController-Bqm2kZ_X.mjs → PageController-BPJNqqBN.mjs} +18 -4
  19. package/dist/{PointerChainEngine-BOhyVsjx.mjs → PointerChainEngine-K7wN8Z-w.mjs} +10 -7
  20. package/dist/ProcessRegistry-zGg12QbE.mjs +74 -0
  21. package/dist/{ResponseBuilder-D3iFYx2N.mjs → ResponseBuilder-CJXWmWNw.mjs} +10 -10
  22. package/dist/{ScriptManager-aHHq0X7U.mjs → ScriptManager-ZuWD-0Jg.mjs} +195 -192
  23. package/dist/{Speedhack-CqdIFlQl.mjs → Speedhack-D-z0umeT.mjs} +2 -2
  24. package/dist/{StructureAnalyzer-DhFaPvRO.mjs → StructureAnalyzer-Cav5AVSL.mjs} +9 -6
  25. package/dist/{ToolCatalog-C0JGZoOm.mjs → ToolCatalog-5OJdMiF0.mjs} +81 -81
  26. package/dist/{ToolProbe-oC7aPrkv.mjs → ToolProbe-DbCFGyrg.mjs} +1 -1
  27. package/dist/{ToolRegistry-BjaF4oNz.mjs → ToolRegistry-B9krbTtI.mjs} +51 -2
  28. package/dist/{ToolRouter.policy-BWV67ZK-.mjs → ToolRouter.policy-BGDAGyeH.mjs} +60 -20
  29. package/dist/TraceRecorder-B41Z5XBj.mjs +1286 -0
  30. package/dist/{Win32API-CePkipZY.mjs → Win32API-C2kjj0ze.mjs} +18 -12
  31. package/dist/{Win32Debug-BvKs-gxc.mjs → Win32Debug-CKrGOTpo.mjs} +2 -2
  32. package/dist/{WorkflowEngine-CuvkZtWu.mjs → WorkflowEngine-DJ6M4opp.mjs} +226 -255
  33. package/dist/analysis-BHeJW2Nb.mjs +1234 -0
  34. package/dist/{antidebug-CqDTB_uk.mjs → antidebug-BRKeyt27.mjs} +3 -3
  35. package/dist/{artifactRetention-CFEprwPw.mjs → artifactRetention-CPXkUJXp.mjs} +13 -6
  36. package/dist/{artifacts-Bk2-_uPq.mjs → artifacts-DkfosXH3.mjs} +1 -1
  37. package/dist/authorization-schema-DRqyJMSk.mjs +31 -0
  38. package/dist/{binary-instrument-CXfpx6fT.mjs → binary-instrument--V3MAhJ4.mjs} +19 -27
  39. package/dist/bind-helpers-ClV34xdn.mjs +42 -0
  40. package/dist/{boringssl-inspector-BH2D3VKc.mjs → boringssl-inspector-Bo_LOLaS.mjs} +1 -1
  41. package/dist/{browser-BpOr5PEx.mjs → browser-Dx3_S2cG.mjs} +324 -37
  42. package/dist/capabilities-CcHlvWgK.mjs +33 -0
  43. package/dist/{constants-B0OANIBL.mjs → constants-CDZLOoVv.mjs} +18 -3
  44. package/dist/{coordination-qUbyF8KU.mjs → coordination-DgItD9DL.mjs} +2 -2
  45. package/dist/{debugger-gnKxRSN0.mjs → debugger-RS3RSAqs.mjs} +30 -13
  46. package/dist/definitions-BEoYofW5.mjs +47 -0
  47. package/dist/{definitions-bAhHQJq9.mjs → definitions-BRaefg3u.mjs} +11 -5
  48. package/dist/{definitions-DVGfrn7y.mjs → definitions-BbkvZkiv.mjs} +2 -2
  49. package/dist/definitions-BtWSHJ3o.mjs +17 -0
  50. package/dist/{definitions-BMfYXoNC.mjs → definitions-C1gCHO0i.mjs} +1 -1
  51. package/dist/{definitions-C1UvM5Iy.mjs → definitions-CDOg_b-l.mjs} +14 -2
  52. package/dist/definitions-CVPD9hzZ.mjs +54 -0
  53. package/dist/{definitions-Cke7zEb8.mjs → definitions-Cea8Lgl7.mjs} +1 -1
  54. package/dist/definitions-DAgIyjxM.mjs +10 -0
  55. package/dist/{definitions-B4rAvHNZ.mjs → definitions-DJA27nsL.mjs} +12 -9
  56. package/dist/{definitions-ClJLzsJQ.mjs → definitions-DKPFU3LW.mjs} +1 -1
  57. package/dist/{definitions-D3VsGcvz.mjs → definitions-DPRpZQ96.mjs} +7 -7
  58. package/dist/{definitions-B18eyf0B.mjs → definitions-DUE5gmdn.mjs} +1 -1
  59. package/dist/definitions-DYVjOtxa.mjs +26 -0
  60. package/dist/{definitions-BB_4jnmy.mjs → definitions-DcYLVLCo.mjs} +1 -1
  61. package/dist/{definitions-Beid2EB3.mjs → definitions-Pp5LI2H4.mjs} +1 -1
  62. package/dist/definitions-j9KdHVNR.mjs +14 -0
  63. package/dist/definitions-uzkjBwa7.mjs +258 -0
  64. package/dist/{definitions-Cq-zroAU.mjs → definitions-va-AnLuQ.mjs} +4 -4
  65. package/dist/{encoding-Bvz5jLRv.mjs → encoding-DJeqHmpd.mjs} +18 -4
  66. package/dist/{evidence-graph-bridge-C_fv9PuC.mjs → evidence-graph-bridge-DcYizFk2.mjs} +1 -0
  67. package/dist/{factory-DxlGh9Xf.mjs → factory-C90tBff6.mjs} +6 -6
  68. package/dist/flat-target-session-Dgax2Cy3.mjs +29 -0
  69. package/dist/{graphql-DYWzJ29s.mjs → graphql-CoHrhweh.mjs} +205 -34
  70. package/dist/{handlers-C67ktuRN.mjs → handlers-4jmR0nMs.mjs} +220 -32
  71. package/dist/{handlers-DlCJN4Td.mjs → handlers-BAHPxcch.mjs} +122 -90
  72. package/dist/{handlers-9sAbfIg-.mjs → handlers-BOs9b907.mjs} +849 -801
  73. package/dist/{handlers-DxGIq15_2.mjs → handlers-BWXEy6ef.mjs} +16 -16
  74. package/dist/{handlers-tB9Mp9ZK.mjs → handlers-Bndn6QvE.mjs} +31 -4
  75. package/dist/{handlers-CTsDAO6p.mjs → handlers-BqC4bD4s.mjs} +1 -1
  76. package/dist/{handlers-C87g8oCe.mjs → handlers-BtYq60bM2.mjs} +1 -1
  77. package/dist/{handlers-DeLOCd5m.mjs → handlers-BzgcB4iv.mjs} +17 -17
  78. package/dist/{handlers-Cgyg6c0U.mjs → handlers-CRyRWj2b.mjs} +237 -23
  79. package/dist/{handlers-U6L4xhuF.mjs → handlers-CVv2H1uq.mjs} +24 -17
  80. package/dist/{handlers-tiy7EIBp.mjs → handlers-Dl5a7JS4.mjs} +3 -3
  81. package/dist/{handlers-D6j6yka7.mjs → handlers-Dx2d7jt7.mjs} +1893 -1480
  82. package/dist/{handlers-Bl8zkwz1.mjs → handlers-Dz9PYsCa.mjs} +95 -6
  83. package/dist/handlers-HujRKC3b.mjs +661 -0
  84. package/dist/{handlers.impl-DS0d9fUw.mjs → handlers.impl-XWXkQfyi.mjs} +70 -24
  85. package/dist/{hooks-CzCWByww.mjs → hooks-B1B8NRHL.mjs} +3 -3
  86. package/dist/index.mjs +154 -144
  87. package/dist/{maintenance-P7ePRXQC.mjs → maintenance-PRMkLVRW.mjs} +35 -30
  88. package/dist/manifest-67Bok-Si.mjs +58 -0
  89. package/dist/{manifest-B3QVVeBS.mjs → manifest-6lNTMZAB2.mjs} +33 -28
  90. package/dist/manifest-B2duEHiH.mjs +90 -0
  91. package/dist/manifest-B6EY9Vm8.mjs +57 -0
  92. package/dist/{manifest-gZ4s_UtG.mjs → manifest-B6nKSbyY.mjs} +32 -33
  93. package/dist/{manifest-2ToTpjv8.mjs → manifest-BL8AQNPF.mjs} +31 -31
  94. package/dist/{manifest-DzwvxPJX.mjs → manifest-BSZvJJmV.mjs} +23 -14
  95. package/dist/{manifest-Sc_0JQ13.mjs → manifest-BU7qzUyX.mjs} +23 -23
  96. package/dist/{manifest-CT7zZBV1.mjs → manifest-Bl62e8WK.mjs} +24 -23
  97. package/dist/manifest-Bo5cXjdt.mjs +82 -0
  98. package/dist/manifest-BpS4gtUK.mjs +1347 -0
  99. package/dist/manifest-Bv65_e2W.mjs +101 -0
  100. package/dist/manifest-BytNIF4Z.mjs +117 -0
  101. package/dist/{manifest-BqrQ4Tpj.mjs → manifest-C-xtsjS3.mjs} +23 -23
  102. package/dist/{manifest-NXctwWQq.mjs → manifest-CDYl7OhA.mjs} +36 -38
  103. package/dist/manifest-CRZ3xmkD.mjs +61 -0
  104. package/dist/manifest-CoW6u4Tp.mjs +132 -0
  105. package/dist/manifest-Cq5zN_8A.mjs +50 -0
  106. package/dist/{manifest-CAhOuvSl.mjs → manifest-D7YZM_2e.mjs} +75 -85
  107. package/dist/{manifest-DCyjf4n2.mjs → manifest-DE_VrAeQ.mjs} +27 -7
  108. package/dist/manifest-DGsXSCpT.mjs +39 -0
  109. package/dist/{manifest-BB2J8IMJ.mjs → manifest-DJ2vfEuW.mjs} +48 -41
  110. package/dist/{manifest-3g71z6Bg.mjs → manifest-DPXDYhEu.mjs} +26 -25
  111. package/dist/manifest-Dd4fQb0a.mjs +322 -0
  112. package/dist/{manifest-CXsRWjjI.mjs → manifest-Deq6opGg.mjs} +95 -96
  113. package/dist/{manifest-C9RT5nk32.mjs → manifest-DfJTafJK.mjs} +14 -11
  114. package/dist/manifest-DgOdgN_j.mjs +50 -0
  115. package/dist/{manifest-BmtZzQiQ2.mjs → manifest-DlbMW4v4.mjs} +17 -15
  116. package/dist/{manifest-DrbmZcFl2.mjs → manifest-DmVfbH0w.mjs} +212 -91
  117. package/dist/manifest-Dog6Ddjr.mjs +109 -0
  118. package/dist/manifest-DvgU5FWb.mjs +58 -0
  119. package/dist/manifest-HsfDBs7j.mjs +50 -0
  120. package/dist/manifest-I8oQHvCG.mjs +186 -0
  121. package/dist/manifest-NvH_a-av.mjs +786 -0
  122. package/dist/{manifest-Dh8WBmEW.mjs → manifest-cEJU1v0Z.mjs} +24 -24
  123. package/dist/manifest-wOl5XLB12.mjs +112 -0
  124. package/dist/{modules-C184v-S9.mjs → modules-tZozf0LQ.mjs} +130 -860
  125. package/dist/{mojo-ipc-B_H61Afw.mjs → mojo-ipc-DXNEXEqb.mjs} +141 -26
  126. package/dist/{network-671Cw6hV.mjs → network-CPVvwvFg.mjs} +1329 -823
  127. package/dist/{outputPaths-B1uGmrWZ.mjs → outputPaths-um7lCRY3.mjs} +4 -8
  128. package/dist/{platform-WmNn8Sxb.mjs → platform-CYeFoTWp.mjs} +101 -10
  129. package/dist/{process-QcbIy5Zq.mjs → process-BTbgcVc6.mjs} +251 -346
  130. package/dist/{proxy-DqNs0bAd.mjs → proxy-r8YN6nP1.mjs} +30 -8
  131. package/dist/{registry-D-6e18lB.mjs → registry-Bl8ZQW61.mjs} +3 -3
  132. package/dist/{response-BQVP-xUn.mjs → response-CWhh2aLo.mjs} +7 -1
  133. package/dist/{shared-state-board-DV-dpHFJ.mjs → shared-state-board-BoZnSoj-.mjs} +2 -2
  134. package/dist/{sourcemap-Dq8ez8vS.mjs → sourcemap-BIDHUVXy.mjs} +350 -66
  135. package/dist/{streaming-BUQ0VJsg.mjs → streaming-Dal6utPp.mjs} +13 -13
  136. package/dist/{tool-builder-DCbIC5Eo.mjs → tool-builder-BHJp32mV.mjs} +1 -1
  137. package/dist/{transform-CiYJfNX0.mjs → transform-DRVgGG90.mjs} +18 -14
  138. package/dist/wasm-BYx5UOeG.mjs +1044 -0
  139. package/dist/webcrack-Be0_FccV.mjs +747 -0
  140. package/dist/{workflow-f3xJOcjx.mjs → workflow-BpuKEtvn.mjs} +8 -8
  141. package/package.json +76 -43
  142. package/dist/TraceRecorder-DgxyVbdQ.mjs +0 -519
  143. package/dist/analysis-CL9uACt9.mjs +0 -463
  144. package/dist/bind-helpers-xFfRF-qm.mjs +0 -22
  145. package/dist/definitions-6M-eejaT.mjs +0 -53
  146. package/dist/definitions-B3QdlrHv.mjs +0 -34
  147. package/dist/definitions-CXEI7QC72.mjs +0 -216
  148. package/dist/definitions-C_4r7Fo-2.mjs +0 -14
  149. package/dist/definitions-CkFDALoa.mjs +0 -26
  150. package/dist/definitions-Cy3Sl6gV.mjs +0 -34
  151. package/dist/definitions-LKpC3-nL.mjs +0 -9
  152. package/dist/handlers-DdFzXLvF.mjs +0 -446
  153. package/dist/manifest-82baTv4U.mjs +0 -45
  154. package/dist/manifest-BKbgbSiY.mjs +0 -60
  155. package/dist/manifest-Bcf-TJzH.mjs +0 -848
  156. package/dist/manifest-Bnd7kqEY.mjs +0 -55
  157. package/dist/manifest-BqQX6OQC2.mjs +0 -65
  158. package/dist/manifest-Br4RPFt5.mjs +0 -370
  159. package/dist/manifest-C5qDjysN.mjs +0 -107
  160. package/dist/manifest-CBYWCUBJ.mjs +0 -51
  161. package/dist/manifest-CFADCRa1.mjs +0 -37
  162. package/dist/manifest-CQVhavRF.mjs +0 -114
  163. package/dist/manifest-CV12bcrF.mjs +0 -121
  164. package/dist/manifest-CZLUCfG02.mjs +0 -95
  165. package/dist/manifest-D6phHKFd.mjs +0 -131
  166. package/dist/manifest-DHsnKgP6.mjs +0 -60
  167. package/dist/manifest-Df_dliIe.mjs +0 -55
  168. package/dist/manifest-DhKRAT8_.mjs +0 -92
  169. package/dist/manifest-DlpTj4ic2.mjs +0 -193
  170. package/dist/manifest-DuwHjUa5.mjs +0 -70
  171. package/dist/manifest-qSleDqdO.mjs +0 -1023
  172. package/dist/wasm-DQTnHDs4.mjs +0 -531
  173. /package/dist/{CacheAdapters-CDe5WPSV.mjs → CacheAdapters-jJFy20G-.mjs} +0 -0
  174. /package/dist/{DarwinAPI-BNPxu0RH.mjs → DarwinAPI-ETyy0xyo.mjs} +0 -0
  175. /package/dist/{EventBus-DgPmwpeu.mjs → EventBus-DFKvADm3.mjs} +0 -0
  176. /package/dist/{EvidenceGraphBridge-SFesNera.mjs → EvidenceGraphBridge-318Oi0Lf.mjs} +0 -0
  177. /package/dist/{FingerprintManager-gzWtkKuf.mjs → FingerprintManager-BN4UQWnX.mjs} +0 -0
  178. /package/dist/{PrerequisiteError-Dl33Svkz.mjs → PrerequisiteError-TuyZIs6n.mjs} +0 -0
  179. /package/dist/{ReverseEvidenceGraph-Dlsk94LC.mjs → ReverseEvidenceGraph-C02-gXOh.mjs} +0 -0
  180. /package/dist/{StealthVerifier-Bo4T3bz8.mjs → StealthVerifier-BWmPgQsv.mjs} +0 -0
  181. /package/dist/{VersionDetector-CwVLVdDM.mjs → VersionDetector-K3V4vGsw.mjs} +0 -0
  182. /package/dist/{betterSqlite3-0pqusHHH.mjs → betterSqlite3-DLSBZodi.mjs} +0 -0
  183. /package/dist/{concurrency-Bt0yv1kJ.mjs → concurrency-Drev_Vz9.mjs} +0 -0
  184. /package/dist/{formatAddress-DVkj9kpI.mjs → formatAddress-nnMvEohD.mjs} +0 -0
  185. /package/dist/{parse-args-BlRjqlkL.mjs → parse-args-B4cY5Vx5.mjs} +0 -0
  186. /package/dist/{ssrf-policy-ZaUfvhq7.mjs → ssrf-policy-Dsqd-DTX.mjs} +0 -0
  187. /package/dist/{types-CPhOReNX.mjs → types-DDBWs9UP.mjs} +0 -0
@@ -1,29 +1,39 @@
1
- import { n as asJsonResponse } from "./response-BQVP-xUn.mjs";
2
- import { a as enableKeyLog, c as parseKeyLog, i as disableKeyLog, l as summarizeKeyLog, n as TLSKeyLogExtractor, o as getKeyLogFilePath, r as decryptPayload, s as lookupSecret } from "./boringssl-inspector-BH2D3VKc.mjs";
3
- import { a as argString, n as argEnum, o as argStringArray, r as argNumber, t as argBool } from "./parse-args-BlRjqlkL.mjs";
4
- import { a as isLoopbackHost, s as isPrivateHost } from "./ssrf-policy-ZaUfvhq7.mjs";
1
+ import { n as asJsonResponse } from "./response-CWhh2aLo.mjs";
2
+ import { a as enableKeyLog, c as parseKeyLog, i as disableKeyLog, l as summarizeKeyLog, n as TLSKeyLogExtractor, o as getKeyLogFilePath, r as decryptPayload, s as lookupSecret } from "./boringssl-inspector-Bo_LOLaS.mjs";
3
+ import { a as argString, n as argEnum, o as argStringArray, r as argNumber, t as argBool } from "./parse-args-B4cY5Vx5.mjs";
4
+ import { a as isLoopbackHost, s as isPrivateHost } from "./ssrf-policy-Dsqd-DTX.mjs";
5
5
  import { X509Certificate, createHash, randomBytes, randomUUID } from "node:crypto";
6
6
  import { readFile } from "node:fs/promises";
7
7
  import { Socket, createServer, isIP } from "node:net";
8
8
  import { createSocket } from "node:dgram";
9
9
  import { checkServerIdentity, connect } from "node:tls";
10
- //#region src/server/domains/boringssl-inspector/handlers/shared.ts
11
- function validateNetworkTarget(host) {
12
- if (isPrivateHost(host) && !isLoopbackHost(host)) return {
13
- ok: false,
14
- error: `Blocked: target host "${host}" resolves to a private/internal address. SSRF protection applies.`
15
- };
16
- return null;
17
- }
10
+ //#region src/server/domains/boringssl-inspector/handlers/shared/types.ts
18
11
  const TLS_VERSION_SET = new Set([
19
12
  "TLSv1",
20
13
  "TLSv1.1",
21
14
  "TLSv1.2",
22
15
  "TLSv1.3"
23
16
  ]);
17
+ //#endregion
18
+ //#region src/server/domains/boringssl-inspector/handlers/shared/common.ts
24
19
  function errorMessage(error) {
25
20
  return error instanceof Error ? error.message : String(error);
26
21
  }
22
+ function normalizeHex(value) {
23
+ return value.replace(/\s+/g, "").toUpperCase();
24
+ }
25
+ function isHex(value) {
26
+ return value.length > 0 && value.length % 2 === 0 && /^[0-9A-F]+$/i.test(value);
27
+ }
28
+ //#endregion
29
+ //#region src/server/domains/boringssl-inspector/handlers/shared/tls-probe.ts
30
+ function validateNetworkTarget(host) {
31
+ if (isPrivateHost(host) && !isLoopbackHost(host)) return {
32
+ ok: false,
33
+ error: `Blocked: target host "${host}" resolves to a private/internal address. SSRF protection applies.`
34
+ };
35
+ return null;
36
+ }
27
37
  function normalizeSocketServername(servername) {
28
38
  return typeof servername === "string" && servername.length > 0 ? servername : null;
29
39
  }
@@ -35,6 +45,45 @@ function applyTlsValidationPolicy(options, allowInvalidCertificates) {
35
45
  Reflect.set(next, "rejectUnauthorized", !allowInvalidCertificates);
36
46
  return next;
37
47
  }
48
+ async function loadProbeCaBundle(args) {
49
+ const caPem = argString(args, "caPem") ?? null;
50
+ const caPath = argString(args, "caPath") ?? null;
51
+ if (caPem && caPath) return {
52
+ ok: false,
53
+ error: "caPem and caPath are mutually exclusive"
54
+ };
55
+ if (caPem) return {
56
+ ok: true,
57
+ ca: caPem,
58
+ source: "inline",
59
+ path: null,
60
+ bytes: Buffer.byteLength(caPem)
61
+ };
62
+ if (caPath) try {
63
+ const ca = await readFile(caPath, "utf8");
64
+ return {
65
+ ok: true,
66
+ ca,
67
+ source: "path",
68
+ path: caPath,
69
+ bytes: Buffer.byteLength(ca)
70
+ };
71
+ } catch (error) {
72
+ return {
73
+ ok: false,
74
+ error: `Failed to read caPath "${caPath}": ${errorMessage(error)}`
75
+ };
76
+ }
77
+ return {
78
+ ok: true,
79
+ ca: void 0,
80
+ source: null,
81
+ path: null,
82
+ bytes: null
83
+ };
84
+ }
85
+ //#endregion
86
+ //#region src/server/domains/boringssl-inspector/handlers/shared/certificates.ts
38
87
  function isNonEmptyObject(value) {
39
88
  return value !== null && typeof value === "object" && Object.keys(value).length > 0;
40
89
  }
@@ -81,43 +130,46 @@ function buildPeerCertificateChain(peerCertificate) {
81
130
  }
82
131
  return chain;
83
132
  }
84
- async function loadProbeCaBundle(args) {
85
- const caPem = argString(args, "caPem") ?? null;
86
- const caPath = argString(args, "caPath") ?? null;
87
- if (caPem && caPath) return {
88
- ok: false,
89
- error: "caPem and caPath are mutually exclusive"
90
- };
91
- if (caPem) return {
92
- ok: true,
93
- ca: caPem,
94
- source: "inline",
95
- path: null,
96
- bytes: Buffer.byteLength(caPem)
97
- };
98
- if (caPath) try {
99
- const ca = await readFile(caPath, "utf8");
133
+ function parseDerCertificate(der) {
134
+ const sha256 = createHash("sha256").update(der).digest("hex").toUpperCase();
135
+ try {
136
+ const cert = new X509Certificate(der);
100
137
  return {
101
- ok: true,
102
- ca,
103
- source: "path",
104
- path: caPath,
105
- bytes: Buffer.byteLength(ca)
138
+ subject: cert.subject || void 0,
139
+ issuer: cert.issuer || void 0,
140
+ serialNumber: cert.serialNumber || void 0,
141
+ validFrom: cert.validFrom || void 0,
142
+ validTo: cert.validTo || void 0,
143
+ sha256,
144
+ length: der.length
106
145
  };
107
- } catch (error) {
146
+ } catch {
108
147
  return {
109
- ok: false,
110
- error: `Failed to read caPath "${caPath}": ${errorMessage(error)}`
148
+ sha256,
149
+ length: der.length
111
150
  };
112
151
  }
113
- return {
114
- ok: true,
115
- ca: void 0,
116
- source: null,
117
- path: null,
118
- bytes: null
119
- };
120
152
  }
153
+ function parseCertificateChain(hexPayload) {
154
+ const buffer = Buffer.from(normalizeHex(hexPayload), "hex");
155
+ const certs = [];
156
+ let cursor = 0;
157
+ while (cursor < buffer.length - 4) if (buffer[cursor] === 48) {
158
+ const info = parseDerCertificate(buffer.subarray(cursor));
159
+ certs.push({
160
+ sha256: info.sha256,
161
+ length: info.length
162
+ });
163
+ cursor += info.length;
164
+ } else cursor += 1;
165
+ if (certs.length === 0 && buffer.length > 0) certs.push({
166
+ sha256: createHash("sha256").update(buffer).digest("hex").toUpperCase(),
167
+ length: buffer.length
168
+ });
169
+ return certs;
170
+ }
171
+ //#endregion
172
+ //#region src/server/domains/boringssl-inspector/handlers/shared/session-buffer.ts
121
173
  function makeSessionId(kind) {
122
174
  return `${kind}_${randomUUID()}`;
123
175
  }
@@ -137,6 +189,83 @@ function serializeSessionState(session) {
137
189
  error: session.error
138
190
  };
139
191
  }
192
+ function wakeSessionWaiters(session) {
193
+ for (const waiter of session.waiters) waiter();
194
+ session.waiters.clear();
195
+ }
196
+ function attachBufferedSession(session) {
197
+ session.socket.on("data", (chunk) => {
198
+ session.buffer = Buffer.concat([session.buffer, chunk]);
199
+ wakeSessionWaiters(session);
200
+ });
201
+ session.socket.on("end", () => {
202
+ session.ended = true;
203
+ wakeSessionWaiters(session);
204
+ });
205
+ session.socket.on("close", () => {
206
+ session.closed = true;
207
+ wakeSessionWaiters(session);
208
+ });
209
+ session.socket.on("error", (error) => {
210
+ session.error = error.message;
211
+ wakeSessionWaiters(session);
212
+ });
213
+ }
214
+ function waitForSessionActivity(session, timeoutMs) {
215
+ return new Promise((resolve) => {
216
+ const onActivity = () => {
217
+ clearTimeout(timer);
218
+ session.waiters.delete(onActivity);
219
+ resolve(true);
220
+ };
221
+ const timer = setTimeout(() => {
222
+ session.waiters.delete(onActivity);
223
+ resolve(false);
224
+ }, timeoutMs);
225
+ session.waiters.add(onActivity);
226
+ });
227
+ }
228
+ function consumeSessionBuffer(session, delimiter, includeDelimiter, maxBytes) {
229
+ const delimiterHex = delimiter ? delimiter.toString("hex").toUpperCase() : null;
230
+ if (delimiter) {
231
+ const matchIndex = session.buffer.indexOf(delimiter);
232
+ if (matchIndex >= 0) {
233
+ const consumedBytes = matchIndex + delimiter.length;
234
+ const data = includeDelimiter ? session.buffer.subarray(0, consumedBytes) : session.buffer.subarray(0, matchIndex);
235
+ session.buffer = session.buffer.subarray(consumedBytes);
236
+ return {
237
+ data,
238
+ matchedDelimiter: true,
239
+ stopReason: "delimiter",
240
+ delimiterHex
241
+ };
242
+ }
243
+ }
244
+ if (typeof maxBytes === "number" && session.buffer.length >= maxBytes) {
245
+ const data = session.buffer.subarray(0, maxBytes);
246
+ session.buffer = session.buffer.subarray(maxBytes);
247
+ return {
248
+ data,
249
+ matchedDelimiter: false,
250
+ stopReason: "maxBytes",
251
+ delimiterHex
252
+ };
253
+ }
254
+ if ((session.error || session.ended || session.closed) && session.buffer.length > 0) {
255
+ const data = session.buffer;
256
+ session.buffer = Buffer.alloc(0);
257
+ return {
258
+ data,
259
+ matchedDelimiter: false,
260
+ stopReason: session.error ? "error" : "closed",
261
+ delimiterHex
262
+ };
263
+ }
264
+ return null;
265
+ }
266
+ //#endregion
267
+ //#region src/server/domains/boringssl-inspector/handlers/shared/websocket.ts
268
+ const WEBSOCKET_ACCEPT_SUFFIX = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
140
269
  function serializeWebSocketSessionState(session) {
141
270
  return {
142
271
  bufferedBytes: session.parserBuffer.length,
@@ -153,32 +282,34 @@ function normalizeWebSocketPath(path) {
153
282
  return path.startsWith("/") ? path : `/${path}`;
154
283
  }
155
284
  function websocketOpcodeName(opcode) {
156
- switch (opcode) {
157
- case 1: return "text";
158
- case 2: return "binary";
159
- case 8: return "close";
160
- case 9: return "ping";
161
- case 10: return "pong";
162
- default: return null;
163
- }
285
+ return {
286
+ 1: "text",
287
+ 2: "binary",
288
+ 8: "close",
289
+ 9: "ping",
290
+ 10: "pong"
291
+ }[opcode] ?? null;
164
292
  }
165
293
  function computeWebSocketAccept(requestKey) {
166
- return createHash("sha1").update(`${requestKey}258EAFA5-E914-47DA-95CA-C5AB0DC85B11`, "utf8").digest("base64");
294
+ return createHash("sha1").update(`${requestKey}${WEBSOCKET_ACCEPT_SUFFIX}`, "utf8").digest("base64");
167
295
  }
168
296
  function encodeWebSocketFrame(type, payload, closeCode, closeReason) {
169
- let opcode = 1;
297
+ const opcodeByType = {
298
+ text: 1,
299
+ binary: 2,
300
+ close: 8,
301
+ ping: 9,
302
+ pong: 10
303
+ };
170
304
  let framePayload = payload;
171
- if (type === "binary") opcode = 2;
172
- else if (type === "close") {
173
- opcode = 8;
305
+ if (type === "close") {
174
306
  if (closeCode !== void 0 && closeCode !== null) {
175
307
  const reasonBuffer = closeReason ? Buffer.from(closeReason, "utf8") : Buffer.alloc(0);
176
308
  framePayload = Buffer.alloc(2 + reasonBuffer.length);
177
309
  framePayload.writeUInt16BE(closeCode, 0);
178
310
  reasonBuffer.copy(framePayload, 2);
179
311
  } else if (closeReason) framePayload = Buffer.from(closeReason, "utf8");
180
- } else if (type === "ping") opcode = 9;
181
- else if (type === "pong") opcode = 10;
312
+ }
182
313
  const maskKey = randomBytes(4);
183
314
  const payloadLength = framePayload.length;
184
315
  let header;
@@ -194,7 +325,7 @@ function encodeWebSocketFrame(type, payload, closeCode, closeReason) {
194
325
  header[1] = 255;
195
326
  header.writeBigUInt64BE(BigInt(payloadLength), 2);
196
327
  }
197
- header[0] = 128 | opcode;
328
+ header[0] = 128 | opcodeByType[type];
198
329
  const maskedPayload = Buffer.alloc(payloadLength);
199
330
  for (let index = 0; index < payloadLength; index += 1) maskedPayload[index] = framePayload[index] ^ maskKey[index % 4];
200
331
  return Buffer.concat([
@@ -255,42 +386,6 @@ function tryConsumeWebSocketFrame(buffer) {
255
386
  bytesConsumed: cursor + payloadLength
256
387
  };
257
388
  }
258
- function wakeSessionWaiters(session) {
259
- for (const waiter of session.waiters) waiter();
260
- session.waiters.clear();
261
- }
262
- function attachBufferedSession(session) {
263
- session.socket.on("data", (chunk) => {
264
- session.buffer = Buffer.concat([session.buffer, chunk]);
265
- wakeSessionWaiters(session);
266
- });
267
- session.socket.on("end", () => {
268
- session.ended = true;
269
- wakeSessionWaiters(session);
270
- });
271
- session.socket.on("close", () => {
272
- session.closed = true;
273
- wakeSessionWaiters(session);
274
- });
275
- session.socket.on("error", (error) => {
276
- session.error = error.message;
277
- wakeSessionWaiters(session);
278
- });
279
- }
280
- function waitForSessionActivity(session, timeoutMs) {
281
- return new Promise((resolve) => {
282
- const onActivity = () => {
283
- clearTimeout(timer);
284
- session.waiters.delete(onActivity);
285
- resolve(true);
286
- };
287
- const timer = setTimeout(() => {
288
- session.waiters.delete(onActivity);
289
- resolve(false);
290
- }, timeoutMs);
291
- session.waiters.add(onActivity);
292
- });
293
- }
294
389
  function wakeWebSocketWaiters(session) {
295
390
  for (const waiter of session.waiters) waiter();
296
391
  session.waiters.clear();
@@ -309,70 +404,59 @@ function waitForWebSocketActivity(session, timeoutMs) {
309
404
  session.waiters.add(onActivity);
310
405
  });
311
406
  }
312
- function consumeSessionBuffer(session, delimiter, includeDelimiter, maxBytes) {
313
- if (delimiter) {
314
- const matchIndex = session.buffer.indexOf(delimiter);
315
- if (matchIndex >= 0) {
316
- const consumedBytes = matchIndex + delimiter.length;
317
- const data = includeDelimiter ? session.buffer.subarray(0, consumedBytes) : session.buffer.subarray(0, matchIndex);
318
- session.buffer = session.buffer.subarray(consumedBytes);
319
- return {
320
- data,
321
- matchedDelimiter: true,
322
- stopReason: "delimiter",
323
- delimiterHex: delimiter.toString("hex").toUpperCase()
324
- };
325
- }
326
- }
327
- if (typeof maxBytes === "number" && session.buffer.length >= maxBytes) {
328
- const data = session.buffer.subarray(0, maxBytes);
329
- session.buffer = session.buffer.subarray(maxBytes);
330
- return {
331
- data,
332
- matchedDelimiter: false,
333
- stopReason: "maxBytes",
334
- delimiterHex: delimiter ? delimiter.toString("hex").toUpperCase() : null
335
- };
336
- }
337
- if ((session.error || session.ended || session.closed) && session.buffer.length > 0) {
338
- const data = session.buffer;
339
- session.buffer = Buffer.alloc(0);
340
- return {
341
- data,
342
- matchedDelimiter: false,
343
- stopReason: session.error ? "error" : "closed",
344
- delimiterHex: delimiter ? delimiter.toString("hex").toUpperCase() : null
345
- };
346
- }
347
- return null;
348
- }
349
- function normalizeHex(value) {
350
- return value.replace(/\s+/g, "").toUpperCase();
351
- }
352
- function isHex(value) {
353
- return value.length > 0 && value.length % 2 === 0 && /^[0-9A-F]+$/i.test(value);
354
- }
407
+ //#endregion
408
+ //#region src/server/domains/boringssl-inspector/handlers/shared/tls-parse.ts
409
+ const TLS_VERSION_NAMES = {
410
+ "3:1": "TLS 1.0",
411
+ "3:2": "TLS 1.1",
412
+ "3:3": "TLS 1.2",
413
+ "3:4": "TLS 1.3"
414
+ };
415
+ const CONTENT_TYPE_NAMES = {
416
+ 20: "change_cipher_spec",
417
+ 21: "alert",
418
+ 22: "handshake",
419
+ 23: "application_data",
420
+ 24: "heartbeat"
421
+ };
422
+ const CIPHER_SUITES_BY_ID = {
423
+ 156: "TLS_RSA_WITH_AES_128_GCM_SHA256",
424
+ 157: "TLS_RSA_WITH_AES_256_GCM_SHA384",
425
+ 52392: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
426
+ 52393: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
427
+ 4865: "TLS_AES_128_GCM_SHA256",
428
+ 4866: "TLS_AES_256_GCM_SHA384",
429
+ 4867: "TLS_CHACHA20_POLY1305_SHA256",
430
+ 4868: "TLS_AES_128_CCM_SHA256",
431
+ 4869: "TLS_AES_128_CCM_8_SHA256",
432
+ 49195: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
433
+ 49196: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
434
+ 49199: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
435
+ 49200: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
436
+ };
437
+ const EXTENSION_NAMES = {
438
+ 0: "server_name",
439
+ 1: "max_fragment_length",
440
+ 5: "status_request",
441
+ 10: "supported_groups",
442
+ 13: "signature_algorithms",
443
+ 16: "application_layer_protocol_negotiation",
444
+ 18: "signed_certificate_timestamp",
445
+ 23: "record_size_limit",
446
+ 27: "compress_certificate",
447
+ 35: "session_ticket",
448
+ 43: "supported_versions",
449
+ 44: "cookie",
450
+ 45: "psk_key_exchange_modes",
451
+ 49: "post_handshake_auth",
452
+ 51: "key_share"
453
+ };
355
454
  function tlsVersionName(major, minor) {
356
- if (major === 3 && minor === 1) return "TLS 1.0";
357
- if (major === 3 && minor === 2) return "TLS 1.1";
358
- if (major === 3 && minor === 3) return "TLS 1.2";
359
- if (major === 3 && minor === 4) return "TLS 1.3";
360
- return `0x${major.toString(16).padStart(2, "0")}${minor.toString(16).padStart(2, "0")}`;
455
+ return TLS_VERSION_NAMES[`${major}:${minor}`] ?? `0x${major.toString(16).padStart(2, "0")}${minor.toString(16).padStart(2, "0")}`;
361
456
  }
362
457
  function contentTypeName(contentType) {
363
- if (contentType === 20) return "change_cipher_spec";
364
- if (contentType === 21) return "alert";
365
- if (contentType === 22) return "handshake";
366
- if (contentType === 23) return "application_data";
367
- if (contentType === 24) return "heartbeat";
368
- return "unknown";
458
+ return CONTENT_TYPE_NAMES[contentType] ?? "unknown";
369
459
  }
370
- /**
371
- * Parse TLS ClientHello to extract SNI, cipher suites, and extensions.
372
- * Handles two input layouts:
373
- * - With handshake header: [type(1) + length(3) + version(2) + random(32) + ...]
374
- * - Without handshake header: [version(2) + random(32) + ...]
375
- */
376
460
  function parseClientHello(payload) {
377
461
  const result = {
378
462
  cipherSuites: [],
@@ -426,88 +510,12 @@ function parseClientHello(payload) {
426
510
  }
427
511
  return result;
428
512
  }
429
- const CIPHER_SUITES_BY_ID = {
430
- 156: "TLS_RSA_WITH_AES_128_GCM_SHA256",
431
- 157: "TLS_RSA_WITH_AES_256_GCM_SHA384",
432
- 52392: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
433
- 52393: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
434
- 4865: "TLS_AES_128_GCM_SHA256",
435
- 4866: "TLS_AES_256_GCM_SHA384",
436
- 4867: "TLS_CHACHA20_POLY1305_SHA256",
437
- 4868: "TLS_AES_128_CCM_SHA256",
438
- 4869: "TLS_AES_128_CCM_8_SHA256",
439
- 49195: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
440
- 49196: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
441
- 49199: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
442
- 49200: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
443
- };
444
- const EXTENSION_NAMES = {
445
- 0: "server_name",
446
- 1: "max_fragment_length",
447
- 5: "status_request",
448
- 10: "supported_groups",
449
- 13: "signature_algorithms",
450
- 16: "application_layer_protocol_negotiation",
451
- 18: "signed_certificate_timestamp",
452
- 23: "record_size_limit",
453
- 27: "compress_certificate",
454
- 35: "session_ticket",
455
- 43: "supported_versions",
456
- 44: "cookie",
457
- 45: "psk_key_exchange_modes",
458
- 49: "post_handshake_auth",
459
- 51: "key_share"
460
- };
461
- /**
462
- * Parse a DER-encoded certificate to extract basic info.
463
- */
464
- function parseDerCertificate(der) {
465
- const sha256 = createHash("sha256").update(der).digest("hex").toUpperCase();
466
- try {
467
- const cert = new X509Certificate(der);
468
- return {
469
- subject: cert.subject || void 0,
470
- issuer: cert.issuer || void 0,
471
- serialNumber: cert.serialNumber || void 0,
472
- validFrom: cert.validFrom || void 0,
473
- validTo: cert.validTo || void 0,
474
- sha256,
475
- length: der.length
476
- };
477
- } catch {
478
- return {
479
- sha256,
480
- length: der.length
481
- };
482
- }
483
- }
484
- /**
485
- * Parse a chain of DER certificates from hex.
486
- */
487
- function parseCertificateChain(hexPayload) {
488
- const buffer = Buffer.from(normalizeHex(hexPayload), "hex");
489
- const certs = [];
490
- let cursor = 0;
491
- while (cursor < buffer.length - 4) if (buffer[cursor] === 48) {
492
- const info = parseDerCertificate(buffer.subarray(cursor));
493
- certs.push({
494
- sha256: info.sha256,
495
- length: info.length
496
- });
497
- cursor += info.length;
498
- } else cursor += 1;
499
- if (certs.length === 0 && buffer.length > 0) certs.push({
500
- sha256: createHash("sha256").update(buffer).digest("hex").toUpperCase(),
501
- length: buffer.length
502
- });
503
- return certs;
504
- }
505
513
  //#endregion
506
- //#region src/server/domains/boringssl-inspector/handlers/handler-class.ts
514
+ //#region src/server/domains/boringssl-inspector/handlers/base.ts
507
515
  /**
508
- * BoringsslInspectorHandlershandler methods using shared utilities from ./shared.ts.
516
+ * BoringsslInspectorBaseHandlersshared state and transport helpers.
509
517
  */
510
- var BoringsslInspectorHandlers = class {
518
+ var BoringsslInspectorBaseHandlers = class {
511
519
  extensionInvoke;
512
520
  eventBus;
513
521
  tcpSessions = /* @__PURE__ */ new Map();
@@ -531,9 +539,6 @@ var BoringsslInspectorHandlers = class {
531
539
  getWebSocketSession(sessionId) {
532
540
  return this.websocketSessions.get(sessionId) ?? null;
533
541
  }
534
- emitWebSocketEvent(event, payload) {
535
- this.eventBus?.emit(event, payload);
536
- }
537
542
  parseWritePayload(args) {
538
543
  const dataHex = argString(args, "dataHex");
539
544
  const dataText = argString(args, "dataText");
@@ -743,347 +748,14 @@ var BoringsslInspectorHandlers = class {
743
748
  session.activeRead = false;
744
749
  }
745
750
  }
746
- attachWebSocketSession(session) {
747
- const parseBufferedFrames = () => {
748
- while (session.parserBuffer.length > 0) {
749
- let consumed;
750
- try {
751
- consumed = tryConsumeWebSocketFrame(session.parserBuffer);
752
- } catch (error) {
753
- session.error = errorMessage(error);
754
- session.socket.destroy();
755
- break;
756
- }
757
- if (!consumed) break;
758
- session.parserBuffer = session.parserBuffer.subarray(consumed.bytesConsumed);
759
- const frame = consumed.frame;
760
- session.frames.push(frame);
761
- if (frame.type === "ping" && !session.closeSent && !session.socket.destroyed) {
762
- const pongFrame = encodeWebSocketFrame("pong", frame.data);
763
- session.socket.write(pongFrame);
764
- this.emitWebSocketEvent("websocket:session_written", {
765
- sessionId: session.id,
766
- frameType: "pong",
767
- byteLength: frame.data.length,
768
- automatic: true,
769
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
770
- });
771
- }
772
- if (frame.type === "close") {
773
- session.closeReceived = true;
774
- if (!session.closeSent && !session.socket.destroyed) {
775
- session.closeSent = true;
776
- session.socket.write(encodeWebSocketFrame("close", frame.data, frame.closeCode, frame.closeReason));
777
- this.emitWebSocketEvent("websocket:session_written", {
778
- sessionId: session.id,
779
- frameType: "close",
780
- byteLength: frame.data.length,
781
- automatic: true,
782
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
783
- });
784
- }
785
- }
786
- }
787
- wakeWebSocketWaiters(session);
751
+ async closeBufferedSession(sessionId, sessions, kind, args) {
752
+ const session = sessions.get(sessionId);
753
+ if (!session) return {
754
+ ok: false,
755
+ error: `Unknown ${kind} sessionId "${sessionId}"`
788
756
  };
789
- session.socket.on("data", (chunk) => {
790
- session.parserBuffer = Buffer.concat([session.parserBuffer, chunk]);
791
- parseBufferedFrames();
792
- });
793
- session.socket.on("end", () => {
794
- session.ended = true;
795
- wakeWebSocketWaiters(session);
796
- });
797
- session.socket.on("close", () => {
798
- session.closed = true;
799
- wakeWebSocketWaiters(session);
800
- });
801
- session.socket.on("error", (error) => {
802
- session.error = error.message;
803
- wakeWebSocketWaiters(session);
804
- });
805
- parseBufferedFrames();
806
- }
807
- async readWebSocketFrame(session, args) {
808
- const timeoutMs = argNumber(args, "timeoutMs") ?? 5e3;
809
- if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) return {
810
- ok: false,
811
- error: "timeoutMs must be a positive number"
812
- };
813
- if (session.activeRead) return {
814
- ok: false,
815
- error: `Session "${session.id}" already has a pending read`,
816
- sessionId: session.id,
817
- kind: session.kind,
818
- state: serializeWebSocketSessionState(session)
819
- };
820
- session.activeRead = true;
821
- const startedAt = Date.now();
822
- try {
823
- while (true) {
824
- const frame = session.frames.shift();
825
- if (frame) {
826
- this.emitWebSocketEvent("websocket:frame_read", {
827
- sessionId: session.id,
828
- frameType: frame.type,
829
- byteLength: frame.data.length,
830
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
831
- });
832
- return {
833
- ok: true,
834
- sessionId: session.id,
835
- kind: session.kind,
836
- scheme: session.scheme,
837
- frameType: frame.type,
838
- fin: frame.fin,
839
- opcode: frame.opcode,
840
- masked: frame.masked,
841
- byteLength: frame.data.length,
842
- dataHex: frame.data.toString("hex").toUpperCase(),
843
- dataText: frame.type === "binary" ? null : frame.data.toString("utf8"),
844
- closeCode: frame.closeCode,
845
- closeReason: frame.closeReason,
846
- elapsedMs: Date.now() - startedAt,
847
- state: serializeWebSocketSessionState(session)
848
- };
849
- }
850
- if (session.error) return {
851
- ok: false,
852
- error: session.error,
853
- sessionId: session.id,
854
- kind: session.kind,
855
- state: serializeWebSocketSessionState(session)
856
- };
857
- if (session.closed || session.ended) return {
858
- ok: false,
859
- error: "socket closed before a WebSocket frame was available",
860
- sessionId: session.id,
861
- kind: session.kind,
862
- state: serializeWebSocketSessionState(session)
863
- };
864
- const remainingMs = timeoutMs - (Date.now() - startedAt);
865
- if (remainingMs <= 0) return {
866
- ok: false,
867
- error: "read timed out",
868
- sessionId: session.id,
869
- kind: session.kind,
870
- state: serializeWebSocketSessionState(session)
871
- };
872
- if (!await waitForWebSocketActivity(session, remainingMs)) return {
873
- ok: false,
874
- error: "read timed out",
875
- sessionId: session.id,
876
- kind: session.kind,
877
- state: serializeWebSocketSessionState(session)
878
- };
879
- }
880
- } finally {
881
- session.activeRead = false;
882
- }
883
- }
884
- async sendWebSocketFrame(session, args) {
885
- if (session.closed || session.socket.destroyed) return {
886
- ok: false,
887
- error: `Session "${session.id}" is already closed`,
888
- sessionId: session.id,
889
- kind: session.kind,
890
- state: serializeWebSocketSessionState(session)
891
- };
892
- const frameType = argEnum(args, "frameType", new Set([
893
- "text",
894
- "binary",
895
- "ping",
896
- "pong",
897
- "close"
898
- ]));
899
- if (!frameType) return {
900
- ok: false,
901
- error: "frameType is required"
902
- };
903
- const dataHex = argString(args, "dataHex");
904
- const dataText = argString(args, "dataText");
905
- if (dataHex && dataText) return {
906
- ok: false,
907
- error: "dataHex and dataText are mutually exclusive"
908
- };
909
- const timeoutMs = argNumber(args, "timeoutMs") ?? 5e3;
910
- if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) return {
911
- ok: false,
912
- error: "timeoutMs must be a positive number"
913
- };
914
- let payload = Buffer.alloc(0);
915
- if (dataHex) {
916
- const normalized = normalizeHex(dataHex);
917
- if (!isHex(normalized)) return {
918
- ok: false,
919
- error: "dataHex must be valid even-length hexadecimal data"
920
- };
921
- payload = Buffer.from(normalized, "hex");
922
- } else if (dataText !== void 0) payload = Buffer.from(dataText, "utf8");
923
- let closeCode = null;
924
- let closeReason = null;
925
- if (frameType === "close") {
926
- const rawCloseCode = argNumber(args, "closeCode");
927
- if (rawCloseCode !== void 0) {
928
- if (!Number.isInteger(rawCloseCode) || rawCloseCode < 1e3 || rawCloseCode > 4999) return {
929
- ok: false,
930
- error: "closeCode must be an integer between 1000 and 4999"
931
- };
932
- closeCode = rawCloseCode;
933
- }
934
- closeReason = argString(args, "closeReason") ?? null;
935
- if (dataHex || dataText) return {
936
- ok: false,
937
- error: "close frames use closeCode/closeReason instead of dataHex/dataText"
938
- };
939
- session.closeSent = true;
940
- }
941
- if (frameType === "text" && dataHex) return {
942
- ok: false,
943
- error: "text frames require UTF-8 dataText instead of dataHex"
944
- };
945
- const frameBuffer = encodeWebSocketFrame(frameType, payload, closeCode, closeReason);
946
- return new Promise((resolve) => {
947
- let settled = false;
948
- const finish = (result) => {
949
- if (settled) return;
950
- settled = true;
951
- clearTimeout(timer);
952
- session.socket.off("error", onError);
953
- resolve(result);
954
- };
955
- const timer = setTimeout(() => {
956
- finish({
957
- ok: false,
958
- error: "write timed out",
959
- sessionId: session.id,
960
- kind: session.kind,
961
- state: serializeWebSocketSessionState(session)
962
- });
963
- }, timeoutMs);
964
- const onError = (error) => {
965
- finish({
966
- ok: false,
967
- error: error.message,
968
- sessionId: session.id,
969
- kind: session.kind,
970
- state: serializeWebSocketSessionState(session)
971
- });
972
- };
973
- session.socket.once("error", onError);
974
- session.socket.write(frameBuffer, () => {
975
- this.emitWebSocketEvent("websocket:session_written", {
976
- sessionId: session.id,
977
- frameType,
978
- byteLength: frameType === "close" ? closeReason ? Buffer.byteLength(closeReason) + 2 : closeCode ? 2 : 0 : payload.length,
979
- automatic: false,
980
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
981
- });
982
- finish({
983
- ok: true,
984
- sessionId: session.id,
985
- kind: session.kind,
986
- scheme: session.scheme,
987
- frameType,
988
- bytesWritten: frameBuffer.length,
989
- payloadBytes: frameType === "close" ? closeReason ? Buffer.byteLength(closeReason) + 2 : closeCode ? 2 : 0 : payload.length,
990
- state: serializeWebSocketSessionState(session)
991
- });
992
- });
993
- });
994
- }
995
- async closeWebSocketSession(sessionId, args) {
996
- const session = this.websocketSessions.get(sessionId);
997
- if (!session) return {
998
- ok: false,
999
- error: `Unknown websocket sessionId "${sessionId}"`
1000
- };
1001
- const force = argBool(args, "force") ?? false;
1002
- const timeoutMs = argNumber(args, "timeoutMs") ?? 1e3;
1003
- if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) return {
1004
- ok: false,
1005
- error: "timeoutMs must be a positive number"
1006
- };
1007
- const queuedFramesDiscarded = session.frames.length;
1008
- if (session.closed || session.socket.destroyed) {
1009
- this.websocketSessions.delete(sessionId);
1010
- return {
1011
- ok: true,
1012
- sessionId,
1013
- kind: session.kind,
1014
- force,
1015
- closed: true,
1016
- queuedFramesDiscarded,
1017
- state: serializeWebSocketSessionState(session)
1018
- };
1019
- }
1020
- let closeCode = null;
1021
- const rawCloseCode = argNumber(args, "closeCode");
1022
- if (rawCloseCode !== void 0) {
1023
- if (!Number.isInteger(rawCloseCode) || rawCloseCode < 1e3 || rawCloseCode > 4999) return {
1024
- ok: false,
1025
- error: "closeCode must be an integer between 1000 and 4999"
1026
- };
1027
- closeCode = rawCloseCode;
1028
- }
1029
- const closeReason = argString(args, "closeReason") ?? null;
1030
- return new Promise((resolve) => {
1031
- let settled = false;
1032
- const finish = (closed) => {
1033
- if (settled) return;
1034
- settled = true;
1035
- clearTimeout(timer);
1036
- session.socket.off("close", onClose);
1037
- session.socket.off("error", onError);
1038
- this.websocketSessions.delete(sessionId);
1039
- this.emitWebSocketEvent("websocket:session_closed", {
1040
- sessionId,
1041
- reason: session.error,
1042
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1043
- });
1044
- resolve({
1045
- ok: true,
1046
- sessionId,
1047
- kind: session.kind,
1048
- force,
1049
- closed,
1050
- queuedFramesDiscarded,
1051
- state: serializeWebSocketSessionState(session)
1052
- });
1053
- };
1054
- const onClose = () => finish(true);
1055
- const onError = () => finish(session.socket.destroyed || session.closed);
1056
- const timer = setTimeout(() => {
1057
- session.socket.destroy();
1058
- finish(session.socket.destroyed || session.closed);
1059
- }, timeoutMs);
1060
- session.socket.once("close", onClose);
1061
- session.socket.once("error", onError);
1062
- if (force) {
1063
- session.socket.destroy();
1064
- return;
1065
- }
1066
- if (!session.closeSent) {
1067
- session.closeSent = true;
1068
- session.socket.write(encodeWebSocketFrame("close", Buffer.alloc(0), closeCode, closeReason));
1069
- this.emitWebSocketEvent("websocket:session_written", {
1070
- sessionId,
1071
- frameType: "close",
1072
- byteLength: closeReason ? Buffer.byteLength(closeReason) + 2 : closeCode ? 2 : 0,
1073
- automatic: false,
1074
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1075
- });
1076
- }
1077
- });
1078
- }
1079
- async closeBufferedSession(sessionId, sessions, kind, args) {
1080
- const session = sessions.get(sessionId);
1081
- if (!session) return {
1082
- ok: false,
1083
- error: `Unknown ${kind} sessionId "${sessionId}"`
1084
- };
1085
- const force = argBool(args, "force") ?? false;
1086
- const timeoutMs = argNumber(args, "timeoutMs") ?? 1e3;
757
+ const force = argBool(args, "force") ?? false;
758
+ const timeoutMs = argNumber(args, "timeoutMs") ?? 1e3;
1087
759
  if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) return {
1088
760
  ok: false,
1089
761
  error: "timeoutMs must be a positive number"
@@ -1145,6 +817,13 @@ var BoringsslInspectorHandlers = class {
1145
817
  session.socket.end();
1146
818
  });
1147
819
  }
820
+ };
821
+ //#endregion
822
+ //#region src/server/domains/boringssl-inspector/handlers/tls-handlers.ts
823
+ /**
824
+ * BoringsslInspectorTlsHandlers — keylog and TLS parsing helpers.
825
+ */
826
+ var BoringsslInspectorTlsHandlers = class extends BoringsslInspectorBaseHandlers {
1148
827
  async handleTlsKeylogEnable(_args) {
1149
828
  return {
1150
829
  enabled: true,
@@ -1240,7 +919,7 @@ var BoringsslInspectorHandlers = class {
1240
919
  error: "rawHex is required"
1241
920
  });
1242
921
  const normalizedHex = normalizeHex(rawHex);
1243
- if (!isHex(normalizedHex)) return asJsonResponse({
922
+ if (!/^(?:[0-9a-f]{2})+$/i.test(normalizedHex)) return asJsonResponse({
1244
923
  success: false,
1245
924
  error: "Invalid hex payload"
1246
925
  });
@@ -1249,9 +928,9 @@ var BoringsslInspectorHandlers = class {
1249
928
  success: false,
1250
929
  error: "TLS record is too short"
1251
930
  });
1252
- const contentType = record[0] ?? 0;
1253
- const versionMajor = record[1] ?? 0;
1254
- const versionMinor = record[2] ?? 0;
931
+ const contentType = record[0];
932
+ const versionMajor = record[1];
933
+ const versionMinor = record[2];
1255
934
  const declaredLength = record.readUInt16BE(3);
1256
935
  const payload = record.subarray(5);
1257
936
  const clientHello = contentType === 22 && payload.length > 0 && payload[0] === 1 ? parseClientHello(payload) : void 0;
@@ -1331,12 +1010,25 @@ var BoringsslInspectorHandlers = class {
1331
1010
  return asJsonResponse({
1332
1011
  success: true,
1333
1012
  certificateCount: certs.length,
1334
- fingerprints: certs.map((c) => ({
1335
- sha256: c.sha256,
1336
- length: c.length
1013
+ fingerprints: certs.map((cert) => ({
1014
+ sha256: cert.sha256,
1015
+ length: cert.length
1337
1016
  }))
1338
1017
  });
1339
1018
  }
1019
+ };
1020
+ //#endregion
1021
+ //#region src/server/domains/boringssl-inspector/handlers/tls-probe-handlers.ts
1022
+ /**
1023
+ * BoringsslInspectorTlsProbeHandlers — active TLS probe handler.
1024
+ */
1025
+ const TLS_VERSION_ORDER$2 = [
1026
+ "TLSv1",
1027
+ "TLSv1.1",
1028
+ "TLSv1.2",
1029
+ "TLSv1.3"
1030
+ ];
1031
+ var BoringsslInspectorTlsProbeHandlers = class extends BoringsslInspectorTlsHandlers {
1340
1032
  async handleTlsProbeEndpoint(args) {
1341
1033
  const host = argString(args, "host")?.trim() ?? null;
1342
1034
  if (!host) return {
@@ -1368,13 +1060,7 @@ var BoringsslInspectorHandlers = class {
1368
1060
  error: errorMessage(error)
1369
1061
  };
1370
1062
  }
1371
- const versionOrder = [
1372
- "TLSv1",
1373
- "TLSv1.1",
1374
- "TLSv1.2",
1375
- "TLSv1.3"
1376
- ];
1377
- if (minVersion && maxVersion && versionOrder.indexOf(minVersion) > versionOrder.indexOf(maxVersion)) return {
1063
+ if (minVersion && maxVersion && TLS_VERSION_ORDER$2.indexOf(minVersion) > TLS_VERSION_ORDER$2.indexOf(maxVersion)) return {
1378
1064
  ok: false,
1379
1065
  error: "minVersion must not be greater than maxVersion"
1380
1066
  };
@@ -1547,6 +1233,19 @@ var BoringsslInspectorHandlers = class {
1547
1233
  });
1548
1234
  });
1549
1235
  }
1236
+ };
1237
+ //#endregion
1238
+ //#region src/server/domains/boringssl-inspector/handlers/session-handlers.ts
1239
+ /**
1240
+ * BoringsslInspectorSessionHandlers — stateful TCP and TLS session handlers.
1241
+ */
1242
+ const TLS_VERSION_ORDER$1 = [
1243
+ "TLSv1",
1244
+ "TLSv1.1",
1245
+ "TLSv1.2",
1246
+ "TLSv1.3"
1247
+ ];
1248
+ var BoringsslInspectorSessionHandlers = class extends BoringsslInspectorTlsProbeHandlers {
1550
1249
  async handleTcpOpen(args) {
1551
1250
  const host = argString(args, "host") ?? "127.0.0.1";
1552
1251
  const port = argNumber(args, "port");
@@ -1702,13 +1401,7 @@ var BoringsslInspectorHandlers = class {
1702
1401
  error: errorMessage(error)
1703
1402
  };
1704
1403
  }
1705
- const versionOrder = [
1706
- "TLSv1",
1707
- "TLSv1.1",
1708
- "TLSv1.2",
1709
- "TLSv1.3"
1710
- ];
1711
- if (minVersion && maxVersion && versionOrder.indexOf(minVersion) > versionOrder.indexOf(maxVersion)) return {
1404
+ if (minVersion && maxVersion && TLS_VERSION_ORDER$1.indexOf(minVersion) > TLS_VERSION_ORDER$1.indexOf(maxVersion)) return {
1712
1405
  ok: false,
1713
1406
  error: "minVersion must not be greater than maxVersion"
1714
1407
  };
@@ -1741,195 +1434,585 @@ var BoringsslInspectorHandlers = class {
1741
1434
  const startedAt = Date.now();
1742
1435
  return new Promise((resolve) => {
1743
1436
  let settled = false;
1744
- const socket = connect(applyTlsValidationPolicy({
1745
- host,
1746
- port,
1747
- servername: target.requestedServername ?? void 0,
1748
- ...minVersion ? { minVersion } : {},
1749
- ...maxVersion ? { maxVersion } : {},
1750
- ...alpnProtocols.length > 0 ? { ALPNProtocols: alpnProtocols } : {},
1751
- ...caBundle.ca ? { ca: caBundle.ca } : {}
1752
- }, allowInvalidCertificates));
1753
- const finish = (payload) => {
1437
+ const socket = connect(applyTlsValidationPolicy({
1438
+ host,
1439
+ port,
1440
+ servername: target.requestedServername ?? void 0,
1441
+ ...minVersion ? { minVersion } : {},
1442
+ ...maxVersion ? { maxVersion } : {},
1443
+ ...alpnProtocols.length > 0 ? { ALPNProtocols: alpnProtocols } : {},
1444
+ ...caBundle.ca ? { ca: caBundle.ca } : {}
1445
+ }, allowInvalidCertificates));
1446
+ const finish = (payload) => {
1447
+ if (settled) return;
1448
+ settled = true;
1449
+ clearTimeout(timer);
1450
+ socket.off("error", onError);
1451
+ socket.off("secureConnect", onSecureConnect);
1452
+ resolve(payload);
1453
+ };
1454
+ const timer = setTimeout(() => {
1455
+ socket.destroy();
1456
+ this.eventBus?.emit("tls:probe_completed", {
1457
+ host,
1458
+ port,
1459
+ success: false,
1460
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1461
+ });
1462
+ finish({
1463
+ ok: false,
1464
+ error: "TLS open timed out",
1465
+ target,
1466
+ policy
1467
+ });
1468
+ }, timeoutMs);
1469
+ const onError = (error) => {
1470
+ this.eventBus?.emit("tls:probe_completed", {
1471
+ host,
1472
+ port,
1473
+ success: false,
1474
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1475
+ });
1476
+ finish({
1477
+ ok: false,
1478
+ error: error.message,
1479
+ errorCode: error.code ?? null,
1480
+ target,
1481
+ policy
1482
+ });
1483
+ };
1484
+ const onSecureConnect = () => {
1485
+ const handshakeMs = Date.now() - startedAt;
1486
+ const peerCertificate = socket.getPeerCertificate(true);
1487
+ const hasLeafCertificate = hasPeerCertificate(peerCertificate);
1488
+ const certificateChain = hasLeafCertificate ? buildPeerCertificateChain(peerCertificate) : [];
1489
+ const leafCertificate = certificateChain[0] ?? null;
1490
+ const hostnameError = skipHostnameCheck || !hasLeafCertificate ? void 0 : checkServerIdentity(target.validationTarget, peerCertificate);
1491
+ const hostnameValidation = {
1492
+ checked: !skipHostnameCheck,
1493
+ target: skipHostnameCheck ? null : target.validationTarget,
1494
+ matched: skipHostnameCheck ? null : hostnameError === void 0,
1495
+ error: !skipHostnameCheck && !hasLeafCertificate ? "Peer certificate was not presented by the server" : hostnameError?.message ?? null
1496
+ };
1497
+ const authorizationReasons = [
1498
+ socket.authorized ? "Certificate chain validated against the active trust store." : `Certificate chain validation failed: ${socket.authorizationError ?? "unknown_authority"}`,
1499
+ skipHostnameCheck ? "Hostname validation was skipped by request." : hostnameValidation.matched ? "Hostname validation passed." : `Hostname validation failed: ${hostnameValidation.error ?? "unknown_error"}`,
1500
+ !socket.authorized && allowInvalidCertificates ? "Policy allowed the session to continue despite certificate trust failure." : null
1501
+ ].filter((reason) => Boolean(reason));
1502
+ const cipher = socket.getCipher();
1503
+ const metadata = {
1504
+ target,
1505
+ policy,
1506
+ transport: {
1507
+ protocol: socket.getProtocol() ?? null,
1508
+ alpnProtocol: normalizeAlpnProtocol(socket.alpnProtocol),
1509
+ cipher: {
1510
+ name: cipher.name,
1511
+ standardName: cipher.standardName,
1512
+ version: cipher.version
1513
+ },
1514
+ localAddress: socket.localAddress ?? null,
1515
+ localPort: socket.localPort ?? null,
1516
+ remoteAddress: socket.remoteAddress ?? null,
1517
+ remotePort: socket.remotePort ?? null,
1518
+ servernameSent: normalizeSocketServername(socket.servername),
1519
+ sessionReused: socket.isSessionReused()
1520
+ },
1521
+ authorization: {
1522
+ socketAuthorized: socket.authorized,
1523
+ authorizationError: typeof socket.authorizationError === "string" ? socket.authorizationError : socket.authorizationError?.message ?? null,
1524
+ hostnameValidation,
1525
+ policyAllowed: (socket.authorized || allowInvalidCertificates) && (skipHostnameCheck || hostnameValidation.matched === true),
1526
+ reasons: authorizationReasons
1527
+ },
1528
+ certificates: {
1529
+ leaf: leafCertificate,
1530
+ chain: certificateChain
1531
+ }
1532
+ };
1533
+ if (!metadata.authorization.policyAllowed) {
1534
+ socket.destroy();
1535
+ this.eventBus?.emit("tls:probe_completed", {
1536
+ host,
1537
+ port,
1538
+ success: false,
1539
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1540
+ });
1541
+ finish({
1542
+ ok: false,
1543
+ error: "TLS session authorization failed",
1544
+ ...metadata,
1545
+ timing: { handshakeMs }
1546
+ });
1547
+ return;
1548
+ }
1549
+ const sessionId = makeSessionId("tls");
1550
+ const session = {
1551
+ id: sessionId,
1552
+ kind: "tls",
1553
+ socket,
1554
+ host,
1555
+ port,
1556
+ createdAt: Date.now(),
1557
+ buffer: Buffer.alloc(0),
1558
+ ended: false,
1559
+ closed: false,
1560
+ error: null,
1561
+ waiters: /* @__PURE__ */ new Set(),
1562
+ activeRead: false,
1563
+ metadata
1564
+ };
1565
+ attachBufferedSession(session);
1566
+ this.tlsSessions.set(sessionId, session);
1567
+ this.eventBus?.emit("tls:session_opened", {
1568
+ sessionId,
1569
+ host,
1570
+ port,
1571
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1572
+ });
1573
+ this.eventBus?.emit("tls:probe_completed", {
1574
+ host,
1575
+ port,
1576
+ success: true,
1577
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1578
+ });
1579
+ finish({
1580
+ ok: true,
1581
+ sessionId,
1582
+ kind: "tls",
1583
+ ...metadata,
1584
+ timing: { handshakeMs },
1585
+ state: serializeSessionState(session)
1586
+ });
1587
+ };
1588
+ socket.once("error", onError);
1589
+ socket.once("secureConnect", onSecureConnect);
1590
+ });
1591
+ }
1592
+ async handleTlsWrite(args) {
1593
+ const sessionId = argString(args, "sessionId")?.trim() ?? null;
1594
+ if (!sessionId) return {
1595
+ ok: false,
1596
+ error: "sessionId is required"
1597
+ };
1598
+ const session = this.getTlsSession(sessionId);
1599
+ if (!session) return {
1600
+ ok: false,
1601
+ error: `Unknown tls sessionId "${sessionId}"`
1602
+ };
1603
+ return this.writeBufferedSession(session, args);
1604
+ }
1605
+ async handleTlsReadUntil(args) {
1606
+ const sessionId = argString(args, "sessionId")?.trim() ?? null;
1607
+ if (!sessionId) return {
1608
+ ok: false,
1609
+ error: "sessionId is required"
1610
+ };
1611
+ const session = this.getTlsSession(sessionId);
1612
+ if (!session) return {
1613
+ ok: false,
1614
+ error: `Unknown tls sessionId "${sessionId}"`
1615
+ };
1616
+ return this.readBufferedSessionUntil(session, args);
1617
+ }
1618
+ async handleTlsClose(args) {
1619
+ const sessionId = argString(args, "sessionId")?.trim() ?? null;
1620
+ if (!sessionId) return {
1621
+ ok: false,
1622
+ error: "sessionId is required"
1623
+ };
1624
+ return this.closeBufferedSession(sessionId, this.tlsSessions, "tls", args);
1625
+ }
1626
+ };
1627
+ //#endregion
1628
+ //#region src/server/domains/boringssl-inspector/handlers/websocket-frame-handlers.ts
1629
+ /**
1630
+ * BoringsslInspectorWebSocketFrameHandlers — WebSocket session/frame operations.
1631
+ */
1632
+ var BoringsslInspectorWebSocketFrameHandlers = class extends BoringsslInspectorSessionHandlers {
1633
+ emitWebSocketEvent(event, payload) {
1634
+ this.eventBus?.emit(event, payload);
1635
+ }
1636
+ attachWebSocketSession(session) {
1637
+ const parseBufferedFrames = () => {
1638
+ while (session.parserBuffer.length > 0) {
1639
+ let consumed;
1640
+ try {
1641
+ consumed = tryConsumeWebSocketFrame(session.parserBuffer);
1642
+ } catch (error) {
1643
+ session.error = errorMessage(error);
1644
+ session.socket.destroy();
1645
+ break;
1646
+ }
1647
+ if (!consumed) break;
1648
+ session.parserBuffer = session.parserBuffer.subarray(consumed.bytesConsumed);
1649
+ const frame = consumed.frame;
1650
+ session.frames.push(frame);
1651
+ if (frame.type === "ping" && !session.closeSent && !session.socket.destroyed) {
1652
+ const pongFrame = encodeWebSocketFrame("pong", frame.data);
1653
+ session.socket.write(pongFrame);
1654
+ this.emitWebSocketEvent("websocket:session_written", {
1655
+ sessionId: session.id,
1656
+ frameType: "pong",
1657
+ byteLength: frame.data.length,
1658
+ automatic: true,
1659
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1660
+ });
1661
+ }
1662
+ if (frame.type === "close") {
1663
+ session.closeReceived = true;
1664
+ if (!session.closeSent && !session.socket.destroyed) {
1665
+ session.closeSent = true;
1666
+ session.socket.write(encodeWebSocketFrame("close", frame.data, frame.closeCode, frame.closeReason));
1667
+ this.emitWebSocketEvent("websocket:session_written", {
1668
+ sessionId: session.id,
1669
+ frameType: "close",
1670
+ byteLength: frame.data.length,
1671
+ automatic: true,
1672
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1673
+ });
1674
+ }
1675
+ }
1676
+ }
1677
+ wakeWebSocketWaiters(session);
1678
+ };
1679
+ session.socket.on("data", (chunk) => {
1680
+ session.parserBuffer = Buffer.concat([session.parserBuffer, chunk]);
1681
+ parseBufferedFrames();
1682
+ });
1683
+ session.socket.on("end", () => {
1684
+ session.ended = true;
1685
+ wakeWebSocketWaiters(session);
1686
+ });
1687
+ session.socket.on("close", () => {
1688
+ session.closed = true;
1689
+ wakeWebSocketWaiters(session);
1690
+ });
1691
+ session.socket.on("error", (error) => {
1692
+ session.error = error.message;
1693
+ wakeWebSocketWaiters(session);
1694
+ });
1695
+ parseBufferedFrames();
1696
+ }
1697
+ async readWebSocketFrame(session, args) {
1698
+ const timeoutMs = argNumber(args, "timeoutMs") ?? 5e3;
1699
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) return {
1700
+ ok: false,
1701
+ error: "timeoutMs must be a positive number"
1702
+ };
1703
+ if (session.activeRead) return {
1704
+ ok: false,
1705
+ error: `Session "${session.id}" already has a pending read`,
1706
+ sessionId: session.id,
1707
+ kind: session.kind,
1708
+ state: serializeWebSocketSessionState(session)
1709
+ };
1710
+ session.activeRead = true;
1711
+ const startedAt = Date.now();
1712
+ try {
1713
+ while (true) {
1714
+ const frame = session.frames.shift();
1715
+ if (frame) {
1716
+ this.emitWebSocketEvent("websocket:frame_read", {
1717
+ sessionId: session.id,
1718
+ frameType: frame.type,
1719
+ byteLength: frame.data.length,
1720
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1721
+ });
1722
+ return {
1723
+ ok: true,
1724
+ sessionId: session.id,
1725
+ kind: session.kind,
1726
+ scheme: session.scheme,
1727
+ frameType: frame.type,
1728
+ fin: frame.fin,
1729
+ opcode: frame.opcode,
1730
+ masked: frame.masked,
1731
+ byteLength: frame.data.length,
1732
+ dataHex: frame.data.toString("hex").toUpperCase(),
1733
+ dataText: frame.type === "binary" ? null : frame.data.toString("utf8"),
1734
+ closeCode: frame.closeCode,
1735
+ closeReason: frame.closeReason,
1736
+ elapsedMs: Date.now() - startedAt,
1737
+ state: serializeWebSocketSessionState(session)
1738
+ };
1739
+ }
1740
+ if (session.error) return {
1741
+ ok: false,
1742
+ error: session.error,
1743
+ sessionId: session.id,
1744
+ kind: session.kind,
1745
+ state: serializeWebSocketSessionState(session)
1746
+ };
1747
+ if (session.closed || session.ended) return {
1748
+ ok: false,
1749
+ error: "socket closed before a WebSocket frame was available",
1750
+ sessionId: session.id,
1751
+ kind: session.kind,
1752
+ state: serializeWebSocketSessionState(session)
1753
+ };
1754
+ const remainingMs = timeoutMs - (Date.now() - startedAt);
1755
+ if (remainingMs <= 0) return {
1756
+ ok: false,
1757
+ error: "read timed out",
1758
+ sessionId: session.id,
1759
+ kind: session.kind,
1760
+ state: serializeWebSocketSessionState(session)
1761
+ };
1762
+ if (!await waitForWebSocketActivity(session, remainingMs)) return {
1763
+ ok: false,
1764
+ error: "read timed out",
1765
+ sessionId: session.id,
1766
+ kind: session.kind,
1767
+ state: serializeWebSocketSessionState(session)
1768
+ };
1769
+ }
1770
+ } finally {
1771
+ session.activeRead = false;
1772
+ }
1773
+ }
1774
+ async sendWebSocketFrame(session, args) {
1775
+ if (session.closed || session.socket.destroyed) return {
1776
+ ok: false,
1777
+ error: `Session "${session.id}" is already closed`,
1778
+ sessionId: session.id,
1779
+ kind: session.kind,
1780
+ state: serializeWebSocketSessionState(session)
1781
+ };
1782
+ const frameType = argEnum(args, "frameType", new Set([
1783
+ "text",
1784
+ "binary",
1785
+ "ping",
1786
+ "pong",
1787
+ "close"
1788
+ ]));
1789
+ if (!frameType) return {
1790
+ ok: false,
1791
+ error: "frameType is required"
1792
+ };
1793
+ const dataHex = argString(args, "dataHex");
1794
+ const dataText = argString(args, "dataText");
1795
+ if (dataHex && dataText) return {
1796
+ ok: false,
1797
+ error: "dataHex and dataText are mutually exclusive"
1798
+ };
1799
+ const timeoutMs = argNumber(args, "timeoutMs") ?? 5e3;
1800
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) return {
1801
+ ok: false,
1802
+ error: "timeoutMs must be a positive number"
1803
+ };
1804
+ let payload = Buffer.alloc(0);
1805
+ if (dataHex) {
1806
+ const normalized = normalizeHex(dataHex);
1807
+ if (!isHex(normalized)) return {
1808
+ ok: false,
1809
+ error: "dataHex must be valid even-length hexadecimal data"
1810
+ };
1811
+ payload = Buffer.from(normalized, "hex");
1812
+ } else if (dataText !== void 0) payload = Buffer.from(dataText, "utf8");
1813
+ let closeCode = null;
1814
+ let closeReason = null;
1815
+ if (frameType === "close") {
1816
+ const rawCloseCode = argNumber(args, "closeCode");
1817
+ if (rawCloseCode !== void 0) {
1818
+ if (!Number.isInteger(rawCloseCode) || rawCloseCode < 1e3 || rawCloseCode > 4999) return {
1819
+ ok: false,
1820
+ error: "closeCode must be an integer between 1000 and 4999"
1821
+ };
1822
+ closeCode = rawCloseCode;
1823
+ }
1824
+ closeReason = argString(args, "closeReason") ?? null;
1825
+ if (dataHex || dataText) return {
1826
+ ok: false,
1827
+ error: "close frames use closeCode/closeReason instead of dataHex/dataText"
1828
+ };
1829
+ session.closeSent = true;
1830
+ }
1831
+ if (frameType === "text" && dataHex) return {
1832
+ ok: false,
1833
+ error: "text frames require UTF-8 dataText instead of dataHex"
1834
+ };
1835
+ const frameBuffer = encodeWebSocketFrame(frameType, payload, closeCode, closeReason);
1836
+ return new Promise((resolve) => {
1837
+ let settled = false;
1838
+ const finish = (result) => {
1839
+ if (settled) return;
1840
+ settled = true;
1841
+ clearTimeout(timer);
1842
+ session.socket.off("error", onError);
1843
+ resolve(result);
1844
+ };
1845
+ const timer = setTimeout(() => {
1846
+ finish({
1847
+ ok: false,
1848
+ error: "write timed out",
1849
+ sessionId: session.id,
1850
+ kind: session.kind,
1851
+ state: serializeWebSocketSessionState(session)
1852
+ });
1853
+ }, timeoutMs);
1854
+ const onError = (error) => {
1855
+ finish({
1856
+ ok: false,
1857
+ error: error.message,
1858
+ sessionId: session.id,
1859
+ kind: session.kind,
1860
+ state: serializeWebSocketSessionState(session)
1861
+ });
1862
+ };
1863
+ session.socket.once("error", onError);
1864
+ session.socket.write(frameBuffer, () => {
1865
+ this.emitWebSocketEvent("websocket:session_written", {
1866
+ sessionId: session.id,
1867
+ frameType,
1868
+ byteLength: frameType === "close" ? closeReason ? Buffer.byteLength(closeReason) + 2 : closeCode ? 2 : 0 : payload.length,
1869
+ automatic: false,
1870
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1871
+ });
1872
+ finish({
1873
+ ok: true,
1874
+ sessionId: session.id,
1875
+ kind: session.kind,
1876
+ scheme: session.scheme,
1877
+ frameType,
1878
+ bytesWritten: frameBuffer.length,
1879
+ payloadBytes: frameType === "close" ? closeReason ? Buffer.byteLength(closeReason) + 2 : closeCode ? 2 : 0 : payload.length,
1880
+ state: serializeWebSocketSessionState(session)
1881
+ });
1882
+ });
1883
+ });
1884
+ }
1885
+ async closeWebSocketSession(sessionId, args) {
1886
+ const session = this.websocketSessions.get(sessionId);
1887
+ if (!session) return {
1888
+ ok: false,
1889
+ error: `Unknown websocket sessionId "${sessionId}"`
1890
+ };
1891
+ const force = argBool(args, "force") ?? false;
1892
+ const timeoutMs = argNumber(args, "timeoutMs") ?? 1e3;
1893
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) return {
1894
+ ok: false,
1895
+ error: "timeoutMs must be a positive number"
1896
+ };
1897
+ const queuedFramesDiscarded = session.frames.length;
1898
+ if (session.closed || session.socket.destroyed) {
1899
+ this.websocketSessions.delete(sessionId);
1900
+ return {
1901
+ ok: true,
1902
+ sessionId,
1903
+ kind: session.kind,
1904
+ force,
1905
+ closed: true,
1906
+ queuedFramesDiscarded,
1907
+ state: serializeWebSocketSessionState(session)
1908
+ };
1909
+ }
1910
+ let closeCode = null;
1911
+ const rawCloseCode = argNumber(args, "closeCode");
1912
+ if (rawCloseCode !== void 0) {
1913
+ if (!Number.isInteger(rawCloseCode) || rawCloseCode < 1e3 || rawCloseCode > 4999) return {
1914
+ ok: false,
1915
+ error: "closeCode must be an integer between 1000 and 4999"
1916
+ };
1917
+ closeCode = rawCloseCode;
1918
+ }
1919
+ const closeReason = argString(args, "closeReason") ?? null;
1920
+ return new Promise((resolve) => {
1921
+ let settled = false;
1922
+ const finish = (closed) => {
1754
1923
  if (settled) return;
1755
1924
  settled = true;
1756
1925
  clearTimeout(timer);
1757
- socket.off("error", onError);
1758
- socket.off("secureConnect", onSecureConnect);
1759
- resolve(payload);
1760
- };
1761
- const timer = setTimeout(() => {
1762
- socket.destroy();
1763
- this.eventBus?.emit("tls:probe_completed", {
1764
- host,
1765
- port,
1766
- success: false,
1767
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1768
- });
1769
- finish({
1770
- ok: false,
1771
- error: "TLS open timed out",
1772
- target,
1773
- policy
1774
- });
1775
- }, timeoutMs);
1776
- const onError = (error) => {
1777
- this.eventBus?.emit("tls:probe_completed", {
1778
- host,
1779
- port,
1780
- success: false,
1781
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1782
- });
1783
- finish({
1784
- ok: false,
1785
- error: error.message,
1786
- errorCode: error.code ?? null,
1787
- target,
1788
- policy
1789
- });
1790
- };
1791
- const onSecureConnect = () => {
1792
- const handshakeMs = Date.now() - startedAt;
1793
- const peerCertificate = socket.getPeerCertificate(true);
1794
- const hasLeafCertificate = hasPeerCertificate(peerCertificate);
1795
- const certificateChain = hasLeafCertificate ? buildPeerCertificateChain(peerCertificate) : [];
1796
- const leafCertificate = certificateChain[0] ?? null;
1797
- const hostnameError = skipHostnameCheck || !hasLeafCertificate ? void 0 : checkServerIdentity(target.validationTarget, peerCertificate);
1798
- const hostnameValidation = {
1799
- checked: !skipHostnameCheck,
1800
- target: skipHostnameCheck ? null : target.validationTarget,
1801
- matched: skipHostnameCheck ? null : hostnameError === void 0,
1802
- error: !skipHostnameCheck && !hasLeafCertificate ? "Peer certificate was not presented by the server" : hostnameError?.message ?? null
1803
- };
1804
- const authorizationReasons = [
1805
- socket.authorized ? "Certificate chain validated against the active trust store." : `Certificate chain validation failed: ${socket.authorizationError ?? "unknown_authority"}`,
1806
- skipHostnameCheck ? "Hostname validation was skipped by request." : hostnameValidation.matched ? "Hostname validation passed." : `Hostname validation failed: ${hostnameValidation.error ?? "unknown_error"}`,
1807
- !socket.authorized && allowInvalidCertificates ? "Policy allowed the session to continue despite certificate trust failure." : null
1808
- ].filter((reason) => Boolean(reason));
1809
- const cipher = socket.getCipher();
1810
- const metadata = {
1811
- target,
1812
- policy,
1813
- transport: {
1814
- protocol: socket.getProtocol() ?? null,
1815
- alpnProtocol: normalizeAlpnProtocol(socket.alpnProtocol),
1816
- cipher: {
1817
- name: cipher.name,
1818
- standardName: cipher.standardName,
1819
- version: cipher.version
1820
- },
1821
- localAddress: socket.localAddress ?? null,
1822
- localPort: socket.localPort ?? null,
1823
- remoteAddress: socket.remoteAddress ?? null,
1824
- remotePort: socket.remotePort ?? null,
1825
- servernameSent: normalizeSocketServername(socket.servername),
1826
- sessionReused: socket.isSessionReused()
1827
- },
1828
- authorization: {
1829
- socketAuthorized: socket.authorized,
1830
- authorizationError: typeof socket.authorizationError === "string" ? socket.authorizationError : socket.authorizationError?.message ?? null,
1831
- hostnameValidation,
1832
- policyAllowed: (socket.authorized || allowInvalidCertificates) && (skipHostnameCheck || hostnameValidation.matched === true),
1833
- reasons: authorizationReasons
1834
- },
1835
- certificates: {
1836
- leaf: leafCertificate,
1837
- chain: certificateChain
1838
- }
1839
- };
1840
- if (!metadata.authorization.policyAllowed) {
1841
- socket.destroy();
1842
- this.eventBus?.emit("tls:probe_completed", {
1843
- host,
1844
- port,
1845
- success: false,
1846
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1847
- });
1848
- finish({
1849
- ok: false,
1850
- error: "TLS session authorization failed",
1851
- ...metadata,
1852
- timing: { handshakeMs }
1853
- });
1854
- return;
1855
- }
1856
- const sessionId = makeSessionId("tls");
1857
- const session = {
1858
- id: sessionId,
1859
- kind: "tls",
1860
- socket,
1861
- host,
1862
- port,
1863
- createdAt: Date.now(),
1864
- buffer: Buffer.alloc(0),
1865
- ended: false,
1866
- closed: false,
1867
- error: null,
1868
- waiters: /* @__PURE__ */ new Set(),
1869
- activeRead: false,
1870
- metadata
1871
- };
1872
- attachBufferedSession(session);
1873
- this.tlsSessions.set(sessionId, session);
1874
- this.eventBus?.emit("tls:session_opened", {
1926
+ session.socket.off("close", onClose);
1927
+ session.socket.off("error", onError);
1928
+ this.websocketSessions.delete(sessionId);
1929
+ this.emitWebSocketEvent("websocket:session_closed", {
1875
1930
  sessionId,
1876
- host,
1877
- port,
1878
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1879
- });
1880
- this.eventBus?.emit("tls:probe_completed", {
1881
- host,
1882
- port,
1883
- success: true,
1931
+ reason: session.error,
1884
1932
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
1885
1933
  });
1886
- finish({
1934
+ resolve({
1887
1935
  ok: true,
1888
1936
  sessionId,
1889
- kind: "tls",
1890
- ...metadata,
1891
- timing: { handshakeMs },
1892
- state: serializeSessionState(session)
1937
+ kind: session.kind,
1938
+ force,
1939
+ closed,
1940
+ queuedFramesDiscarded,
1941
+ state: serializeWebSocketSessionState(session)
1893
1942
  });
1894
1943
  };
1895
- socket.once("error", onError);
1896
- socket.once("secureConnect", onSecureConnect);
1944
+ const onClose = () => finish(true);
1945
+ const onError = () => finish(session.socket.destroyed || session.closed);
1946
+ const timer = setTimeout(() => {
1947
+ session.socket.destroy();
1948
+ finish(session.socket.destroyed || session.closed);
1949
+ }, timeoutMs);
1950
+ session.socket.once("close", onClose);
1951
+ session.socket.once("error", onError);
1952
+ if (force) {
1953
+ session.socket.destroy();
1954
+ return;
1955
+ }
1956
+ if (!session.closeSent) {
1957
+ session.closeSent = true;
1958
+ session.socket.write(encodeWebSocketFrame("close", Buffer.alloc(0), closeCode, closeReason));
1959
+ this.emitWebSocketEvent("websocket:session_written", {
1960
+ sessionId,
1961
+ frameType: "close",
1962
+ byteLength: closeReason ? Buffer.byteLength(closeReason) + 2 : closeCode ? 2 : 0,
1963
+ automatic: false,
1964
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1965
+ });
1966
+ }
1897
1967
  });
1898
1968
  }
1899
- async handleTlsWrite(args) {
1969
+ async handleWebSocketSendFrame(args) {
1900
1970
  const sessionId = argString(args, "sessionId")?.trim() ?? null;
1901
1971
  if (!sessionId) return {
1902
1972
  ok: false,
1903
1973
  error: "sessionId is required"
1904
1974
  };
1905
- const session = this.getTlsSession(sessionId);
1975
+ const session = this.getWebSocketSession(sessionId);
1906
1976
  if (!session) return {
1907
1977
  ok: false,
1908
- error: `Unknown tls sessionId "${sessionId}"`
1978
+ error: `Unknown websocket sessionId "${sessionId}"`
1909
1979
  };
1910
- return this.writeBufferedSession(session, args);
1980
+ return this.sendWebSocketFrame(session, args);
1911
1981
  }
1912
- async handleTlsReadUntil(args) {
1982
+ async handleWebSocketReadFrame(args) {
1913
1983
  const sessionId = argString(args, "sessionId")?.trim() ?? null;
1914
1984
  if (!sessionId) return {
1915
1985
  ok: false,
1916
1986
  error: "sessionId is required"
1917
1987
  };
1918
- const session = this.getTlsSession(sessionId);
1988
+ const session = this.getWebSocketSession(sessionId);
1919
1989
  if (!session) return {
1920
1990
  ok: false,
1921
- error: `Unknown tls sessionId "${sessionId}"`
1991
+ error: `Unknown websocket sessionId "${sessionId}"`
1922
1992
  };
1923
- return this.readBufferedSessionUntil(session, args);
1993
+ return this.readWebSocketFrame(session, args);
1924
1994
  }
1925
- async handleTlsClose(args) {
1995
+ async handleWebSocketClose(args) {
1926
1996
  const sessionId = argString(args, "sessionId")?.trim() ?? null;
1927
1997
  if (!sessionId) return {
1928
1998
  ok: false,
1929
1999
  error: "sessionId is required"
1930
2000
  };
1931
- return this.closeBufferedSession(sessionId, this.tlsSessions, "tls", args);
2001
+ return this.closeWebSocketSession(sessionId, args);
1932
2002
  }
2003
+ };
2004
+ //#endregion
2005
+ //#region src/server/domains/boringssl-inspector/handlers/websocket-handlers.ts
2006
+ /**
2007
+ * BoringsslInspectorWebSocketHandlers — WebSocket upgrade/open handlers and bypass helpers.
2008
+ */
2009
+ const TLS_VERSION_ORDER = [
2010
+ "TLSv1",
2011
+ "TLSv1.1",
2012
+ "TLSv1.2",
2013
+ "TLSv1.3"
2014
+ ];
2015
+ var BoringsslInspectorWebSocketHandlers = class extends BoringsslInspectorWebSocketFrameHandlers {
1933
2016
  async handleWebSocketOpen(args) {
1934
2017
  const rawUrl = argString(args, "url")?.trim() ?? null;
1935
2018
  const rawHost = argString(args, "host")?.trim() ?? null;
@@ -2007,13 +2090,7 @@ var BoringsslInspectorHandlers = class {
2007
2090
  error: errorMessage(error)
2008
2091
  };
2009
2092
  }
2010
- const versionOrder = [
2011
- "TLSv1",
2012
- "TLSv1.1",
2013
- "TLSv1.2",
2014
- "TLSv1.3"
2015
- ];
2016
- if (minVersion && maxVersion && versionOrder.indexOf(minVersion) > versionOrder.indexOf(maxVersion)) return {
2093
+ if (minVersion && maxVersion && TLS_VERSION_ORDER.indexOf(minVersion) > TLS_VERSION_ORDER.indexOf(maxVersion)) return {
2017
2094
  ok: false,
2018
2095
  error: "minVersion must not be greater than maxVersion"
2019
2096
  };
@@ -2110,14 +2187,6 @@ var BoringsslInspectorHandlers = class {
2110
2187
  sendHandshake();
2111
2188
  };
2112
2189
  const onSecureConnect = () => {
2113
- if (!(socket instanceof Object) || !("getPeerCertificate" in socket)) {
2114
- finish({
2115
- ok: false,
2116
- error: "Expected a TLS socket for wss session",
2117
- target
2118
- });
2119
- return;
2120
- }
2121
2190
  const tlsSocket = socket;
2122
2191
  const peerCertificate = tlsSocket.getPeerCertificate(true);
2123
2192
  const hasLeafCertificate = hasPeerCertificate(peerCertificate);
@@ -2300,40 +2369,6 @@ var BoringsslInspectorHandlers = class {
2300
2369
  }
2301
2370
  });
2302
2371
  }
2303
- async handleWebSocketSendFrame(args) {
2304
- const sessionId = argString(args, "sessionId")?.trim() ?? null;
2305
- if (!sessionId) return {
2306
- ok: false,
2307
- error: "sessionId is required"
2308
- };
2309
- const session = this.getWebSocketSession(sessionId);
2310
- if (!session) return {
2311
- ok: false,
2312
- error: `Unknown websocket sessionId "${sessionId}"`
2313
- };
2314
- return this.sendWebSocketFrame(session, args);
2315
- }
2316
- async handleWebSocketReadFrame(args) {
2317
- const sessionId = argString(args, "sessionId")?.trim() ?? null;
2318
- if (!sessionId) return {
2319
- ok: false,
2320
- error: "sessionId is required"
2321
- };
2322
- const session = this.getWebSocketSession(sessionId);
2323
- if (!session) return {
2324
- ok: false,
2325
- error: `Unknown websocket sessionId "${sessionId}"`
2326
- };
2327
- return this.readWebSocketFrame(session, args);
2328
- }
2329
- async handleWebSocketClose(args) {
2330
- const sessionId = argString(args, "sessionId")?.trim() ?? null;
2331
- if (!sessionId) return {
2332
- ok: false,
2333
- error: "sessionId is required"
2334
- };
2335
- return this.closeWebSocketSession(sessionId, args);
2336
- }
2337
2372
  async handleBypassCertPinning(args) {
2338
2373
  if (this.extensionInvoke) try {
2339
2374
  const result = await this.extensionInvoke(args);
@@ -2354,6 +2389,13 @@ var BoringsslInspectorHandlers = class {
2354
2389
  args
2355
2390
  });
2356
2391
  }
2392
+ };
2393
+ //#endregion
2394
+ //#region src/server/domains/boringssl-inspector/handlers/raw-socket-handlers.ts
2395
+ /**
2396
+ * BoringsslInspectorRawSocketHandlers — stateless raw TCP/UDP helpers.
2397
+ */
2398
+ var BoringsslInspectorRawSocketHandlers = class extends BoringsslInspectorWebSocketHandlers {
2357
2399
  async handleRawTcpSend(args) {
2358
2400
  const host = argString(args, "host") ?? "127.0.0.1";
2359
2401
  const port = argNumber(args, "port");
@@ -2420,7 +2462,7 @@ var BoringsslInspectorHandlers = class {
2420
2462
  server.close();
2421
2463
  resolve({
2422
2464
  ok: false,
2423
- error: "Listen timed out no connection received"
2465
+ error: "Listen timed out - no connection received"
2424
2466
  });
2425
2467
  }, timeout);
2426
2468
  server.on("connection", (socket) => {
@@ -2549,4 +2591,10 @@ var BoringsslInspectorHandlers = class {
2549
2591
  }
2550
2592
  };
2551
2593
  //#endregion
2594
+ //#region src/server/domains/boringssl-inspector/handlers/handler-class.ts
2595
+ /**
2596
+ * BoringsslInspectorHandlers — thin facade over the split handler chain.
2597
+ */
2598
+ var BoringsslInspectorHandlers = class extends BoringsslInspectorRawSocketHandlers {};
2599
+ //#endregion
2552
2600
  export { BoringsslInspectorHandlers };