@teleporthq/teleport-plugin-next-workflows 0.44.6 → 0.44.8

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 (67) hide show
  1. package/__tests__/credential-columns-reply.test.ts +286 -0
  2. package/__tests__/credential-result-reply.test.ts +509 -0
  3. package/dist/cjs/api-route-generator.d.ts.map +1 -1
  4. package/dist/cjs/api-route-generator.js +6 -2
  5. package/dist/cjs/api-route-generator.js.map +1 -1
  6. package/dist/cjs/credential-columns-code.d.ts +17 -0
  7. package/dist/cjs/credential-columns-code.d.ts.map +1 -0
  8. package/dist/cjs/credential-columns-code.js +61 -0
  9. package/dist/cjs/credential-columns-code.js.map +1 -0
  10. package/dist/cjs/credential-results.d.ts +18 -0
  11. package/dist/cjs/credential-results.d.ts.map +1 -0
  12. package/dist/cjs/credential-results.js +72 -0
  13. package/dist/cjs/credential-results.js.map +1 -0
  14. package/dist/cjs/data-api-route-generator.d.ts +1 -0
  15. package/dist/cjs/data-api-route-generator.d.ts.map +1 -1
  16. package/dist/cjs/data-api-route-generator.js +13 -5
  17. package/dist/cjs/data-api-route-generator.js.map +1 -1
  18. package/dist/cjs/executor-generator.d.ts.map +1 -1
  19. package/dist/cjs/executor-generator.js +20 -3
  20. package/dist/cjs/executor-generator.js.map +1 -1
  21. package/dist/cjs/nodes/account/account-hash-password.d.ts.map +1 -1
  22. package/dist/cjs/nodes/account/account-hash-password.js +1 -0
  23. package/dist/cjs/nodes/account/account-hash-password.js.map +1 -1
  24. package/dist/cjs/nodes/types.d.ts +6 -0
  25. package/dist/cjs/nodes/types.d.ts.map +1 -1
  26. package/dist/cjs/server-runtime-code.d.ts +8 -0
  27. package/dist/cjs/server-runtime-code.d.ts.map +1 -1
  28. package/dist/cjs/server-runtime-code.js +106 -0
  29. package/dist/cjs/server-runtime-code.js.map +1 -1
  30. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  31. package/dist/esm/api-route-generator.d.ts.map +1 -1
  32. package/dist/esm/api-route-generator.js +6 -2
  33. package/dist/esm/api-route-generator.js.map +1 -1
  34. package/dist/esm/credential-columns-code.d.ts +17 -0
  35. package/dist/esm/credential-columns-code.d.ts.map +1 -0
  36. package/dist/esm/credential-columns-code.js +57 -0
  37. package/dist/esm/credential-columns-code.js.map +1 -0
  38. package/dist/esm/credential-results.d.ts +18 -0
  39. package/dist/esm/credential-results.d.ts.map +1 -0
  40. package/dist/esm/credential-results.js +68 -0
  41. package/dist/esm/credential-results.js.map +1 -0
  42. package/dist/esm/data-api-route-generator.d.ts +1 -0
  43. package/dist/esm/data-api-route-generator.d.ts.map +1 -1
  44. package/dist/esm/data-api-route-generator.js +11 -3
  45. package/dist/esm/data-api-route-generator.js.map +1 -1
  46. package/dist/esm/executor-generator.d.ts.map +1 -1
  47. package/dist/esm/executor-generator.js +20 -3
  48. package/dist/esm/executor-generator.js.map +1 -1
  49. package/dist/esm/nodes/account/account-hash-password.d.ts.map +1 -1
  50. package/dist/esm/nodes/account/account-hash-password.js +1 -0
  51. package/dist/esm/nodes/account/account-hash-password.js.map +1 -1
  52. package/dist/esm/nodes/types.d.ts +6 -0
  53. package/dist/esm/nodes/types.d.ts.map +1 -1
  54. package/dist/esm/server-runtime-code.d.ts +8 -0
  55. package/dist/esm/server-runtime-code.d.ts.map +1 -1
  56. package/dist/esm/server-runtime-code.js +106 -0
  57. package/dist/esm/server-runtime-code.js.map +1 -1
  58. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  59. package/package.json +3 -3
  60. package/src/api-route-generator.ts +6 -2
  61. package/src/credential-columns-code.ts +59 -0
  62. package/src/credential-results.ts +71 -0
  63. package/src/data-api-route-generator.ts +11 -3
  64. package/src/executor-generator.ts +20 -3
  65. package/src/nodes/account/account-hash-password.ts +1 -0
  66. package/src/nodes/types.ts +6 -0
  67. package/src/server-runtime-code.ts +106 -0
