mosquito-transport 1.6.0 → 1.6.2

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.
@@ -1,6 +1,7 @@
1
1
  import LimitTask from "limit-task";
2
2
  import naclPkg from 'tweetnacl-functional';
3
3
  import { availableParallelism } from 'os';
4
+ import { Worker } from "worker_threads";
4
5
 
5
6
  function e2e_baseCode() {
6
7
  const serializeE2E = (data, clientPublicKey, serverPrivateKey) => {
@@ -43,13 +43,9 @@ export const niceTry = (promise) => new Promise(async resolve => {
43
43
  }
44
44
  });
45
45
 
46
- export const serializeEntity = obj => serialize(obj);
47
-
48
- export const deserializeEntity = deserialize;
49
-
50
- export const deserializeE2E = async (data = '', projectName) => {
46
+ export const deserializeE2E = async (data, projectName) => {
51
47
  try {
52
- const [clientPubKey, clientNonce, clientData] = data.split('.').map(v => new Uint8Array(Buffer.from(v, 'base64'))),
48
+ const [clientPubKey, clientNonce, clientData] = deserialize(data),
53
49
  [_, serverPrivateKey] = Scoped.InstancesData[projectName].E2E_BufferPair || [];
54
50
 
55
51
  if (!serverPrivateKey) throw '"e2eKeyPair" is required for decrypting an end-to-end messages';
@@ -62,7 +58,7 @@ export const deserializeE2E = async (data = '', projectName) => {
62
58
 
63
59
  if (!baseArray) throw 'The server was unable to decrypt the request body';
64
60
 
65
- const decryptedData = JSON.parse(Buffer.from(baseArray).toString('utf8'));
61
+ const decryptedData = deserialize(baseArray);
66
62
 
67
63
  return [decryptedData[0], clientPubKey, decryptedData[1]];
68
64
  } catch (e) {
@@ -72,24 +68,24 @@ export const deserializeE2E = async (data = '', projectName) => {
72
68
 
73
69
  export const serializeE2E = async (message, clientPublicKey, projectName) => {
74
70
  const [_, serverPrivateKey] = Scoped.InstancesData[projectName].E2E_BufferPair || [];
75
- const inputData = new Uint8Array(Buffer.from(JSON.stringify([message]), 'utf8'));
71
+ const inputData = serialize(message);
76
72
 
77
73
  if (inputData.byteLength > 10240) {
78
74
  const { data, nonce } = await e2e_worker.encrypt(inputData, clientPublicKey, serverPrivateKey);
79
- return `${Buffer.from(nonce).toString('base64')}.${Buffer.from(data).toString('base64')}`;
75
+ return serialize([nonce, data]);
80
76
  }
81
77
 
82
- const nonce = randomBytes(box.nonceLength),
83
- nonceBase64 = Buffer.from(nonce).toString('base64');
78
+ const nonce = randomBytes(box.nonceLength);
84
79
 
85
- return `${nonceBase64}.${Buffer.from(
80
+ return serialize([
81
+ nonce,
86
82
  box(
87
83
  inputData,
88
84
  nonce,
89
85
  clientPublicKey,
90
86
  serverPrivateKey
91
87
  )
92
- ).toString('base64')}`;
88
+ ]);
93
89
  };
94
90
 
95
91
  export const getStringExtension = (url) => {
package/lib/index.d.ts CHANGED
@@ -276,6 +276,7 @@ interface MSocketSnapshot {
276
276
  interface MSocketError {
277
277
  error: string;
278
278
  message: string;
279
+ socket: Socket;
279
280
  }
280
281
 
281
282
  interface TransformMediaOption {
@@ -358,7 +359,8 @@ interface MosquitoServerConfig {
358
359
 
359
360
  storageRules: (snapshot?: StorageRulesSnapshot) => Promise<void> | undefined;
360
361
  databaseRules: (snapshot?: DatabaseRulesSnapshot) => Promise<void> | undefined;
361
- onSocketSnapshot?: (snapshot?: MSocketSnapshot, error?: MSocketError) => void;
362
+ onSocketSnapshot?: (snapshot?: MSocketSnapshot) => void;
363
+ onSocketError?: (error?: MSocketError) => void;
362
364
  /**
363
365
  * the port number you want mosquito-transport instance to be running on
364
366
  */
@@ -709,6 +711,13 @@ interface RawBodyRequest extends express.Request {
709
711
  rawBody: Buffer;
710
712
  }
711
713
 
714
+ /**
715
+ * useful for avoiding encrypting data and extra overhead
716
+ */
717
+ export class DoNotEncrypt {
718
+ value: any;
719
+ }
720
+
712
721
  export default class MosquitoTransportServer {
713
722
  constructor(config: MosquitoServerConfig);
714
723
 
package/lib/index.js CHANGED
@@ -4,7 +4,7 @@ import { databaseLivePath, databaseLiveRoutes, databaseRoutes, dbRoute, emitData
4
4
  import { authLivePath, authLiveRoutes, authRouteName, authRoutes } from "./products/auth/index.js";
5
5
  import { removeVideoFreezer, storageRouteName, storageRoutes, validateStoragePath } from "./products/storage/index.js";
6
6
  import { Scoped } from "./helpers/variables.js";
7
- import { decodeBinary, deserializeE2E, deserializeEntity, encodeBinary, ensureDir, getStringExtension, interpolate, niceTry, normalizeRoute, serializeE2E, serializeEntity } from "./helpers/utils.js";
7
+ import { decodeBinary, deserializeE2E, encodeBinary, ensureDir, getStringExtension, interpolate, niceTry, normalizeRoute, serializeE2E } from "./helpers/utils.js";
8
8
  import { getDB } from "./products/database/base.js";
9
9
  import { releaseTokenSelfDestruction, validateJWT, verifyJWT } from "./products/auth/tokenizer.js";
10
10
  import { ADMIN_DB_NAME, ADMIN_DB_URL, AUTH_PROVIDER_ID, ERRORS, EnginePath, EngineRoutes, STORAGE_DIRS, STORAGE_PREFIX_PATH, STORAGE_ROUTE, one_hour, one_mb, one_minute } from "./helpers/values.js";
@@ -301,7 +301,7 @@ const InternalRoutes = [
301
301
  ];
302
302
 
303
303
  const useMosquitoServer = (app, config) => {
304
- const { projectName, port, accessKey, corsOrigin, maxRequestBufferSize, onSocketSnapshot, enforceE2E_Encryption, preMiddlewares, onUserMounted } = config;
304
+ const { projectName, port, accessKey, corsOrigin, maxRequestBufferSize, onSocketSnapshot, onSocketError, enforceE2E_Encryption, preMiddlewares, onUserMounted } = config;
305
305
 
306
306
  app.disable("x-powered-by");
307
307
 
@@ -328,6 +328,7 @@ const useMosquitoServer = (app, config) => {
328
328
  serveStorage({ ...config }),
329
329
  express.json({ type: '*/json', limit: maxRequestBufferSize || '100MB' }),
330
330
  express.text({ type: 'text/plain', limit: maxRequestBufferSize || '100MB' }),
331
+ express.raw({ type: 'request/buffer', limit: maxRequestBufferSize || '100MB' }),
331
332
  (req, _, next) => {
332
333
  const authToken = req.headers['mosquito-token'];
333
334
  if (authToken) req.headers.mtoken = authToken;
@@ -352,7 +353,7 @@ const useMosquitoServer = (app, config) => {
352
353
  if (chunk) chunks.push(Buffer.from(chunk));
353
354
  const totalBuf = Buffer.concat(chunks);
354
355
  const { __sender } = req;
355
- const transformedBody = typeof __sender === 'function' ? Buffer.from(await __sender(totalBuf), 'utf8') : totalBuf;
356
+ const transformedBody = typeof __sender === 'function' ? await __sender(totalBuf) : totalBuf;
356
357
 
357
358
  res.setHeader('Content-Length', Buffer.byteLength(transformedBody));
358
359
  originalWrite.call(res, transformedBody);
@@ -443,7 +444,7 @@ const useMosquitoServer = (app, config) => {
443
444
  let mtoken, clientPublicKey, extraAuth;
444
445
 
445
446
  if (ugly) {
446
- const [body, clientKey, atoken] = await deserializeE2E(e2e, projectName);
447
+ const [body, clientKey, atoken] = await deserializeE2E(Buffer.from(e2e, 'base64'), projectName);
447
448
  mtoken = atoken;
448
449
  clientPublicKey = clientKey;
449
450
  if (body.accessKey !== accessKey)
@@ -468,7 +469,7 @@ const useMosquitoServer = (app, config) => {
468
469
  callback?.(...[...arguments]);
469
470
  return;
470
471
  }
471
- const [emittion, emitable, ...rest] = [...arguments];
472
+ const [[emittion, not_encrypted], emitable, ...rest] = [...arguments];
472
473
 
473
474
  let reqBody, clientPublicKey;
474
475
 
@@ -480,9 +481,10 @@ const useMosquitoServer = (app, config) => {
480
481
  ) throw 'tampered socket emittion';
481
482
  if (ugly) {
482
483
  const [body, clientKey] = await deserializeE2E(emittion, projectName);
483
- reqBody = deserializeEntity(body);
484
+ reqBody = body;
484
485
  clientPublicKey = clientKey;
485
486
  } else reqBody = emittion;
487
+ reqBody = discloseSocketArguments([reqBody, not_encrypted]);
486
488
  if (!Array.isArray(reqBody))
487
489
  throw simplifyError('invalid_argument_result', 'The request body was not deserialized correctly');
488
490
  } catch (e) {
@@ -490,19 +492,19 @@ const useMosquitoServer = (app, config) => {
490
492
  onError?.({
491
493
  ...simplifyCaughtError(e)?.simpleError,
492
494
  auth: extraAuth,
493
- data: reqBody || emittion
495
+ data: emittion
494
496
  });
495
497
  return;
496
498
  }
497
499
 
498
500
  callback?.(...reqBody, ...typeof emitable === 'function' ? [async function () {
499
- const args = [...arguments];
501
+ const [args, not_encrypted] = encloseSocketArguments([...arguments]);
500
502
  let res;
501
503
 
502
504
  if (ugly) {
503
- res = await serializeE2E(serializeEntity(args), clientPublicKey, projectName);
505
+ res = await serializeE2E(args, clientPublicKey, projectName);
504
506
  } else res = args;
505
- emitable(res);
507
+ emitable([res, not_encrypted]);
506
508
  }] : []);
507
509
  });
508
510
  };
@@ -516,31 +518,31 @@ const useMosquitoServer = (app, config) => {
516
518
 
517
519
  const lastEmit = restEmit.slice(-1)[0];
518
520
  const hasEmitable = typeof lastEmit === 'function';
519
- const mit = hasEmitable ? restEmit.slice(0, -1) : restEmit;
521
+ const [mit, not_encrypted] = encloseSocketArguments(hasEmitable ? restEmit.slice(0, -1) : restEmit);
520
522
 
521
523
  if (hasEmitable && promise)
522
524
  throw 'emitWithAck cannot have function in it argument';
523
525
 
524
- const reqBuilder = ugly ? await serializeE2E(serializeEntity(mit), clientPublicKey, projectName) : null;
526
+ const reqBuilder = ugly ? await serializeE2E(mit, clientPublicKey, projectName) : null;
525
527
 
526
528
  const result = await (timeout ? socket.timeout(timeout) : socket)[promise ? 'emitWithAck' : 'emit'](
527
529
  route,
528
- ugly ? reqBuilder : mit,
529
- ...hasEmitable ? [function () {
530
- const [args] = [...arguments];
531
- (async function () {
532
- let res;
530
+ [ugly ? reqBuilder : mit, not_encrypted],
531
+ ...hasEmitable ? [async function () {
532
+ const [[args, not_encrypted]] = [...arguments];
533
+ let res;
533
534
 
534
- if (ugly) {
535
- res = deserializeEntity((await deserializeE2E(args, projectName))[0]);
536
- } else res = args;
535
+ if (ugly) {
536
+ res = (await deserializeE2E(args, projectName))[0];
537
+ } else res = args;
537
538
 
538
- lastEmit(...res || []);
539
- })();
539
+ lastEmit(...discloseSocketArguments([res, not_encrypted]));
540
540
  }] : []
541
541
  );
542
542
 
543
- if (result && promise) return ugly ? deserializeEntity((await deserializeE2E(result, projectName))[0])[0] : result[0];
543
+ if (result && promise) {
544
+ return discloseSocketArguments([ugly ? (await deserializeE2E(result[0], projectName))[0] : result[0], result[1]])[0];
545
+ }
544
546
  };
545
547
 
546
548
  const clonedSocket = {
@@ -569,7 +571,7 @@ const useMosquitoServer = (app, config) => {
569
571
  };
570
572
  onSocketSnapshot(clonedSocket);
571
573
  } catch (e) {
572
- onSocketSnapshot?.(undefined, simplifyCaughtError(e).simpleError);
574
+ onSocketError?.(Object.assign(simplifyCaughtError(e).simpleError, { socket }));
573
575
  }
574
576
  });
575
577
 
@@ -578,6 +580,30 @@ const useMosquitoServer = (app, config) => {
578
580
  });
579
581
  }
580
582
 
583
+ export class DoNotEncrypt {
584
+ constructor(value) {
585
+ this.value = value;
586
+ }
587
+ };
588
+
589
+ const encloseSocketArguments = (args) => {
590
+ const [encrypted, unencrypted] = [{}, {}];
591
+
592
+ args.forEach((v, i) => {
593
+ if (v instanceof DoNotEncrypt) {
594
+ unencrypted[i] = v.value;
595
+ } else encrypted[i] = v;
596
+ });
597
+ return [encrypted, unencrypted];
598
+ }
599
+
600
+ const discloseSocketArguments = (args = []) => {
601
+ return args.map((obj, i) => Object.entries(obj).map(v => i ? [v[0], new DoNotEncrypt(v[1])] : v)).flat()
602
+ .sort((a, b) => (a[0] * 1) - (b[0] * 1)).map((v, i) => {
603
+ if (v[0] * 1 !== i) throw 'corrupted socket arguments';
604
+ return v[1];
605
+ });
606
+ }
581
607
 
582
608
  export default class MosquitoTransportServer {
583
609
  constructor(configx) {
@@ -753,14 +779,13 @@ export default class MosquitoTransportServer {
753
779
  const [body, clientKey, atoken] = await deserializeE2E(req.body, this.projectName);
754
780
  clientPublicKey = clientKey;
755
781
  authToken = atoken;
756
- const rawBody = deserializeEntity(body)[0];
757
782
  const initContentType = req.headers["init-content-type"];
758
783
 
759
784
  if (initContentType === 'application/json') {
760
785
  try {
761
- reqBody = JSON.parse(rawBody);
786
+ reqBody = JSON.parse(body);
762
787
  } catch (_) { }
763
- } else reqBody = rawBody;
788
+ } else reqBody = body;
764
789
  }
765
790
 
766
791
  req.body = reqBody;
@@ -789,15 +814,20 @@ export default class MosquitoTransportServer {
789
814
  try {
790
815
  if (uglified) {
791
816
  req.__sender = async (buffer) => {
792
- res.set('content-type', 'text/plain');
793
- return await serializeE2E(buffer.toString('base64'), clientPublicKey, this.projectName);
817
+ res.set('content-type', 'application/octet-stream');
818
+ return await serializeE2E(buffer, clientPublicKey, this.projectName);
794
819
  };
795
820
  }
796
821
 
797
822
  await callback(req, res, auth ? { ...auth, token: authToken } : null);
798
823
  } catch (e) {
799
824
  if (hasErrorLogger) console.error(`errRoute: /${route} err:`, e);
800
- if (!res.headersSent) res.end();
825
+ if (!res.headersSent) {
826
+ res.setHeader(
827
+ 'simple_error',
828
+ JSON.stringify(simplifyCaughtError(e).simpleError || {})
829
+ );
830
+ }
801
831
  }
802
832
  if (hasLogger) console.log(`${req.url} took: ${Date.now() - now}ms`);
803
833
  })
@@ -1155,7 +1185,9 @@ const validateServerConfig = (config, that) => {
1155
1185
  transformMediaCleanupTimeout,
1156
1186
  onUserMounted,
1157
1187
  ddosMap,
1158
- internals
1188
+ internals,
1189
+ onSocketSnapshot,
1190
+ onSocketError
1159
1191
  } = config;
1160
1192
 
1161
1193
  if (
@@ -1225,6 +1257,12 @@ const validateServerConfig = (config, that) => {
1225
1257
  if (storageRules !== undefined && typeof storageRules !== 'function')
1226
1258
  throw `storageRules type must be function but got ${typeof storageRules}`;
1227
1259
 
1260
+ if (onSocketSnapshot !== undefined && typeof onSocketSnapshot !== 'function')
1261
+ throw `onSocketSnapshot type must be function but got ${typeof onSocketSnapshot}`;
1262
+
1263
+ if (onSocketError !== undefined && typeof onSocketError !== 'function')
1264
+ throw `onSocketError type must be function but got ${typeof onSocketError}`;
1265
+
1228
1266
  if (typeof signerKey !== 'string' || signerKey.trim().length !== 90)
1229
1267
  throw `signerKey must have string length equals to 90 characters without spaces`;
1230
1268
 
@@ -9,6 +9,7 @@ import { validateJWT } from "./tokenizer.js";
9
9
  import { Validator } from "guard-object";
10
10
  import { simplifyCaughtError } from 'simplify-error';
11
11
  import { statusErrorCode, useDDOS } from "../../helpers/ddos.js";
12
+ import { serialize } from "entity-serializer";
12
13
 
13
14
  const {
14
15
  _listenUserVerification,
@@ -87,7 +88,7 @@ export const authRoutes = ({
87
88
  const { data, metadata: metax, token, r_token } = reqBody;
88
89
 
89
90
  const makeResult = async (b) => {
90
- return ugly ? { e2e: await serializeE2E(b, clientPublicKey, projectName) } : b;
91
+ return ugly ? serialize([await serializeE2E(b, clientPublicKey, projectName)]) : b;
91
92
  }
92
93
 
93
94
  switch (route) {
@@ -154,7 +155,9 @@ export const authRoutes = ({
154
155
  logger.includes('all') ||
155
156
  logger.includes('error')
156
157
  ) console.error(`errRoute: /${route} err:`, e);
157
- res.status(statusErrorCode(e)).send({ status: 'error', ...simplifyCaughtError(e) });
158
+ const result = { status: 'error', ...simplifyCaughtError(e) };
159
+
160
+ res.status(statusErrorCode(e)).send(ugly ? serialize([undefined, result]) : result);
158
161
  }
159
162
  if (hasLogger) console.log(`/${route} took: ${Date.now() - now}ms`);
160
163
  })
@@ -181,7 +184,7 @@ export const authLiveRoutes = ({ projectName, logger, enforceETE_Encryption, int
181
184
  let mtoken = initAuthHandshake.mtoken, clientPublicKey;
182
185
 
183
186
  if (ugly) {
184
- const [body, clientKey] = await deserializeE2E(initAuthHandshake.e2e, projectName);
187
+ const [body, clientKey] = await deserializeE2E(Buffer.from(initAuthHandshake.e2e, 'base64'), projectName);
185
188
  mtoken = body.mtoken;
186
189
  clientPublicKey = clientKey;
187
190
  }
@@ -14,6 +14,7 @@ import { simplifyCaughtError, simplifyError } from 'simplify-error';
14
14
  import { deserializeBSON, serializeToBase64 } from "./bson.js";
15
15
  import cloneDeep from "lodash.clonedeep";
16
16
  import { statusErrorCode, useDDOS } from "../../helpers/ddos.js";
17
+ import { serialize } from "entity-serializer";
17
18
 
18
19
  export const TIMESTAMP = { $timestamp: 'now' };
19
20
 
@@ -421,7 +422,7 @@ export const databaseRoutes = ({ projectName, logger, accessKey, enforceE2E_Encr
421
422
  }
422
423
 
423
424
  const makeResult = async (b) => {
424
- return ugly ? { e2e: await serializeE2E(b, clientPublicKey, projectName) } : b;
425
+ return ugly ? serialize([await serializeE2E(b, clientPublicKey, projectName)]) : b;
425
426
  }
426
427
 
427
428
  switch (route) {
@@ -456,10 +457,9 @@ export const databaseRoutes = ({ projectName, logger, accessKey, enforceE2E_Encr
456
457
  }
457
458
  } catch (e) {
458
459
  if (logger.includes('all') || logger.includes('error')) console.error(`errRoute: /${route} err:`, e);
459
- res.status(statusErrorCode(e)).send({
460
- status: 'error',
461
- ...simplifyCaughtError(e)
462
- });
460
+ const result = { status: 'error', ...simplifyCaughtError(e) };
461
+
462
+ res.status(statusErrorCode(e)).send(ugly ? serialize([undefined, result]) : result);
463
463
  }
464
464
  if (hasLogger) console.log(`${route} took: ${Date.now() - now}ms`);
465
465
  })
@@ -504,7 +504,7 @@ export const databaseLiveRoutes = ({
504
504
 
505
505
  if (ugly) {
506
506
  try {
507
- const [body, clientKey, atoken] = await deserializeE2E(initAuthshake.e2e, projectName);
507
+ const [body, clientKey, atoken] = await deserializeE2E(Buffer.from(initAuthshake.e2e, 'base64'), projectName);
508
508
 
509
509
  mtoken = atoken;
510
510
  authshakeObj = body;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mosquito-transport",
3
- "version": "1.6.0",
3
+ "version": "1.6.2",
4
4
  "description": "MosquitoTransport is a powerful tool that helps persist and synchronize data between your MongoDB database and frontend applications",
5
5
  "main": "lib/index.js",
6
6
  "type": "module",
@@ -36,11 +36,11 @@
36
36
  },
37
37
  "homepage": "https://github.com/deflexable/mosquito-transport#readme",
38
38
  "dependencies": {
39
- "@deflexable/bit-stream": "^1.0.2",
39
+ "@deflexable/bit-stream": "^1.0.4",
40
40
  "buffer": "^6.0.3",
41
41
  "compression": "^1.7.4",
42
42
  "cors": "^2.8.5",
43
- "entity-serializer": "^1.0.0",
43
+ "entity-serializer": "^1.0.1",
44
44
  "express": "^4.18.2",
45
45
  "google-auth-library": "^8.8.0",
46
46
  "guard-object": "^1.1.3",