vortez 5.0.0-dev.17 → 5.0.0-dev.19

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 (85) hide show
  1. package/.gitignore +9 -4
  2. package/README.md +681 -176
  3. package/build/Vortez.d.ts +1 -0
  4. package/build/Vortez.js +1 -0
  5. package/build/Vortez.js.map +1 -1
  6. package/build/beta/JwtManager/HeaderValidator.d.ts +25 -0
  7. package/build/beta/JwtManager/HeaderValidator.js +47 -0
  8. package/build/beta/JwtManager/HeaderValidator.js.map +1 -0
  9. package/build/beta/JwtManager/Jwt.d.ts +14 -6
  10. package/build/beta/JwtManager/Jwt.js +15 -7
  11. package/build/beta/JwtManager/Jwt.js.map +1 -1
  12. package/build/beta/JwtManager/JwtManager.d.ts +31 -24
  13. package/build/beta/JwtManager/JwtManager.js +61 -40
  14. package/build/beta/JwtManager/JwtManager.js.map +1 -1
  15. package/build/beta/JwtManager/JwtUtils.d.ts +19 -4
  16. package/build/beta/JwtManager/JwtUtils.js +18 -0
  17. package/build/beta/JwtManager/JwtUtils.js.map +1 -1
  18. package/build/beta/JwtManager/KeyEntry.d.ts +52 -0
  19. package/build/beta/JwtManager/KeyEntry.js +42 -0
  20. package/build/beta/JwtManager/KeyEntry.js.map +1 -0
  21. package/build/beta/JwtManager/KeyGenerator.d.ts +5 -0
  22. package/build/beta/JwtManager/KeyGenerator.js +12 -9
  23. package/build/beta/JwtManager/KeyGenerator.js.map +1 -1
  24. package/build/server/Response.d.ts +1 -1
  25. package/build/server/Response.js +1 -1
  26. package/build/server/Response.js.map +1 -1
  27. package/build/server/Server.d.ts +4 -4
  28. package/build/server/Server.js +5 -5
  29. package/build/server/Server.js.map +1 -1
  30. package/build/server/ServerDebug.d.ts +10 -1
  31. package/build/server/ServerDebug.js +85 -17
  32. package/build/server/ServerDebug.js.map +1 -1
  33. package/build/server/config/Config.d.ts +274 -47
  34. package/build/server/config/Config.js +68 -47
  35. package/build/server/config/Config.js.map +1 -1
  36. package/build/server/config/{ConfigLoader.d.ts → Loader.d.ts} +4 -5
  37. package/build/server/config/{ConfigLoader.js → Loader.js} +7 -10
  38. package/build/server/config/Loader.js.map +1 -0
  39. package/build/server/router/Router.d.ts +87 -30
  40. package/build/server/router/Router.js +110 -48
  41. package/build/server/router/Router.js.map +1 -1
  42. package/build/server/router/algorithm/Algorithm.d.ts +39 -0
  43. package/build/server/router/algorithm/Algorithm.js +20 -0
  44. package/build/server/router/algorithm/Algorithm.js.map +1 -0
  45. package/build/server/router/algorithm/FIFO.d.ts +15 -0
  46. package/build/server/router/algorithm/FIFO.js +24 -0
  47. package/build/server/router/algorithm/FIFO.js.map +1 -0
  48. package/build/server/router/algorithm/Tree.d.ts +38 -0
  49. package/build/server/router/algorithm/Tree.js +126 -0
  50. package/build/server/router/algorithm/Tree.js.map +1 -0
  51. package/build/server/router/middleware/WsMiddleware.js +1 -1
  52. package/build/server/router/middleware/WsMiddleware.js.map +1 -1
  53. package/build/utilities/Flatten.d.ts +56 -0
  54. package/build/utilities/Flatten.js +59 -0
  55. package/build/utilities/Flatten.js.map +1 -0
  56. package/build/utilities/Utilities.d.ts +7 -58
  57. package/build/utilities/Utilities.js +8 -33
  58. package/build/utilities/Utilities.js.map +1 -1
  59. package/build/utilities/schema/Introspection.d.ts +24 -0
  60. package/build/utilities/schema/Introspection.js +87 -0
  61. package/build/utilities/schema/Introspection.js.map +1 -0
  62. package/build/utilities/schema/JSONSchema.d.ts +68 -0
  63. package/build/utilities/schema/JSONSchema.js +13 -0
  64. package/build/utilities/schema/JSONSchema.js.map +1 -0
  65. package/build/utilities/schema/Schema.d.ts +253 -0
  66. package/build/utilities/schema/Schema.js +241 -0
  67. package/build/utilities/schema/Schema.js.map +1 -0
  68. package/build/utilities/schema/SchemaError.d.ts +10 -0
  69. package/build/utilities/schema/SchemaError.js +13 -0
  70. package/build/utilities/schema/SchemaError.js.map +1 -0
  71. package/build/utilities/schema/Validator.d.ts +94 -0
  72. package/build/utilities/schema/Validator.js +246 -0
  73. package/build/utilities/schema/Validator.js.map +1 -0
  74. package/package.json +1 -1
  75. package/tests/config/config.js +233 -0
  76. package/tests/jwtManager/jwtManager.js +310 -46
  77. package/tests/router.js +596 -0
  78. package/tests/schema/schema.js +368 -0
  79. package/tests/test.env +0 -0
  80. package/tests/test.js +3 -3
  81. package/build/server/config/ConfigLoader.js.map +0 -1
  82. package/build/server/config/ConfigValidator.d.ts +0 -71
  83. package/build/server/config/ConfigValidator.js +0 -131
  84. package/build/server/config/ConfigValidator.js.map +0 -1
  85. package/examples/in-docs.js +0 -96