@@ -27,6 +27,14 @@
27
27
  * that needs a trustworthy figure (a price, an owner) reads it from the
28
28
  * database itself.
29
29
  *
30
+ * A result that is a credential (a password hash, see credential-results.ts)
31
+ * makes the same trip SEALED: `segmentReply` replies it encrypted with a key
32
+ * derived from NEXTAUTH_SECRET (AES-256-GCM) and signs the sealed form, and
33
+ * `claimSegmentContext` opens every sealed value a request hands it (a
34
+ * relayed result once its signature holds). The browser relays it without
35
+ * ever reading it, and a later segment's reply hands back the sealed form,
36
+ * never the opened one.
37
+ *
30
38
  * The facts about the graph each route needs are baked into its
31
39
  * SEGMENT_CONFIG at generation time (see segment-trust.ts).
32
40
  */
@@ -51,6 +59,59 @@ function mac(text) {
51
59
  return crypto.createHmac('sha256', stepKey()).update(text).digest('hex');
52
60
  }
53
61
 
62
+ var __sealKeyCache = { secret: null, key: null };
63
+
64
+ function sealKey() {
65
+ var secret = process.env.NEXTAUTH_SECRET || '';
66
+ if (__sealKeyCache.secret !== secret) {
67
+ __sealKeyCache = {
68
+ secret: secret,
69
+ key: crypto.createHmac('sha256', secret).update('teleport-workflow-seal:v1').digest()
70
+ };
71
+ }
72
+ return __sealKeyCache.key;
73
+ }
74
+
75
+ var SEAL_IV_BYTES = 12;
76
+ var SEAL_TAG_BYTES = 16;
77
+
78
+ var SEAL_AAD = Buffer.from('teleport-workflow-sealed-result:v1', 'utf8');
79
+
80
+ // The sealed form of a credential result; undefined (the result is then not
81
+ // replied at all) without an app secret, whose key anyone could derive.
82
+ function sealResult(value) {
83
+ if (!process.env.NEXTAUTH_SECRET) return undefined;
84
+ var serialized;
85
+ try { serialized = JSON.stringify(value); } catch (e) { return undefined; }
86
+ if (serialized === undefined) return undefined;
87
+ var iv = crypto.randomBytes(SEAL_IV_BYTES);
88
+ var cipher = crypto.createCipheriv('aes-256-gcm', sealKey(), iv);
89
+ cipher.setAAD(SEAL_AAD);
90
+ var data = Buffer.concat([cipher.update(serialized, 'utf8'), cipher.final()]);
91
+ return { __sealed: Buffer.concat([iv, cipher.getAuthTag(), data]).toString('base64') };
92
+ }
93
+
94
+ function isSealedResult(value) {
95
+ return !!value && typeof value === 'object' && !Array.isArray(value) &&
96
+ typeof value.__sealed === 'string' && Object.keys(value).length === 1;
97
+ }
98
+
99
+ // { value } when the sealed form opens under this app's key, else null.
100
+ function openSealedResult(sealed) {
101
+ if (!process.env.NEXTAUTH_SECRET) return null;
102
+ try {
103
+ var raw = Buffer.from(sealed.__sealed, 'base64');
104
+ if (raw.length <= SEAL_IV_BYTES + SEAL_TAG_BYTES) return null;
105
+ var decipher = crypto.createDecipheriv('aes-256-gcm', sealKey(), raw.subarray(0, SEAL_IV_BYTES));
106
+ decipher.setAAD(SEAL_AAD);
107
+ decipher.setAuthTag(raw.subarray(SEAL_IV_BYTES, SEAL_IV_BYTES + SEAL_TAG_BYTES));
108
+ var data = Buffer.concat([decipher.update(raw.subarray(SEAL_IV_BYTES + SEAL_TAG_BYTES)), decipher.final()]);
109
+ return { value: JSON.parse(data.toString('utf8')) };
110
+ } catch (e) {
111
+ return null;
112
+ }
113
+ }
114
+
54
115
  // Without an app secret every key here would derive from '' — a value anyone
