api-ape 3.0.2 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/README.md +59 -572
  2. package/client/README.md +73 -14
  3. package/client/auth/crypto/aead.js +214 -0
  4. package/client/auth/crypto/constants.js +32 -0
  5. package/client/auth/crypto/encoding.js +104 -0
  6. package/client/auth/crypto/files.md +27 -0
  7. package/client/auth/crypto/kdf.js +217 -0
  8. package/client/auth/crypto-utils.js +118 -0
  9. package/client/auth/files.md +52 -0
  10. package/client/auth/key-recovery.js +288 -0
  11. package/client/auth/recovery/constants.js +37 -0
  12. package/client/auth/recovery/files.md +23 -0
  13. package/client/auth/recovery/key-derivation.js +61 -0
  14. package/client/auth/recovery/sss-browser.js +189 -0
  15. package/client/auth/share-storage.js +205 -0
  16. package/client/auth/storage/constants.js +18 -0
  17. package/client/auth/storage/db.js +132 -0
  18. package/client/auth/storage/files.md +27 -0
  19. package/client/auth/storage/keys.js +173 -0
  20. package/client/auth/storage/shares.js +200 -0
  21. package/client/browser.js +190 -23
  22. package/client/connectSocket.js +418 -988
  23. package/client/connection/README.md +23 -0
  24. package/client/connection/fileDownload.js +256 -0
  25. package/client/connection/fileHandling.js +450 -0
  26. package/client/connection/fileUtils.js +346 -0
  27. package/client/connection/files.md +71 -0
  28. package/client/connection/messageHandler.js +105 -0
  29. package/client/connection/network.js +350 -0
  30. package/client/connection/proxy.js +233 -0
  31. package/client/connection/sender.js +333 -0
  32. package/client/connection/state.js +321 -0
  33. package/client/connection/subscriptions.js +151 -0
  34. package/client/files.md +53 -0
  35. package/client/index.js +298 -142
  36. package/client/transports/README.md +50 -0
  37. package/client/transports/files.md +41 -0
  38. package/client/transports/streamParser.js +195 -0
  39. package/client/transports/streaming.js +555 -203
  40. package/dist/ape.js +6 -1
  41. package/dist/ape.js.map +4 -4
  42. package/index.d.ts +38 -16
  43. package/package.json +31 -6
  44. package/server/README.md +272 -67
  45. package/server/adapters/README.md +23 -14
  46. package/server/adapters/files.md +68 -0
  47. package/server/adapters/firebase.js +543 -160
  48. package/server/adapters/index.js +362 -112
  49. package/server/adapters/mongo.js +530 -140
  50. package/server/adapters/postgres.js +534 -155
  51. package/server/adapters/redis.js +508 -143
  52. package/server/adapters/supabase.js +555 -186
  53. package/server/client/README.md +43 -0
  54. package/server/client/connection.js +586 -0
  55. package/server/client/files.md +40 -0
  56. package/server/client/index.js +342 -0
  57. package/server/files.md +54 -0
  58. package/server/index.js +322 -71
  59. package/server/lib/README.md +26 -0
  60. package/server/lib/broadcast/clients.js +219 -0
  61. package/server/lib/broadcast/files.md +58 -0
  62. package/server/lib/broadcast/index.js +57 -0
  63. package/server/lib/broadcast/publishProxy.js +110 -0
  64. package/server/lib/broadcast/pubsub.js +137 -0
  65. package/server/lib/broadcast/sendProxy.js +103 -0
  66. package/server/lib/bun.js +315 -99
  67. package/server/lib/fileTransfer/README.md +63 -0
  68. package/server/lib/fileTransfer/files.md +30 -0
  69. package/server/lib/fileTransfer/streaming.js +435 -0
  70. package/server/lib/fileTransfer.js +710 -326
  71. package/server/lib/files.md +111 -0
  72. package/server/lib/httpUtils.js +283 -0
  73. package/server/lib/loader.js +208 -7
  74. package/server/lib/longPolling/README.md +63 -0
  75. package/server/lib/longPolling/files.md +44 -0
  76. package/server/lib/longPolling/getHandler.js +365 -0
  77. package/server/lib/longPolling/postHandler.js +327 -0
  78. package/server/lib/longPolling.js +174 -219
  79. package/server/lib/main.js +369 -532
  80. package/server/lib/runtimes/README.md +42 -0
  81. package/server/lib/runtimes/bun.js +586 -0
  82. package/server/lib/runtimes/files.md +56 -0
  83. package/server/lib/runtimes/node.js +511 -0
  84. package/server/lib/wiring.js +539 -98
  85. package/server/lib/ws/README.md +35 -0
  86. package/server/lib/ws/adapters/README.md +54 -0
  87. package/server/lib/ws/adapters/bun.js +538 -170
  88. package/server/lib/ws/adapters/deno.js +623 -149
  89. package/server/lib/ws/adapters/files.md +42 -0
  90. package/server/lib/ws/files.md +74 -0
  91. package/server/lib/ws/frames.js +532 -154
  92. package/server/lib/ws/index.js +207 -10
  93. package/server/lib/ws/server.js +385 -92
  94. package/server/lib/ws/socket.js +549 -181
  95. package/server/lib/wsProvider.js +363 -89
  96. package/server/plugins/binary.js +282 -0
  97. package/server/security/README.md +92 -0
  98. package/server/security/auth/README.md +319 -0
  99. package/server/security/auth/adapters/files.md +95 -0
  100. package/server/security/auth/adapters/ldap/constants.js +37 -0
  101. package/server/security/auth/adapters/ldap/files.md +19 -0
  102. package/server/security/auth/adapters/ldap/helpers.js +111 -0
  103. package/server/security/auth/adapters/ldap.js +353 -0
  104. package/server/security/auth/adapters/oauth2/constants.js +41 -0
  105. package/server/security/auth/adapters/oauth2/files.md +19 -0
  106. package/server/security/auth/adapters/oauth2/helpers.js +123 -0
  107. package/server/security/auth/adapters/oauth2.js +273 -0
  108. package/server/security/auth/adapters/opaque-handlers.js +314 -0
  109. package/server/security/auth/adapters/opaque.js +205 -0
  110. package/server/security/auth/adapters/saml/constants.js +52 -0
  111. package/server/security/auth/adapters/saml/files.md +19 -0
  112. package/server/security/auth/adapters/saml/helpers.js +74 -0
  113. package/server/security/auth/adapters/saml.js +173 -0
  114. package/server/security/auth/adapters/totp.js +703 -0
  115. package/server/security/auth/adapters/webauthn.js +625 -0
  116. package/server/security/auth/files.md +61 -0
  117. package/server/security/auth/framework/constants.js +27 -0
  118. package/server/security/auth/framework/files.md +23 -0
  119. package/server/security/auth/framework/handlers.js +272 -0
  120. package/server/security/auth/framework/socket-auth.js +177 -0
  121. package/server/security/auth/handlers/auth-messages.js +143 -0
  122. package/server/security/auth/handlers/files.md +28 -0
  123. package/server/security/auth/index.js +290 -0
  124. package/server/security/auth/mfa/crypto/aead.js +148 -0
  125. package/server/security/auth/mfa/crypto/constants.js +35 -0
  126. package/server/security/auth/mfa/crypto/files.md +27 -0
  127. package/server/security/auth/mfa/crypto/kdf.js +120 -0
  128. package/server/security/auth/mfa/crypto/utils.js +68 -0
  129. package/server/security/auth/mfa/crypto-utils.js +80 -0
  130. package/server/security/auth/mfa/files.md +77 -0
  131. package/server/security/auth/mfa/ledger/constants.js +75 -0
  132. package/server/security/auth/mfa/ledger/errors.js +73 -0
  133. package/server/security/auth/mfa/ledger/files.md +23 -0
  134. package/server/security/auth/mfa/ledger/share-record.js +32 -0
  135. package/server/security/auth/mfa/ledger.js +255 -0
  136. package/server/security/auth/mfa/recovery/constants.js +67 -0
  137. package/server/security/auth/mfa/recovery/files.md +19 -0
  138. package/server/security/auth/mfa/recovery/handlers.js +216 -0
  139. package/server/security/auth/mfa/recovery.js +191 -0
  140. package/server/security/auth/mfa/sss/constants.js +21 -0
  141. package/server/security/auth/mfa/sss/files.md +23 -0
  142. package/server/security/auth/mfa/sss/gf256.js +103 -0
  143. package/server/security/auth/mfa/sss/serialization.js +82 -0
  144. package/server/security/auth/mfa/sss.js +161 -0
  145. package/server/security/auth/mfa/two-of-three/constants.js +58 -0
  146. package/server/security/auth/mfa/two-of-three/files.md +23 -0
  147. package/server/security/auth/mfa/two-of-three/handlers.js +241 -0
  148. package/server/security/auth/mfa/two-of-three/helpers.js +71 -0
  149. package/server/security/auth/mfa/two-of-three.js +136 -0
  150. package/server/security/auth/nonce-manager.js +89 -0
  151. package/server/security/auth/state-machine-mfa.js +269 -0
  152. package/server/security/auth/state-machine.js +257 -0
  153. package/server/security/extractRootDomain.js +144 -16
  154. package/server/security/files.md +51 -0
  155. package/server/security/origin.js +197 -15
  156. package/server/security/reply.js +274 -16
  157. package/server/socket/README.md +119 -0
  158. package/server/socket/authMiddleware.js +299 -0
  159. package/server/socket/files.md +86 -0
  160. package/server/socket/open.js +154 -8
  161. package/server/socket/pluginHooks.js +334 -0
  162. package/server/socket/receive.js +184 -224
  163. package/server/socket/receiveContext.js +117 -0
  164. package/server/socket/send.js +416 -78
  165. package/server/socket/tagUtils.js +402 -0
  166. package/server/utils/README.md +19 -0
  167. package/server/utils/deepRequire.js +255 -30
  168. package/server/utils/files.md +57 -0
  169. package/server/utils/genId.js +182 -20
  170. package/server/utils/parseUserAgent.js +313 -251
  171. package/server/utils/userAgent/README.md +65 -0
  172. package/server/utils/userAgent/files.md +46 -0
  173. package/server/utils/userAgent/patterns.js +545 -0
  174. package/utils/README.md +21 -0
  175. package/utils/files.md +66 -0
  176. package/utils/jss/README.md +21 -0
  177. package/utils/jss/decode.js +471 -0
  178. package/utils/jss/encode.js +312 -0
  179. package/utils/jss/files.md +68 -0
  180. package/utils/jss/plugins.js +210 -0
  181. package/utils/jss.js +219 -273
  182. package/utils/messageHash.js +238 -35
  183. package/dist/api-ape.min.js +0 -2
  184. package/dist/api-ape.min.js.map +0 -7
  185. package/server/client.js +0 -311
  186. package/server/lib/broadcast.js +0 -146