@@ -1,14 +1,18 @@
1
1
  // @ts-check
2
- import JwtManager from "../../build/beta/JwtManager/JwtManager.js";
3
2
  import { strict as assert } from 'assert';
4
3
 
5
- /** @type { JwtManager.Algorithm.HashLength[] } */
4
+ import { Beta, Logger } from "../../build/Vortez.js";
5
+
6
+ const JwtManager = Beta.JwtManager;
7
+ const logger = new Logger({prefix: 'JWT'});
8
+
9
+ /** @type { Beta.JwtManager.Algorithm.HashLength[] } */
6
10
  const MASKS = ['256', '384', '512'];
7
11
 
8
- const { pub: HMAC_PUB_KEY, key: SECRET } = JwtManager.KeyGenerator.generate('secret');
9
- const { pub: RSA_PUB_KEY, key: RSA_PRIV_KEY } = JwtManager.KeyGenerator.generate('rsa');
10
- const { pub: PSS_PUB_KEY, key: PSS_PRIV_KEY } = JwtManager.KeyGenerator.generate('rsa-pss');
11
- const { pub: EC_PUB_KEY, key: EC_PRIV_KEY } = JwtManager.KeyGenerator.generate('ec');
12
+ const RSA_KEY = JwtManager.KeyGenerator.generate('rsa');
13
+ const PSS_KEY = JwtManager.KeyGenerator.generate('rsa-pss');
14
+ const EC_KEY = JwtManager.KeyGenerator.generate('ec');
15
+ const SECRET = JwtManager.KeyGenerator.generate('secret');
12
16
 