55
116
  // can compute — so nothing verifies: relayed server results are dropped and no
56
117
  // request passes as an internal call. (The generator gives every app with
@@ -206,10 +267,48 @@ function resetPreviousResult(segmentConfig, context) {
206
267
  // merge will.
207
268
  var __incomingOrders = new WeakMap();
208
269
 
270
+ // The sealed results this segment opened, so its reply hands the caller back
271
+ // the sealed form it relayed (see resealRelayedResults).
272
+ var __openedResults = new WeakMap();
273
+
274
+ // Opens every sealed value the request hands this segment, so its nodes read
275
+ // the credential itself: a relayed server result (whose signature
276
+ // dropUnsignedResults checked first), or a value the browser holds from one
277
+ // (a custom node's output, the previous result). Only this app's server
278
+ // seals, so what opens is what it sealed.
279
+ function openSealedResults(context) {
280
+ var opened = [];
281
+ Object.keys(context).forEach(function(key) {
282
+ if (isControlKey(key) && key !== '__previousNodeResult') return;
283
+ if (!isSealedResult(context[key])) return;
284
+ var result = openSealedResult(context[key]);
285
+ if (!result) return;
286
+ opened.push({ sealed: context[key], value: result.value });
287
+ context[key] = result.value;
288
+ });
289
+ __openedResults.set(context, opened);
290
+ }
291
+
292
+ // Every reply entry that is an opened value (the value itself, or the previous
293
+ // result taken from it) goes back sealed.
294
+ function resealRelayedResults(context, reply) {
295
+ var opened = __openedResults.get(context) || [];
296
+ if (opened.length === 0) return;
297
+ Object.keys(reply).forEach(function(key) {
298
+ for (var i = 0; i < opened.length; i++) {
299
+ if (reply[key] === opened[i].value) {
300
+ reply[key] = opened[i].sealed;
301
+ return;
302
+ }
303
+ }
304
+ });
305
+ }
306
+
209
307
  function claimSegmentContext(segmentConfig, context) {
210
308
  var orderKeys = readSignedOrder(context);
211
309
  if (orderKeys) restoreOrder(context, orderKeys);
212
310
  dropUnsignedResults(segmentConfig, context, orderKeys);
311
+ openSealedResults(context);
213
312
  stripForeignControlKeys(segmentConfig, context);
214
313
  __incomingOrders.set(context, Object.keys(context).filter(function(k) { return !isControlKey(k); }));
215
314
  utils.claimSegmentContext(segmentConfig, context);
@@ -219,6 +318,13 @@ function claimSegmentContext(segmentConfig, context) {
219
318
 
220
319
  function segmentReply(segmentConfig, context, snapshot) {
221
320
  var reply = utils.segmentReply(segmentConfig, context, snapshot);
321
+ resealRelayedResults(context, reply);
322
+ var credentialIds = utils.credentialResultNodeIds(segmentConfig);
323
+ Object.keys(credentialIds).forEach(function(id) {
324
+ if (context[id] === undefined) return;
325
+ var sealed = sealResult(context[id]);
326
+ if (sealed) reply[id] = sealed;
327
+ });
222
328
  var sigs = {};
223
329
  var incoming = context.__sig && typeof context.__sig === 'object' && !Array.isArray(context.__sig) ? context.__sig : {};
224
330
  Object.keys(incoming).forEach(function(id) {
@@ -1 +1 @@
1
- {"version":3,"file":"server-runtime-code.js","sourceRoot":"","sources":["../../src/server-runtime-code.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAW,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyQtD,CAAA"}
1
+ {"version":3,"file":"server-runtime-code.js","sourceRoot":"","sources":["../../src/server-runtime-code.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAW,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2WtD,CAAA"}