@@ -0,0 +1,299 @@
1
+ /**
2
+ * @fileoverview Authorization Middleware for api-ape Server
3
+ *
4
+ * Provides authorization checks for incoming messages based on
5
+ * authentication tier and permissions.
6
+ *
7
+ * ## Usage
8
+ *
9
+ * The middleware can be configured with endpoint-specific requirements:
10
+ *
11
+ * ```javascript
12
+ * const authz = createAuthMiddleware({
13
+ * requirements: {
14
+ * 'admin/users': { tier: 2, permissions: ['admin:users'] },
15
+ * 'chat/send': { tier: 1, permissions: ['chat:send'] },
16
+ * 'public/status': { tier: 0 } // guest allowed
17
+ * },
18
+ * defaultTier: 0 // default for unlisted endpoints
19
+ * });
20
+ * ```
21
+ *
22
+ * @module server/socket/authMiddleware
23
+ * @see {@link module:server/security/auth} for authentication framework
24
+ */
25
+
26
+ const { AuthTier } = require("../security/auth");
27
+
28
+ /**
29
+ * Authorization error response
30
+ * @typedef {Object} AuthzError
31
+ * @property {string} type - Always "authz_fail"
32
+ * @property {string} reason - Error reason code
33
+ * @property {string} [required] - What was required
34
+ * @property {number} [requiredTier] - Required tier
35
+ * @property {number} [currentTier] - Current tier
36
+ */
37
+
38
+ /**
39
+ * Endpoint authorization requirement
40
+ * @typedef {Object} EndpointRequirement
41
+ * @property {number} [tier=0] - Minimum required tier
42
+ * @property {string[]} [permissions=[]] - Required permissions (any)
43
+ * @property {string[]} [roles=[]] - Required roles (any)
44
+ * @property {boolean} [requireAll=false] - Require all permissions/roles (not just any)
45
+ */
46
+
47
+ /**
48
+ * Authorization middleware configuration
49
+ * @typedef {Object} AuthMiddlewareConfig
50
+ * @property {Object<string, EndpointRequirement>} [requirements={}] - Per-endpoint requirements
51
+ * @property {number} [defaultTier=0] - Default tier for unlisted endpoints
52
+ * @property {boolean} [requireAuthByDefault=false] - Require auth for unlisted endpoints
53
+ * @property {Function} [onAuthzFail] - Callback on authorization failure
54
+ */
55
+
56
+ /**
57
+ * Create authorization middleware
58
+ *
59
+ * @param {AuthMiddlewareConfig} [config={}] - Configuration options
60
+ * @returns {Object} Authorization middleware
61
+ *
62
+ * @example
63
+ * const authz = createAuthMiddleware({
64
+ * requirements: {
65
+ * 'admin/*': { tier: 2 },
66
+ * 'user/profile': { tier: 1 },
67
+ * 'public/*': { tier: 0 }
68
+ * },
69
+ * defaultTier: 1
70
+ * });
71
+ *
72
+ * // Check authorization:
73
+ * const result = authz.check(socketAuth, 'admin/users');
74
+ * if (!result.allowed) {
75
+ * send(queryId, 'authz_fail', result, null);
76
+ * return;
77
+ * }
78
+ */
79
+ function createAuthMiddleware(config = {}) {
80
+ const {
81
+ requirements = {},
82
+ defaultTier = 0,
83
+ requireAuthByDefault = false,
84
+ onAuthzFail = () => {},
85
+ } = config;
86
+
87
+ /**
88
+ * Find requirement for an endpoint, supporting wildcards
89
+ *
90
+ * @param {string} endpoint - Endpoint path
91
+ * @returns {EndpointRequirement|null} Requirement or null
92
+ */
93
+ function findRequirement(endpoint) {
94
+ if (requirements[endpoint]) {
95
+ return requirements[endpoint];
96
+ }
97
+
98
+ const parts = endpoint.split("/");
99
+ for (let i = parts.length - 1; i >= 0; i--) {
100
+ const wildcardPath = parts.slice(0, i).join("/") + "/*";
101
+ if (requirements[wildcardPath]) {
102
+ return requirements[wildcardPath];
103
+ }
104
+ }
105
+
106
+ if (requirements["*"]) {
107
+ return requirements["*"];
108
+ }
109
+
110
+ return null;
111
+ }
112
+
113
+ /**
114
+ * Check if principal has required permission
115
+ *
116
+ * @param {Object} principal - Authenticated principal
117
+ * @param {string} permission - Required permission
118
+ * @returns {boolean} Whether principal has permission
119
+ */
120
+ function hasPermission(principal, permission) {
121
+ if (!principal || !principal.permissions) return false;
122
+
123
+ if (principal.permissions[permission] === true) {
124
+ return true;
125
+ }
126
+
127
+ const parts = permission.split(":");
128
+ for (let i = parts.length - 1; i > 0; i--) {
129
+ const wildcardPerm = parts.slice(0, i).join(":") + ":*";
130
+ if (principal.permissions[wildcardPerm] === true) {
131
+ return true;
132
+ }
133
+ }
134
+
135
+ if (principal.permissions["*"] === true) {
136
+ return true;
137
+ }
138
+
139
+ return false;
140
+ }
141
+
142
+ /**
143
+ * Check if principal has required role
144
+ *
145
+ * @param {Object} principal - Authenticated principal
146
+ * @param {string} role - Required role
147
+ * @returns {boolean} Whether principal has role
148
+ */
149
+ function hasRole(principal, role) {
150
+ if (!principal || !principal.roles) return false;
151
+ return principal.roles.includes(role) || principal.roles.includes("*");
152
+ }
153
+
154
+ /**
155
+ * Check authorization for an endpoint
156
+ *
157
+ * @param {Object} socketAuth - Socket auth manager
158
+ * @param {string} endpoint - Endpoint being accessed
159
+ * @param {Object} [context={}] - Additional context
160
+ * @returns {Object} Authorization result { allowed, reason, ... }
161
+ */
162
+ function check(socketAuth, endpoint, context = {}) {
163
+ const state = socketAuth.getState();
164
+ const requirement = findRequirement(endpoint);
165
+
166
+ let requiredTier = defaultTier;
167
+ let requiredPermissions = [];
168
+ let requiredRoles = [];
169
+ let requireAll = false;
170
+
171
+ if (requirement) {
172
+ requiredTier = requirement.tier ?? defaultTier;
173
+ requiredPermissions = requirement.permissions || [];
174
+ requiredRoles = requirement.roles || [];
175
+ requireAll = requirement.requireAll || false;
176
+ } else if (requireAuthByDefault) {
177
+ requiredTier = AuthTier.BASIC;
178
+ }
179
+
180
+ if (state.tier < requiredTier) {
181
+ const result = {
182
+ allowed: false,
183
+ reason: "INSUFFICIENT_TIER",
184
+ requiredTier,
185
+ currentTier: state.tier,
186
+ endpoint,
187
+ };
188
+ onAuthzFail(endpoint, result, context);
189
+ return result;
190
+ }
191
+
192
+ if (requiredPermissions.length > 0) {
193
+ const hasRequired = requireAll
194
+ ? requiredPermissions.every((p) => hasPermission(state.principal, p))
195
+ : requiredPermissions.some((p) => hasPermission(state.principal, p));
196
+
197
+ if (!hasRequired) {
198
+ const result = {
199
+ allowed: false,
200
+ reason: "MISSING_PERMISSION",
201
+ required: requiredPermissions,
202
+ endpoint,
203
+ };
204
+ onAuthzFail(endpoint, result, context);
205
+ return result;
206
+ }
207
+ }
208
+
209
+ if (requiredRoles.length > 0) {
210
+ const hasRequired = requireAll
211
+ ? requiredRoles.every((r) => hasRole(state.principal, r))
212
+ : requiredRoles.some((r) => hasRole(state.principal, r));
213
+
214
+ if (!hasRequired) {
215
+ const result = {
216
+ allowed: false,
217
+ reason: "MISSING_ROLE",
218
+ required: requiredRoles,
219
+ endpoint,
220
+ };
221
+ onAuthzFail(endpoint, result, context);
222
+ return result;
223
+ }
224
+ }
225
+
226
+ return {
227
+ allowed: true,
228
+ tier: state.tier,
229
+ principal: state.principal,
230
+ endpoint,
231
+ };
232
+ }
233
+
234
+ /**
235
+ * Create an authz_fail response
236
+ *
237
+ * @param {Object} checkResult - Result from check()
238
+ * @returns {Object} Response suitable for sending to client
239
+ */
240
+ function createFailResponse(checkResult) {
241
+ return {
242
+ type: "authz_fail",
243
+ reason: checkResult.reason,
244
+ required: checkResult.required || checkResult.requiredTier,
245
+ currentTier: checkResult.currentTier,
246
+ };
247
+ }
248
+
249
+ /**
250
+ * Set requirement for an endpoint
251
+ *
252
+ * @param {string} endpoint - Endpoint path
253
+ * @param {EndpointRequirement} requirement - Requirement config
254
+ */
255
+ function setRequirement(endpoint, requirement) {
256
+ requirements[endpoint] = requirement;
257
+ }
258
+
259
+ /**
260
+ * Remove requirement for an endpoint
261
+ *
262
+ * @param {string} endpoint - Endpoint path
263
+ */
264
+ function removeRequirement(endpoint) {
265
+ delete requirements[endpoint];
266
+ }
267
+
268
+ /**
269
+ * Get all configured requirements
270
+ * @returns {Object<string, EndpointRequirement>} Requirements map
271
+ */
272
+ function getRequirements() {
273
+ return { ...requirements };
274
+ }
275
+
276
+ return {
277
+ check,
278
+ createFailResponse,
279
+ setRequirement,
280
+ removeRequirement,
281
+ getRequirements,
282
+ findRequirement,
283
+ hasPermission,
284
+ hasRole,
285
+ };
286
+ }
287
+
288
+ /**
289
+ * Default authorization middleware instance
290
+ *
291
+ * Created with default settings (tier 0 required for all endpoints).
292
+ * Can be replaced or configured in the main server setup.
293
+ */
294
+ const defaultAuthMiddleware = createAuthMiddleware();
295
+
296
+ module.exports = {
297
+ createAuthMiddleware,
298
+ defaultAuthMiddleware,
299
+ };
@@ -0,0 +1,86 @@
1
+ # Socket Module Files
2
+
3
+ This module handles WebSocket message processing for api-ape servers. It manages the complete lifecycle of messages flowing between clients and controllers—from initial connection validation through message parsing, controller invocation, and response serialization.
4
+
5
+ ## Guidelines
6
+
7
+ - **JSS encoding** — Always use `utils/jss` for message parsing/serialization; never raw `JSON.parse/stringify`
8
+ - **QueryId correlation** — Every response must include the original `queryId` for client-side Promise resolution
9
+ - **Binary tag system** — Use `tagUtils.js` for all binary data detection; don't implement custom tag parsing
10
+ - **Controller context** — Controllers receive `this` with `clientId`, `broadcast`, `send`, and embedded data
11
+ - **Error handling** — Catch all controller errors and send error responses; never let exceptions crash the connection
12
+ - **Security first** — All connections must pass `open.js` validation before processing messages
13
+
14
+ ## Directory Structure
15
+
16
+ ```
17
+ socket/
18
+ ├── authMiddleware.js # Authorization middleware for endpoint access control
19
+ ├── open.js # Connection validation & security check
20
+ ├── receive.js # Incoming message handler
21
+ ├── receiveContext.js # Controller context factory
22
+ ├── send.js # Outgoing message handler
23
+ └── tagUtils.js # Binary data tag parsing utilities
24
+ ```
25
+
26
+ ## Files
27
+
28
+ ### `authMiddleware.js`
29
+
30
+ Authorization middleware for endpoint access control:
31
+
32
+ - Checks auth tier requirements before controller invocation
33
+ - Supports wildcard endpoint patterns (`admin/*`, `*`)
34
+ - Permission and role-based authorization with `requireAll` option
35
+ - Configurable per-endpoint requirements via `setRequirement()`
36
+ - Creates standard `authz_fail` responses for unauthorized requests
37
+
38
+ ### `open.js`
39
+
40
+ Connection open handler called when a new WebSocket connection is established:
41
+
42
+ - Validates the connection origin against the host (CSRF protection)
43
+ - Delegates to `security/origin.js` for origin verification
44
+ - Returns `true` if connection is allowed, `false` to reject
45
+
46
+ ### `receive.js`
47
+
48
+ Incoming message handler that processes client requests:
49
+
50
+ - Parses incoming WebSocket messages (JSS encoded JSON)
51
+ - Handles `subscribe` and `unsubscribe` messages for pub/sub channels
52
+ - Extracts `type`, `data`, and `queryId` from the message
53
+ - Detects binary upload tags (`<!B>`, `<!A>`, `<!F>`) in the message
54
+ - Coordinates with `fileTransfer` to wait for HTTP uploads
55
+ - Injects uploaded binary data into the message at the tagged paths
56
+ - Invokes the appropriate controller with the complete message
57
+ - Handles errors and sends error responses back to the client
58
+
59
+ ### `send.js`
60
+
61
+ Outgoing message handler that serializes and sends responses:
62
+
63
+ - Serializes controller return values using JSS encoding
64
+ - Detects Buffer/ArrayBuffer values in responses
65
+ - Replaces binary data with `<!L>` (link) tags for client download
66
+ - Registers binary data with `fileTransfer` for HTTP download
67
+ - Sends the serialized response with matching `queryId`
68
+
69
+ ### `receiveContext.js`
70
+
71
+ Controller context factory that creates the `this` binding for controllers:
72
+
73
+ - Extracts session ID from request cookies
74
+ - Provides `broadcast()` and `broadcastOthers()` for messaging all clients
75
+ - Provides `publish()` for pub/sub channel messaging
76
+ - Exposes `clientId`, `sessionId`, and `clients` Map
77
+ - When auth is configured, adds `isAuthenticated`, `authTier`, `principal`, and `requiresTier()`
78
+
79
+ ### `tagUtils.js`
80
+
81
+ Utilities for parsing and processing binary data tags:
82
+
83
+ - `findUploadTags(obj)` — Find all `<!B>` and `<!A>` tags with their paths
84
+ - `findFileTags(obj)` — Find all `<!F>` tags for streaming transfers
85
+ - `cleanUploadTags(obj)` — Remove tag suffixes from object keys
86
+ - `setValueAtPath(obj, path, value)` — Inject values at dot-notation paths
@@ -1,10 +1,156 @@
1
- const originSecurity = require( '../security/origin')
1
+ /**
2
+ * @fileoverview Socket Open Handler - Connection Validation Gateway
3
+ *
4
+ * This module provides the entry point handler for new WebSocket connections.
5
+ * It acts as a security gateway, validating the connection origin and enforcing
6
+ * security policies before allowing the connection to proceed.
7
+ *
8
+ * ## Purpose
9
+ *
10
+ * When a new WebSocket connection is initiated, this handler:
11
+ * 1. Receives the socket and HTTP upgrade request
12
+ * 2. Delegates to origin security validation
13
+ * 3. Returns whether the connection should be accepted or rejected
14
+ *
15
+ * ## Security Flow
16
+ *
17
+ * ```
18
+ * Client Connect → open() → originSecurity() → Accept/Reject
19
+ * ↓
20
+ * - Validates Origin header
21
+ * - Checks domain allowlist
22
+ * - Prevents CSRF attacks
23
+ * ```
24
+ *
25
+ * ## Integration
26
+ *
27
+ * This handler is called by the wiring layer when a new WebSocket
28
+ * connection is received. It's the first line of defense against
29
+ * unauthorized or malicious connections.
30
+ *
31
+ * @module server/socket/open
32
+ * @see {@link module:server/security/origin} - Origin validation implementation
33
+ * @see {@link module:server/lib/wiring} - Wiring layer that calls this handler
34
+ *
35
+ * @example
36
+ * // Used internally by wiring layer
37
+ * const open = require('./socket/open')
38
+ *
39
+ * wss.on('connection', (socket, req) => {
40
+ * const isValid = open(socket, req, (error) => {
41
+ * console.error('Connection rejected:', error)
42
+ * })
43
+ *
44
+ * if (!isValid) {
45
+ * socket.close(1008, 'Security policy violation')
46
+ * return
47
+ * }
48
+ *
49
+ * // Connection accepted, continue with setup...
50
+ * })
51
+ *
52
+ * @example
53
+ * // Error callback receives security violation details
54
+ * open(socket, req, (error) => {
55
+ * console.error('Security violation:', error.message)
56
+ * // Log for security monitoring
57
+ * securityLog.warn({
58
+ * type: 'connection_rejected',
59
+ * origin: req.headers.origin,
60
+ * ip: req.socket.remoteAddress,
61
+ * reason: error.message
62
+ * })
63
+ * })
64
+ */
2
65
 