13
17
  const payload = {
14
18
  uuid: '00000000-0000-0000-0000-000000000000',
@@ -19,60 +23,320 @@ const payload = {
19
23
  const header = {
20
24
  exp: Math.floor((Date.now() + 10000) / 1000)
21
25
  };
26
+
27
+ let testsPassed = 0;
28
+ let testsFailed = 0;
29
+
30
+ /**
31
+ * Logs test results.
32
+ * @param { string } testName The test name.
33
+ * @param { boolean } passed Whether the test passed.
34
+ * @param { unknown | null } error Optional error object for failures.
35
+ */
36
+ function logTestResult(testName, passed, error = null) {
37
+ if (passed) {
38
+ logger.log(`&C2✓ ${testName}`);
39
+ testsPassed++;
40
+ } else {
41
+ const message = error instanceof Error ? error.message : String(error);
42
+ logger.error(`&C1✗ ${testName}${error ? ': ' + message : ''}`);
43
+ testsFailed++;
44
+ }
45
+ };
46
+
22
47
  /**
23
- * Tests the signing and verification of JWTs using the specified algorithm and key.
24
- * @param { JwtManager.AlgorithmName } algorithm The name of the algorithm to be tested (e.g., 'HS256', 'RS256', etc.).
25
- * @param { string } key The secret key or private key to be used for signing and verifying JWTs.
26
- * @param { string | null } pubKey The public key to be used for verifying JWTs (required for asymmetric algorithms).
48
+ * Verifies signing and parsing for a given algorithm and key.
49
+ * @param { Beta.JwtManager.KeyEntry.AlgorithmName } algorithm The algorithm under test.
50
+ * @param { Beta.JwtManager.KeyEntry.Signer } key The signing key.
27
51
  */
28
- const testJwtManagerSignVerify = (algorithm, key, pubKey = null) => {
29
- const jwt = new JwtManager(algorithm, {
30
- key: key,
31
- pub: pubKey || null
32
- });
33
- console.log('--------------------------')
34
- const token = jwt.sign(payload, header);
35
- console.log('jwt:', token);
36
- /* if (algorithm.startsWith('HS')) console.log('secret:', key);
37
- else {
38
- console.log('Private Key:', key);
39
- if (pubKey) console.log('Public Key:', pubKey);
40
- } */
52
+ function testValidSignVerify(algorithm, key) {
41
53
  try {
54
+ const jwt = new JwtManager({
55
+ alg: algorithm,
56
+ key: key,
57
+ kid: 'key-v1'
58
+ });
59
+ const token = jwt.sign(payload, header);
42
60
  const parsed = jwt.parse(token);
43
- console.log(parsed, '\n');
61
+
44
62
  assert.deepEqual(parsed.payload, payload);
45
- console.log(`${algorithm} test passed`);
63
+ assert.equal(parsed.header.alg, algorithm);
64
+ logTestResult(`${algorithm} - Valid token sign/verify`, true);
46
65
  } catch (error) {
47
- console.error(`${algorithm} test failed:`, error);
66
+ logTestResult(`${algorithm} - Valid token sign/verify`, false, error);
48
67
  }
49
68
  };
50
69
 
51
- const runTests = () => {
70
+ /**
71
+ * Ensures a tampered payload is rejected.
72
+ * @param { Beta.JwtManager.KeyEntry.AlgorithmName } algorithm The algorithm under test.
73
+ * @param { Beta.JwtManager.KeyEntry.Signer } key The signing key.
74
+ */
75
+ function testTamperedPayload(algorithm, key) {
52
76
  try {
53
- for (let index = 0; index < MASKS.length; index++) {
54
- /** @type { JwtManager.AlgorithmName } */
55
- const algorithm = `HS${MASKS[index]}`;
56
- testJwtManagerSignVerify(algorithm, SECRET);
57
-
58
- /** @type { JwtManager.AlgorithmName } */
59
- const algorithmRS = `RS${MASKS[index]}`;
60
- testJwtManagerSignVerify(algorithmRS, RSA_PRIV_KEY, RSA_PUB_KEY);
61
-
62
- /** @type { JwtManager.AlgorithmName } */
63
- const algorithmPS = `PS${MASKS[index]}`;
64
- testJwtManagerSignVerify(algorithmPS, PSS_PRIV_KEY, PSS_PUB_KEY);
65
-
66
- /** @type { JwtManager.AlgorithmName } */
67
- const algorithmES = `ES${MASKS[index]}`;
68
- testJwtManagerSignVerify(algorithmES, EC_PRIV_KEY, EC_PUB_KEY);
77
+ const jwt = new JwtManager({
78
+ alg: algorithm,
79
+ key: key,
80
+ });
81
+ const token = jwt.sign(payload, header);
82
+
83
+ const parts = token.split('.');
84
+ const encodedPayload = JwtManager.JwtUtils.objectToBase64Url({ ...payload, name: 'Hacker' });
85
+ const tamperedToken = `${parts[0]}.${encodedPayload}.${parts[2]}`;
86
+
87
+ try {
88
+ jwt.parse(tamperedToken);
89
+ logTestResult(`${algorithm} - Reject tampered payload`, false, new Error('Should have thrown'));
90
+ } catch (error) {
91
+ if (`${error}`.includes('Invalid signature')) {
92
+ logTestResult(`${algorithm} - Reject tampered payload`, true);
93
+ } else {
94
+ logTestResult(`${algorithm} - Reject tampered payload`, false, error);
95
+ }
69
96
  }
97
+ } catch (error) {
98
+ logTestResult(`${algorithm} - Reject tampered payload`, false, error);
99
+ }
100
+ };
70
101
 
71
- console.log('All tests passed');
102
+ /**
103
+ * Ensures an expired token is rejected.
104
+ * @param { Beta.JwtManager.KeyEntry.AlgorithmName } algorithm The algorithm under test.
105
+ * @param { Beta.JwtManager.KeyEntry.Signer } key The signing key.
106
+ */
107
+ function testExpiredToken(algorithm, key) {
108
+ try {
109
+ const jwt = new JwtManager({
110
+ alg: algorithm,
111
+ key: key,
112
+ });
113
+
114
+ const expiredPayload = { ...payload, exp: Math.floor(Date.now() / 1000) - 1 };
115
+ const token = jwt.sign(expiredPayload, header);
116
+
117
+ try {
118
+ jwt.parse(token);
119
+ logTestResult(`${algorithm} - Reject expired token`, false, new Error('Should have thrown'));
120
+ } catch (error) {
121
+ const message = `${error}`;
122
+ if (message.includes('expired')) {
123
+ logTestResult(`${algorithm} - Reject expired token`, true);
124
+ } else {
125
+ logTestResult(`${algorithm} - Reject expired token`, false, message);
126
+ }
127
+ }
72
128
  } catch (error) {
73
- console.error('Test failed:', error);
129
+ logTestResult(`${algorithm} - Reject expired token`, false, error);
74
130
  }
75
131
  };
76
132
 
133
+ /**
134
+ * Ensures a token cannot be verified with an unrelated key set.
135
+ * @param { Beta.JwtManager.KeyEntry.AlgorithmName } algorithm The algorithm under test.
136
+ * @param { Beta.JwtManager.KeyEntry.Signer } key The signing key.
137
+ */
138
+ function testMissingKey(algorithm, key) {
139
+ try {
140
+ const jwt = new JwtManager({
141
+ alg: algorithm,
142
+ key: key,
143
+ });
144
+ const token = jwt.sign(payload, header);
145
+
146
+ const replacementKey = algorithm.startsWith('HS')
147
+ ? JwtManager.KeyGenerator.generate('secret')
148
+ : algorithm.startsWith('RS')
149
+ ? JwtManager.KeyGenerator.generate('rsa')
150
+ : algorithm.startsWith('PS')
151
+ ? JwtManager.KeyGenerator.generate('rsa-pss')
152
+ : JwtManager.KeyGenerator.generate('ec');
153
+
154
+ const newJwt = new JwtManager({
155
+ alg: algorithm,
156
+ key: replacementKey,
157
+ kid: `${algorithm}-replacement`
158
+ });
159
+
160
+ try {
161
+ newJwt.parse(token);
162
+ logTestResult(`${algorithm} - Reject token with unknown KID`, false, new Error('Should have thrown'));
163
+ } catch (error) {
164
+ const message = `${error}`;
165
+ if (message.includes('No key found')) {
166
+ logTestResult(`${algorithm} - Reject token with unknown KID`, true);
167
+ } else {
168
+ logTestResult(`${algorithm} - Reject token with unknown KID`, false, error);
169
+ }
170
+ }
171
+ } catch (error) {
172
+ logTestResult(`${algorithm} - Reject token with unknown KID`, false, error);
173
+ }
174
+ };
175
+
176
+ /**
177
+ * Ensures key rotation works.
178
+ * @param { Beta.JwtManager.KeyEntry.AlgorithmName } algorithm The algorithm under test.
179
+ * @param { Beta.JwtManager.KeyEntry.Signer } key The signing key.
180
+ */
181
+ function testKeyRotation(algorithm, key) {
182
+ try {
183
+ const jwt = new JwtManager({
184
+ alg: algorithm,
185
+ key: key,
186
+ kid: 'key-v1',
187
+ });
188
+
189
+ const newKeyObj = algorithm.startsWith('HS')
190
+ ? JwtManager.KeyGenerator.generate('secret')
191
+ : algorithm.startsWith('RS')
192
+ ? JwtManager.KeyGenerator.generate('rsa')
193
+ : algorithm.startsWith('PS')
194
+ ? JwtManager.KeyGenerator.generate('rsa-pss')
195
+ : JwtManager.KeyGenerator.generate('ec');
196
+
197
+ jwt.addKey({
198
+ alg: algorithm,
199
+ key: newKeyObj,
200
+ kid: 'key-v2'
201
+ });
202
+
203
+ const token = jwt.sign(payload, { ...header, kid: 'key-v2' });
204
+
205
+ const parsed = jwt.parse(token);
206
+ assert.deepEqual(parsed.payload, payload);
207
+ assert.equal(parsed.header.kid, 'key-v2');
208
+
209
+ logTestResult(`${algorithm} - Key rotation`, true);
210
+ } catch (error) {
211
+ logTestResult(`${algorithm} - Key rotation`, false, error);
212
+ }
213
+ };
214
+
215
+ /**
216
+ * Ensures a public-key-only manager can verify but cannot sign.
217
+ * @param { Beta.JwtManager.KeyEntry.AlgorithmName } algorithm
218
+ * @param { { pub: string } } keyObj Object containing the public key.
219
+ */
220
+ function testReadOnlyPublicKey(algorithm, keyObj) {
221
+ if (algorithm.startsWith('HS')) return;
222
+
223
+ try {
224
+ const sharedKid = `${algorithm}-public-only`;
225
+ const verifier = new JwtManager({
226
+ alg: algorithm,
227
+ key: { pub: keyObj.pub },
228
+ kid: sharedKid
229
+ });
230
+
231
+ try {
232
+ verifier.sign(payload);
233
+ logTestResult(`${algorithm} - Public key only: Sign blocked`, false, new Error('Should not be able to sign'));
234
+ } catch (error) {
235
+ if (`${error}`.includes('No key provided')) {
236
+ logTestResult(`${algorithm} - Public key only: Sign blocked`, true);
237
+ } else {
238
+ logTestResult(`${algorithm} - Public key only: Sign blocked`, false, error);
239
+ }
240
+ }
241
+
242
+ const signer = new JwtManager({
243
+ alg: algorithm,
244
+ key: keyObj,
245
+ kid: sharedKid
246
+ });
247
+ const token = signer.sign(payload, { ...header, kid: sharedKid });
248
+
249
+ const parsed = verifier.parse(token);
250
+ assert.deepEqual(parsed.payload, payload);
251
+ logTestResult(`${algorithm} - Public key only: Verify works`, true);
252
+
253
+ } catch (error) {
254
+ logTestResult(`${algorithm} - Public key only`, false, error);
255
+ }
256
+ }
257
+ /**
258
+ * Prevents RS256 to HS256 downgrade attacks.
259
+ */
260
+ function testAlgorithmConfusionAttack() {
261
+ const algRS = 'RS256';
262
+ const algHS = 'HS256';
263
+
264
+ try {
265
+ const manager = new JwtManager({
266
+ alg: algRS,
267
+ key: RSA_KEY,
268
+ kid: 'secure-key'
269
+ });
270
+
271
+ const attackerManager = new JwtManager({
272
+ alg: algHS,
273
+ key: RSA_KEY.pub,
274
+ });
275
+
276
+ const maliciousToken = attackerManager.sign({ ...payload, name: 'Attacker' });
277
+
278
+ try {
279
+ manager.parse(maliciousToken);
280
+ logTestResult(`Security - RS256 to HS256 Attack`, false, new Error('VULNERABLE: Accepted HS256 signed with RSA Public Key'));
281
+ } catch (error) {
282
+ logTestResult(`Security - RS256 to HS256 Attack blocked`, true);
283
+ }
284
+ } catch (error) {
285
+ logTestResult(`Security - RS256 to HS256 Attack error`, false, error);
286
+ }
287
+ }
288
+
289
+ /**
290
+ * Runs all tests for the JwtManager across different algorithms and key types.
291
+ * Logs the results and exits with a non-zero code if any tests failed.
292
+ */
293
+ const runTests = () => {
294
+ logger.log('\n&C6=== JWT Manager Test Suite ===\n');
295
+
296
+ for (let index = 0; index < MASKS.length; index++) {
297
+
298
+ /** @type {Beta.JwtManager.KeyEntry.AlgorithmName} */
299
+ const algorithm = `HS${MASKS[index]}`;
300
+ /** @type {Beta.JwtManager.KeyEntry.AlgorithmName} */
301
+ const algorithmRS = `RS${MASKS[index]}`;
302
+ /** @type {Beta.JwtManager.KeyEntry.AlgorithmName} */
303
+ const algorithmPS = `PS${MASKS[index]}`;
304
+ /** @type {Beta.JwtManager.KeyEntry.AlgorithmName} */
305
+ const algorithmES = `ES${MASKS[index]}`;
306
+
307
+ testValidSignVerify(algorithm, SECRET);
308
+ testTamperedPayload(algorithm, SECRET);
309
+ testExpiredToken(algorithm, SECRET);
310
+ testMissingKey(algorithm, SECRET);
311
+ testKeyRotation(algorithm, SECRET);
312
+
313
+ testValidSignVerify(algorithmRS, RSA_KEY);
314
+ testTamperedPayload(algorithmRS, RSA_KEY);
315
+ testExpiredToken(algorithmRS, RSA_KEY);
316
+ testMissingKey(algorithmRS, RSA_KEY);
317
+ testReadOnlyPublicKey(algorithmRS, RSA_KEY);
318
+
319
+
320
+ testValidSignVerify(algorithmPS, PSS_KEY);
321
+ testTamperedPayload(algorithmPS, PSS_KEY);
322
+ testExpiredToken(algorithmPS, PSS_KEY);
323
+ testReadOnlyPublicKey(algorithmPS, PSS_KEY);
324
+
325
+ testValidSignVerify(algorithmES, EC_KEY);
326
+ testTamperedPayload(algorithmES, EC_KEY);
327
+ testExpiredToken(algorithmES, EC_KEY);
328
+ testReadOnlyPublicKey(algorithmES, EC_KEY);
329
+
330
+ }
331
+
332
+ testAlgorithmConfusionAttack();
333
+
334
+ logger.log(`\n&C6=== Results ===`);
335
+ logger.log(`&C2✓ Passed: ${testsPassed}`);
336
+ logger.log(`&C1✗ Failed: ${testsFailed}`);
337
+ logger.log(`&C3Total: ${testsPassed + testsFailed}\n`);
338
+
339
+ process.exit(testsFailed > 0 ? 1 : 0);
340
+ };
77
341
 
78
- runTests();
342
+ runTests();