3
- module.exports = function open(socket, req, onError){
66
+ const originSecurity = require("../security/origin");
4
67
 
5
- const isSecure = originSecurity(socket, req, onError)
6
- if ( ! isSecure) {
7
- return false;
8
- }
9
- return true
10
- }
68
+ /**
69
+ * Handle socket open event and validate connection security.
70
+ *
71
+ * This function is the first handler called when a new WebSocket
72
+ * connection is established. It validates the connection against
73
+ * security policies (primarily origin validation) and returns
74
+ * whether the connection should be accepted.
75
+ *
76
+ * ## Security Checks
77
+ *
78
+ * - **Origin Validation**: Ensures the Origin header matches allowed domains
79
+ * - **CSRF Prevention**: Blocks cross-origin requests from untrusted sources
80
+ * - **Protocol Compliance**: Validates WebSocket upgrade request format
81
+ *
82
+ * ## Failure Handling
83
+ *
84
+ * When validation fails:
85
+ * 1. The onError callback is invoked with error details
86
+ * 2. The function returns `false`
87
+ * 3. The caller is responsible for closing the socket
88
+ *
89
+ * ## Success Flow
90
+ *
91
+ * When validation succeeds:
92
+ * 1. The function returns `true`
93
+ * 2. The caller can proceed with connection setup
94
+ * 3. No error callback is invoked
95
+ *
96
+ * @function open
97
+ * @param {Object} socket - WebSocket instance (from WebSocketServer)
98
+ * @param {http.IncomingMessage} req - HTTP upgrade request object
99
+ * @param {function(Error): void} onError - Callback invoked on security failure
100
+ * @returns {boolean} True if connection is valid and secure, false otherwise
101
+ *
102
+ * @example
103
+ * // Basic usage in connection handler
104
+ * const isSecure = open(socket, req, (err) => {
105
+ * console.error('Connection failed security check:', err)
106
+ * })
107
+ *
108
+ * if (!isSecure) {
109
+ * return // Connection will be terminated
110
+ * }
111
+ *
112
+ * // Safe to proceed with connection setup
113
+ * setupConnection(socket, req)
114
+ *
115
+ * @example
116
+ * // With detailed error handling
117
+ * const isSecure = open(socket, req, (error) => {
118
+ * // Log security event
119
+ * logger.security('connection_rejected', {
120
+ * origin: req.headers.origin,
121
+ * host: req.headers.host,
122
+ * ip: req.socket.remoteAddress,
123
+ * userAgent: req.headers['user-agent'],
124
+ * error: error.message
125
+ * })
126
+ *
127
+ * // Optionally notify security monitoring
128
+ * if (isRateLimitExceeded(req.socket.remoteAddress)) {
129
+ * alertSecurityTeam('Possible attack detected')
130
+ * }
131
+ * })
132
+ *
133
+ * @example
134
+ * // In wiring layer integration
135
+ * function handleConnection(socket, req) {
136
+ * // First, validate security
137
+ * if (!open(socket, req, handleSecurityError)) {
138
+ * socket.close(1008, 'Policy violation')
139
+ * return null
140
+ * }
141
+ *
142
+ * // Connection is secure, create session
143
+ * const session = createSession(socket, req)
144
+ * return session
145
+ * }
146
+ */
147
+ module.exports = function open(socket, req, onError) {
148
+ // Delegate to origin security for validation
149
+ const isSecure = originSecurity(socket, req, onError);
150
+
151
+ if (!isSecure) {
152
+ return false;
153
+ }
154
+
155
+ return true;
156
+ };