@tellescope/sdk 1.255.18 → 1.256.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 (165) hide show
  1. package/lib/cjs/sdk.d.ts +7 -2
  2. package/lib/cjs/sdk.d.ts.map +1 -1
  3. package/lib/cjs/sdk.js +1 -0
  4. package/lib/cjs/sdk.js.map +1 -1
  5. package/lib/cjs/tests/api_tests/auto_merge_form_submission.test.d.ts.map +1 -1
  6. package/lib/cjs/tests/api_tests/auto_merge_form_submission.test.js +18 -8
  7. package/lib/cjs/tests/api_tests/auto_merge_form_submission.test.js.map +1 -1
  8. package/lib/cjs/tests/api_tests/custom_dashboards.test.d.ts.map +1 -1
  9. package/lib/cjs/tests/api_tests/custom_dashboards.test.js +99 -8
  10. package/lib/cjs/tests/api_tests/custom_dashboards.test.js.map +1 -1
  11. package/lib/cjs/tests/api_tests/default_summary_journey.test.d.ts.map +1 -0
  12. package/lib/cjs/tests/api_tests/default_summary_journey.test.js.map +1 -0
  13. package/lib/cjs/tests/api_tests/file_download_unicode_names.test.d.ts +6 -0
  14. package/lib/cjs/tests/api_tests/file_download_unicode_names.test.d.ts.map +1 -0
  15. package/lib/cjs/tests/api_tests/file_download_unicode_names.test.js +278 -0
  16. package/lib/cjs/tests/api_tests/file_download_unicode_names.test.js.map +1 -0
  17. package/lib/cjs/tests/api_tests/healthie_multi_integration.test.d.ts.map +1 -1
  18. package/lib/cjs/tests/api_tests/healthie_multi_integration.test.js +203 -72
  19. package/lib/cjs/tests/api_tests/healthie_multi_integration.test.js.map +1 -1
  20. package/lib/cjs/tests/api_tests/openloop_webhooks.test.d.ts.map +1 -1
  21. package/lib/cjs/tests/api_tests/openloop_webhooks.test.js +212 -33
  22. package/lib/cjs/tests/api_tests/openloop_webhooks.test.js.map +1 -1
  23. package/lib/cjs/tests/api_tests/phone_calls_cold_transfer.test.d.ts.map +1 -1
  24. package/lib/cjs/tests/api_tests/phone_calls_cold_transfer.test.js +85 -9
  25. package/lib/cjs/tests/api_tests/phone_calls_cold_transfer.test.js.map +1 -1
  26. package/lib/cjs/tests/api_tests/phone_calls_conference_hold.test.d.ts +6 -0
  27. package/lib/cjs/tests/api_tests/phone_calls_conference_hold.test.d.ts.map +1 -0
  28. package/lib/cjs/tests/api_tests/phone_calls_conference_hold.test.js +102 -0
  29. package/lib/cjs/tests/api_tests/phone_calls_conference_hold.test.js.map +1 -0
  30. package/lib/cjs/tests/api_tests/scoped_api_keys.test.d.ts.map +1 -1
  31. package/lib/cjs/tests/api_tests/scoped_api_keys.test.js +84 -81
  32. package/lib/cjs/tests/api_tests/scoped_api_keys.test.js.map +1 -1
  33. package/lib/cjs/tests/api_tests/security/F-0013-sanitize-user-html-with-iframes.test.d.ts +2 -0
  34. package/lib/cjs/tests/api_tests/security/F-0013-sanitize-user-html-with-iframes.test.d.ts.map +1 -0
  35. package/lib/cjs/tests/api_tests/security/F-0013-sanitize-user-html-with-iframes.test.js +242 -0
  36. package/lib/cjs/tests/api_tests/security/F-0013-sanitize-user-html-with-iframes.test.js.map +1 -0
  37. package/lib/cjs/tests/api_tests/security/F-0015-webhook-outbound-ssrf.test.d.ts +29 -0
  38. package/lib/cjs/tests/api_tests/security/F-0015-webhook-outbound-ssrf.test.d.ts.map +1 -0
  39. package/lib/cjs/tests/api_tests/security/F-0015-webhook-outbound-ssrf.test.js +443 -0
  40. package/lib/cjs/tests/api_tests/security/F-0015-webhook-outbound-ssrf.test.js.map +1 -0
  41. package/lib/cjs/tests/api_tests/security/F-0025-proxy-image-unauth-ssrf.test.d.ts +35 -0
  42. package/lib/cjs/tests/api_tests/security/F-0025-proxy-image-unauth-ssrf.test.d.ts.map +1 -0
  43. package/lib/cjs/tests/api_tests/security/F-0025-proxy-image-unauth-ssrf.test.js +231 -0
  44. package/lib/cjs/tests/api_tests/security/F-0025-proxy-image-unauth-ssrf.test.js.map +1 -0
  45. package/lib/cjs/tests/api_tests/security/F-0106-F-0110-enduser-write-restrictions.test.d.ts.map +1 -1
  46. package/lib/cjs/tests/api_tests/security/F-0106-F-0110-enduser-write-restrictions.test.js +121 -78
  47. package/lib/cjs/tests/api_tests/security/F-0106-F-0110-enduser-write-restrictions.test.js.map +1 -1
  48. package/lib/cjs/tests/api_tests/security/F-0166-allowed-paths-wildcard-bypass.test.d.ts +6 -0
  49. package/lib/cjs/tests/api_tests/security/F-0166-allowed-paths-wildcard-bypass.test.d.ts.map +1 -0
  50. package/lib/cjs/tests/api_tests/security/F-0166-allowed-paths-wildcard-bypass.test.js +1003 -0
  51. package/lib/cjs/tests/api_tests/security/F-0166-allowed-paths-wildcard-bypass.test.js.map +1 -0
  52. package/lib/cjs/tests/api_tests/security/enduser-ai-summary-trust-gate.test.d.ts +17 -0
  53. package/lib/cjs/tests/api_tests/security/enduser-ai-summary-trust-gate.test.d.ts.map +1 -0
  54. package/lib/cjs/tests/api_tests/security/enduser-ai-summary-trust-gate.test.js +215 -0
  55. package/lib/cjs/tests/api_tests/security/enduser-ai-summary-trust-gate.test.js.map +1 -0
  56. package/lib/cjs/tests/api_tests/security/formsort_webhook_auth_field_lock.test.d.ts +6 -0
  57. package/lib/cjs/tests/api_tests/security/formsort_webhook_auth_field_lock.test.d.ts.map +1 -0
  58. package/lib/cjs/tests/api_tests/security/formsort_webhook_auth_field_lock.test.js +1200 -0
  59. package/lib/cjs/tests/api_tests/security/formsort_webhook_auth_field_lock.test.js.map +1 -0
  60. package/lib/cjs/tests/api_tests/security/public_endpoint_auth_field_lock.test.d.ts +6 -0
  61. package/lib/cjs/tests/api_tests/security/public_endpoint_auth_field_lock.test.d.ts.map +1 -0
  62. package/lib/cjs/tests/api_tests/security/public_endpoint_auth_field_lock.test.js +2330 -0
  63. package/lib/cjs/tests/api_tests/security/public_endpoint_auth_field_lock.test.js.map +1 -0
  64. package/lib/cjs/tests/api_tests/tickets_bulk_assign_care_team.test.d.ts +6 -0
  65. package/lib/cjs/tests/api_tests/tickets_bulk_assign_care_team.test.d.ts.map +1 -0
  66. package/lib/cjs/tests/api_tests/tickets_bulk_assign_care_team.test.js +204 -0
  67. package/lib/cjs/tests/api_tests/tickets_bulk_assign_care_team.test.js.map +1 -0
  68. package/lib/cjs/tests/tests.d.ts.map +1 -1
  69. package/lib/cjs/tests/tests.js +489 -255
  70. package/lib/cjs/tests/tests.js.map +1 -1
  71. package/lib/esm/enduser.d.ts +0 -1
  72. package/lib/esm/enduser.d.ts.map +1 -1
  73. package/lib/esm/sdk.d.ts +9 -5
  74. package/lib/esm/sdk.d.ts.map +1 -1
  75. package/lib/esm/sdk.js +1 -0
  76. package/lib/esm/sdk.js.map +1 -1
  77. package/lib/esm/session.d.ts +0 -1
  78. package/lib/esm/session.d.ts.map +1 -1
  79. package/lib/esm/tests/api_tests/auto_merge_form_submission.test.d.ts.map +1 -1
  80. package/lib/esm/tests/api_tests/auto_merge_form_submission.test.js +18 -8
  81. package/lib/esm/tests/api_tests/auto_merge_form_submission.test.js.map +1 -1
  82. package/lib/esm/tests/api_tests/custom_dashboards.test.d.ts.map +1 -1
  83. package/lib/esm/tests/api_tests/custom_dashboards.test.js +99 -8
  84. package/lib/esm/tests/api_tests/custom_dashboards.test.js.map +1 -1
  85. package/lib/esm/tests/api_tests/default_summary_journey.test.d.ts.map +1 -0
  86. package/lib/esm/tests/api_tests/default_summary_journey.test.js.map +1 -0
  87. package/lib/esm/tests/api_tests/file_download_unicode_names.test.d.ts +6 -0
  88. package/lib/esm/tests/api_tests/file_download_unicode_names.test.d.ts.map +1 -0
  89. package/lib/esm/tests/api_tests/file_download_unicode_names.test.js +248 -0
  90. package/lib/esm/tests/api_tests/file_download_unicode_names.test.js.map +1 -0
  91. package/lib/esm/tests/api_tests/healthie_multi_integration.test.d.ts.map +1 -1
  92. package/lib/esm/tests/api_tests/healthie_multi_integration.test.js +205 -74
  93. package/lib/esm/tests/api_tests/healthie_multi_integration.test.js.map +1 -1
  94. package/lib/esm/tests/api_tests/openloop_webhooks.test.d.ts.map +1 -1
  95. package/lib/esm/tests/api_tests/openloop_webhooks.test.js +213 -34
  96. package/lib/esm/tests/api_tests/openloop_webhooks.test.js.map +1 -1
  97. package/lib/esm/tests/api_tests/phone_calls_cold_transfer.test.d.ts.map +1 -1
  98. package/lib/esm/tests/api_tests/phone_calls_cold_transfer.test.js +85 -9
  99. package/lib/esm/tests/api_tests/phone_calls_cold_transfer.test.js.map +1 -1
  100. package/lib/esm/tests/api_tests/phone_calls_conference_hold.test.d.ts +6 -0
  101. package/lib/esm/tests/api_tests/phone_calls_conference_hold.test.d.ts.map +1 -0
  102. package/lib/esm/tests/api_tests/phone_calls_conference_hold.test.js +98 -0
  103. package/lib/esm/tests/api_tests/phone_calls_conference_hold.test.js.map +1 -0
  104. package/lib/esm/tests/api_tests/scoped_api_keys.test.d.ts.map +1 -1
  105. package/lib/esm/tests/api_tests/scoped_api_keys.test.js +84 -81
  106. package/lib/esm/tests/api_tests/scoped_api_keys.test.js.map +1 -1
  107. package/lib/esm/tests/api_tests/security/F-0013-sanitize-user-html-with-iframes.test.d.ts +2 -0
  108. package/lib/esm/tests/api_tests/security/F-0013-sanitize-user-html-with-iframes.test.d.ts.map +1 -0
  109. package/lib/esm/tests/api_tests/security/F-0013-sanitize-user-html-with-iframes.test.js +238 -0
  110. package/lib/esm/tests/api_tests/security/F-0013-sanitize-user-html-with-iframes.test.js.map +1 -0
  111. package/lib/esm/tests/api_tests/security/F-0015-webhook-outbound-ssrf.test.d.ts +29 -0
  112. package/lib/esm/tests/api_tests/security/F-0015-webhook-outbound-ssrf.test.d.ts.map +1 -0
  113. package/lib/esm/tests/api_tests/security/F-0015-webhook-outbound-ssrf.test.js +436 -0
  114. package/lib/esm/tests/api_tests/security/F-0015-webhook-outbound-ssrf.test.js.map +1 -0
  115. package/lib/esm/tests/api_tests/security/F-0025-proxy-image-unauth-ssrf.test.d.ts +35 -0
  116. package/lib/esm/tests/api_tests/security/F-0025-proxy-image-unauth-ssrf.test.d.ts.map +1 -0
  117. package/lib/esm/tests/api_tests/security/F-0025-proxy-image-unauth-ssrf.test.js +224 -0
  118. package/lib/esm/tests/api_tests/security/F-0025-proxy-image-unauth-ssrf.test.js.map +1 -0
  119. package/lib/esm/tests/api_tests/security/F-0106-F-0110-enduser-write-restrictions.test.d.ts.map +1 -1
  120. package/lib/esm/tests/api_tests/security/F-0106-F-0110-enduser-write-restrictions.test.js +121 -78
  121. package/lib/esm/tests/api_tests/security/F-0106-F-0110-enduser-write-restrictions.test.js.map +1 -1
  122. package/lib/esm/tests/api_tests/security/F-0166-allowed-paths-wildcard-bypass.test.d.ts +6 -0
  123. package/lib/esm/tests/api_tests/security/F-0166-allowed-paths-wildcard-bypass.test.d.ts.map +1 -0
  124. package/lib/esm/tests/api_tests/security/F-0166-allowed-paths-wildcard-bypass.test.js +996 -0
  125. package/lib/esm/tests/api_tests/security/F-0166-allowed-paths-wildcard-bypass.test.js.map +1 -0
  126. package/lib/esm/tests/api_tests/security/enduser-ai-summary-trust-gate.test.d.ts +17 -0
  127. package/lib/esm/tests/api_tests/security/enduser-ai-summary-trust-gate.test.d.ts.map +1 -0
  128. package/lib/esm/tests/api_tests/security/enduser-ai-summary-trust-gate.test.js +211 -0
  129. package/lib/esm/tests/api_tests/security/enduser-ai-summary-trust-gate.test.js.map +1 -0
  130. package/lib/esm/tests/api_tests/security/formsort_webhook_auth_field_lock.test.d.ts +6 -0
  131. package/lib/esm/tests/api_tests/security/formsort_webhook_auth_field_lock.test.d.ts.map +1 -0
  132. package/lib/esm/tests/api_tests/security/formsort_webhook_auth_field_lock.test.js +1193 -0
  133. package/lib/esm/tests/api_tests/security/formsort_webhook_auth_field_lock.test.js.map +1 -0
  134. package/lib/esm/tests/api_tests/security/public_endpoint_auth_field_lock.test.d.ts +6 -0
  135. package/lib/esm/tests/api_tests/security/public_endpoint_auth_field_lock.test.d.ts.map +1 -0
  136. package/lib/esm/tests/api_tests/security/public_endpoint_auth_field_lock.test.js +2323 -0
  137. package/lib/esm/tests/api_tests/security/public_endpoint_auth_field_lock.test.js.map +1 -0
  138. package/lib/esm/tests/api_tests/tickets_bulk_assign_care_team.test.d.ts +6 -0
  139. package/lib/esm/tests/api_tests/tickets_bulk_assign_care_team.test.d.ts.map +1 -0
  140. package/lib/esm/tests/api_tests/tickets_bulk_assign_care_team.test.js +200 -0
  141. package/lib/esm/tests/api_tests/tickets_bulk_assign_care_team.test.js.map +1 -0
  142. package/lib/esm/tests/tests.d.ts.map +1 -1
  143. package/lib/esm/tests/tests.js +489 -255
  144. package/lib/esm/tests/tests.js.map +1 -1
  145. package/lib/tsconfig.tsbuildinfo +1 -1
  146. package/package.json +10 -10
  147. package/src/sdk.ts +4 -0
  148. package/src/tests/api_tests/auto_merge_form_submission.test.ts +13 -4
  149. package/src/tests/api_tests/custom_dashboards.test.ts +92 -0
  150. package/src/tests/api_tests/file_download_unicode_names.test.ts +223 -0
  151. package/src/tests/api_tests/healthie_multi_integration.test.ts +103 -5
  152. package/src/tests/api_tests/openloop_webhooks.test.ts +138 -0
  153. package/src/tests/api_tests/phone_calls_cold_transfer.test.ts +54 -0
  154. package/src/tests/api_tests/phone_calls_conference_hold.test.ts +62 -0
  155. package/src/tests/api_tests/scoped_api_keys.test.ts +5 -0
  156. package/src/tests/api_tests/security/F-0013-sanitize-user-html-with-iframes.test.ts +215 -0
  157. package/src/tests/api_tests/security/F-0106-F-0110-enduser-write-restrictions.test.ts +38 -4
  158. package/src/tests/api_tests/security/F-0166-allowed-paths-wildcard-bypass.test.ts +721 -0
  159. package/src/tests/api_tests/security/enduser-ai-summary-trust-gate.test.ts +125 -0
  160. package/src/tests/api_tests/security/formsort_webhook_auth_field_lock.test.ts +753 -0
  161. package/src/tests/api_tests/security/public_endpoint_auth_field_lock.test.ts +1707 -0
  162. package/src/tests/api_tests/tickets_bulk_assign_care_team.test.ts +132 -0
  163. package/src/tests/tests.ts +232 -2
  164. package/test_generated.pdf +0 -0
  165. package/.env +0 -13
@@ -0,0 +1,996 @@
1
+ var __assign = (this && this.__assign) || function () {
2
+ __assign = Object.assign || function(t) {
3
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
4
+ s = arguments[i];
5
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
+ t[p] = s[p];
7
+ }
8
+ return t;
9
+ };
10
+ return __assign.apply(this, arguments);
11
+ };
12
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
13
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
14
+ return new (P || (P = Promise))(function (resolve, reject) {
15
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
16
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
17
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
18
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
19
+ });
20
+ };
21
+ var __generator = (this && this.__generator) || function (thisArg, body) {
22
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
23
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
24
+ function verb(n) { return function (v) { return step([n, v]); }; }
25
+ function step(op) {
26
+ if (f) throw new TypeError("Generator is already executing.");
27
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
28
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
29
+ if (y = 0, t) op = [op[0] & 2, t.value];
30
+ switch (op[0]) {
31
+ case 0: case 1: t = op; break;
32
+ case 4: _.label++; return { value: op[1], done: false };
33
+ case 5: _.label++; y = op[1]; op = [0]; continue;
34
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
35
+ default:
36
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
37
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
38
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
39
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
40
+ if (t[2]) _.ops.pop();
41
+ _.trys.pop(); continue;
42
+ }
43
+ op = body.call(thisArg, _);
44
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
45
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
46
+ }
47
+ };
48
+ require('source-map-support').install();
49
+ import axios from "axios";
50
+ import { Session, EnduserSession } from "../../../sdk";
51
+ import { assert, log_header, } from "@tellescope/testing";
52
+ import { setup_tests } from "../../setup";
53
+ var host = process.env.API_URL || 'http://localhost:8080';
54
+ /**
55
+ * Regression test for F-0166 — allowedPaths wildcard disables path confinement entirely.
56
+ *
57
+ * Several public, unauthenticated endpoints mint a JWT confined to a small set of API paths via an
58
+ * `allowedPaths` claim. The enforcement predicate (api/modules/authentication.ts checkAccess)
59
+ * contained:
60
+ *
61
+ * || (p.endsWith('*') && req.path.split('/')[0] === p.split('/')[0])
62
+ *
63
+ * Every request path begins with '/', so req.path.split('/')[0] is ''. Every wildcard entry also
64
+ * begins with '/', so p.split('/')[0] is ''. The clause reduces to '' === '' — always true. Any
65
+ * allowedPaths array containing a single '*' entry therefore matched EVERY request path and the
66
+ * 403 never fired.
67
+ *
68
+ * Two mint sites contain `/meeting/*`, and both are public and unauthenticated:
69
+ * - session_for_join_link — enduser session, MFA bypassed
70
+ * - session_for_start_link — full STAFF session with mfa: false
71
+ *
72
+ * So an unauthenticated caller holding a link token had an effectively unrestricted session.
73
+ *
74
+ * Fixing that exposed a second defect in the same primitive: entries matched by bare string prefix
75
+ * with no notion of HTTP method, so '/users' and '/endusers' were collection-NAMESPACE grants (22
76
+ * and 21 routes each). The worst consequence was a full escape — PATCH /users/consent called
77
+ * login_user on a freshly-read DB record with no options, minting a token with no allowedPaths at
78
+ * all, so two requests turned a start link into an unconfined staff session.
79
+ *
80
+ * Fix, in two parts:
81
+ * - entries are now method-scoped and exact ('get:/endusers'), with ':param' for segments not
82
+ * knowable at mint time ('get:/meeting/:id'). Un-prefixed and '*' entries remain honoured only
83
+ * so tokens minted before this grammar keep working until they expire.
84
+ * - consent / configure_inbox / switch_account forward req.session.allowedPaths into login_user.
85
+ *
86
+ * Scenarios:
87
+ * S1 public-form session — no wildcard; correctly confined throughout (regression anchor)
88
+ * S2 join-link session — RED before the wildcard fix
89
+ * S3 start-link session — RED before the wildcard fix (staff session; the severe case)
90
+ * S4 side-effects of the /calendar-events entry
91
+ * S6 namespace grants and the privilege-escalation chain — RED before method-scoped exact matching
92
+ * S5 ordinary sessions with no allowedPaths claim, incl. headless API-key callers, are unaffected
93
+ */
94
+ var RAND = function () { return Math.random().toString(36).slice(2, 10); };
95
+ var OBJECT_ID = '68a1b2c3d4e5f60718293a4b';
96
+ export var allowed_paths_confinement_tests = function (_a) {
97
+ var sdk = _a.sdk;
98
+ return __awaiter(void 0, void 0, void 0, function () {
99
+ var probes, request, isPathDenial, expectDenied, expectAllowed, formId, productId, joinEnduserId, formEnduserEmail, hostUserId, templateId, bookingEnduserId, eventIds, businessId, form, publicFormSession, product, joinEnduser, joinLinkToken, joinEvent, unrelatedEvent, joinSession, hostUser, startLinkToken, startEvent, startSession, baselineEnduserToken, baselineUserToken, S1, formEnduserId, publicRefresh, refreshedEnduser_1, withheld, consentEcho, echoedKeys, leakedKeys, fieldsThatMustBeRefused, _i, fieldsThatMustBeRefused_1, _b, field, value, r, mixed, afterProbes, S2, attendanceWrite, echoedEvent, refreshedJoin, refreshedJoinToken, S3, refreshedStart, refreshedStartToken, S4, prefixGranted, _c, prefixGranted_1, _d, method, path, r, S6, ABSENT_ID_A, ABSENT_ID_B, INVALID_EMAIL, consentAttempt, refreshedForScopes, rescopedToken, S7, bookingTemplate, bookingEnduserEmail, bookingEnduser, bookingSession, bookingEcho, bookingLeaked, _e, _f, _g, field, value, r, S5, _h, _j, path, _k, _l, path, adminAuthenticated, apiKeySDK, _m, _o, path, r, failures, scenarios, _loop_1, _p, scenarios_1, scenario, _q, probes_1, p, _r, eventIds_1, id, _s, _t, e, _u, _v, _w, _x, _y, _z;
100
+ var _0, _1;
101
+ var _2, _3, _4, _5, _6, _7, _8, _9, _10;
102
+ return __generator(this, function (_11) {
103
+ switch (_11.label) {
104
+ case 0:
105
+ log_header("F-0166: allowedPaths wildcard bypasses session path confinement");
106
+ probes = [];
107
+ request = function (token, method, path, data) { return __awaiter(void 0, void 0, void 0, function () {
108
+ var r;
109
+ return __generator(this, function (_a) {
110
+ switch (_a.label) {
111
+ case 0: return [4 /*yield*/, axios.request(__assign(__assign({ method: method, url: "".concat(host, "/v1").concat(path) }, method === 'get' ? { params: data } : { data: data }), { headers: { Authorization: "Bearer ".concat(token) }, validateStatus: function () { return true; } }))];
112
+ case 1:
113
+ r = _a.sent();
114
+ return [2 /*return*/, { status: r.status, body: r.data }];
115
+ }
116
+ });
117
+ }); };
118
+ isPathDenial = function (r) {
119
+ var _a;
120
+ return (r.status === 403 && ((_a = r.body) === null || _a === void 0 ? void 0 : _a.message) === 'Inaccessible');
121
+ };
122
+ expectDenied = function (scenario, label, confinedToken, baselineToken, method, path, data) { return __awaiter(void 0, void 0, void 0, function () {
123
+ var baseline, confined;
124
+ return __generator(this, function (_a) {
125
+ switch (_a.label) {
126
+ case 0: return [4 /*yield*/, request(baselineToken, method, path, data)];
127
+ case 1:
128
+ baseline = _a.sent();
129
+ return [4 /*yield*/, request(confinedToken, method, path, data)];
130
+ case 2:
131
+ confined = _a.sent();
132
+ probes.push({
133
+ scenario: scenario,
134
+ label: label,
135
+ path: path,
136
+ expected: 'denied',
137
+ status: confined.status,
138
+ baselineStatus: baseline.status,
139
+ pass: baseline.status !== 403 && isPathDenial(confined),
140
+ note: baseline.status === 403 ? 'BAD PROBE: baseline session also got 403' : undefined,
141
+ });
142
+ return [2 /*return*/];
143
+ }
144
+ });
145
+ }); };
146
+ expectAllowed = function (scenario, label, token, method, path, data, _a) {
147
+ var _b = _a === void 0 ? {} : _a, _c = _b.allow401, allow401 = _c === void 0 ? false : _c;
148
+ return __awaiter(void 0, void 0, void 0, function () {
149
+ var r;
150
+ return __generator(this, function (_d) {
151
+ switch (_d.label) {
152
+ case 0: return [4 /*yield*/, request(token, method, path, data)];
153
+ case 1:
154
+ r = _d.sent();
155
+ probes.push({
156
+ scenario: scenario,
157
+ label: label,
158
+ path: path,
159
+ expected: 'allowed',
160
+ status: r.status,
161
+ pass: r.status !== 403 && (allow401 || r.status !== 401),
162
+ note: allow401 && r.status === 401 ? '401 expected: enduser cannot authenticate this model' : undefined,
163
+ });
164
+ return [2 /*return*/];
165
+ }
166
+ });
167
+ });
168
+ };
169
+ eventIds = [];
170
+ _11.label = 1;
171
+ case 1:
172
+ _11.trys.push([1, , 123, 160]);
173
+ businessId = sdk.userInfo.businessId;
174
+ return [4 /*yield*/, sdk.api.forms.createOne({
175
+ title: "F-0166 path confinement ".concat(RAND()),
176
+ allowPublicURL: true,
177
+ })];
178
+ case 2:
179
+ form = _11.sent();
180
+ formId = form.id;
181
+ return [4 /*yield*/, sdk.api.form_fields.createOne({
182
+ formId: form.id,
183
+ title: 'q',
184
+ type: 'string',
185
+ previousFields: [{ type: 'root', info: {} }],
186
+ })];
187
+ case 3:
188
+ _11.sent();
189
+ formEnduserEmail = "f0166-form-".concat(RAND(), "@tellescope.example");
190
+ return [4 /*yield*/, new EnduserSession({ host: host, businessId: businessId })
191
+ .api.form_responses.session_for_public_form({
192
+ businessId: businessId,
193
+ formId: form.id,
194
+ email: formEnduserEmail,
195
+ fname: 'Path',
196
+ lname: 'Confinement',
197
+ skipMatch: true, // also skips the intake-phone requirement
198
+ })];
199
+ case 4:
200
+ publicFormSession = _11.sent();
201
+ return [4 /*yield*/, sdk.api.products.createOne({
202
+ title: "F-0166 product ".concat(RAND()),
203
+ cost: { amount: 500, currency: 'USD' },
204
+ processor: 'Stripe',
205
+ })];
206
+ case 5:
207
+ product = _11.sent();
208
+ productId = product.id;
209
+ return [4 /*yield*/, sdk.api.endusers.createOne({
210
+ email: "f0166-join-".concat(RAND(), "@tellescope.example"),
211
+ })];
212
+ case 6:
213
+ joinEnduser = _11.sent();
214
+ joinEnduserId = joinEnduser.id;
215
+ joinLinkToken = "f0166-join-".concat(RAND()).concat(RAND());
216
+ return [4 /*yield*/, sdk.api.calendar_events.createOne({
217
+ title: 'F-0166 join', durationInMinutes: 30, startTimeInMS: Date.now() + 3600000,
218
+ attendees: [{ type: 'enduser', id: joinEnduser.id, joinLinkToken: joinLinkToken }],
219
+ })];
220
+ case 7:
221
+ joinEvent = _11.sent();
222
+ eventIds.push(joinEvent.id);
223
+ return [4 /*yield*/, sdk.api.calendar_events.createOne({
224
+ title: 'F-0166 unrelated', durationInMinutes: 30, startTimeInMS: Date.now() + 3600000,
225
+ attendees: [{ type: 'enduser', id: joinEnduser.id }],
226
+ })];
227
+ case 8:
228
+ unrelatedEvent = _11.sent();
229
+ eventIds.push(unrelatedEvent.id);
230
+ return [4 /*yield*/, new EnduserSession({ host: host, businessId: businessId })
231
+ .api.calendar_events.session_for_join_link({ token: joinLinkToken })
232
+ // -- start link (STAFF session, allowedPaths contains /meeting/*)
233
+ // throwaway user; verifiedEmail must be set at create time (it is rejected on update)
234
+ ];
235
+ case 9:
236
+ joinSession = _11.sent();
237
+ return [4 /*yield*/, sdk.api.users.createOne({
238
+ email: "f0166-host-".concat(RAND(), "@tellescope.example"),
239
+ fname: 'Path', lname: 'Host',
240
+ verifiedEmail: true,
241
+ notificationEmailsDisabled: true,
242
+ })];
243
+ case 10:
244
+ hostUser = _11.sent();
245
+ hostUserId = hostUser.id;
246
+ startLinkToken = "f0166-start-".concat(RAND()).concat(RAND());
247
+ return [4 /*yield*/, sdk.api.calendar_events.createOne({
248
+ title: 'F-0166 start', durationInMinutes: 30, startTimeInMS: Date.now() + 3600000,
249
+ attendees: [{ type: 'user', id: hostUser.id }, { type: 'enduser', id: joinEnduser.id }],
250
+ startLinkToken: startLinkToken,
251
+ })];
252
+ case 11:
253
+ startEvent = _11.sent();
254
+ eventIds.push(startEvent.id);
255
+ return [4 /*yield*/, sdk.api.calendar_events.session_for_start_link({ token: startLinkToken })
256
+ // -- unconfined baselines of each session type (generate_auth_token, never .authenticate)
257
+ ];
258
+ case 12:
259
+ startSession = _11.sent();
260
+ return [4 /*yield*/, sdk.api.endusers.generate_auth_token({ id: joinEnduser.id })];
261
+ case 13:
262
+ baselineEnduserToken = (_11.sent()).authToken;
263
+ return [4 /*yield*/, sdk.api.users.generate_auth_token({ id: hostUser.id })
264
+ /* ============ S1: public-form session — no wildcard, correctly confined already ============ */
265
+ ];
266
+ case 14:
267
+ baselineUserToken = (_11.sent()).authToken;
268
+ S1 = 'S1 public-form';
269
+ return [4 /*yield*/, expectAllowed(S1, 'always-allowed path', publicFormSession.authToken, 'get', '/test-authenticated')];
270
+ case 15:
271
+ _11.sent();
272
+ return [4 /*yield*/, expectAllowed(S1, 'listed: form info for access code', publicFormSession.authToken, 'get', '/form-info-for-access-code', { accessCode: publicFormSession.accessCode })];
273
+ case 16:
274
+ _11.sent();
275
+ return [4 /*yield*/, expectAllowed(S1, 'listed: refresh enduser session', publicFormSession.authToken, 'post', '/refresh-enduser-session')];
276
+ case 17:
277
+ _11.sent();
278
+ return [4 /*yield*/, expectAllowed(S1, 'listed: product by id', publicFormSession.authToken, 'get', "/product/".concat(productId))];
279
+ case 18:
280
+ _11.sent();
281
+ return [4 /*yield*/, expectDenied(S1, 'unlisted: calendar events', publicFormSession.authToken, baselineEnduserToken, 'get', '/calendar-events')];
282
+ case 19:
283
+ _11.sent();
284
+ return [4 /*yield*/, expectDenied(S1, 'unlisted: form responses', publicFormSession.authToken, baselineEnduserToken, 'get', '/form-responses')];
285
+ case 20:
286
+ _11.sent();
287
+ return [4 /*yield*/, expectDenied(S1, 'unlisted: files', publicFormSession.authToken, baselineEnduserToken, 'get', '/files')
288
+ // 'get:/product' names one route, so it no longer leaks the whole /products namespace the way a
289
+ // bare '/product' prefix entry did — that used to reach /products/prepare-stripe-checkout too
290
+ ];
291
+ case 21:
292
+ _11.sent();
293
+ // 'get:/product' names one route, so it no longer leaks the whole /products namespace the way a
294
+ // bare '/product' prefix entry did — that used to reach /products/prepare-stripe-checkout too
295
+ return [4 /*yield*/, expectDenied(S1, 'exact matching: /products not granted by get:/product', publicFormSession.authToken, baselineEnduserToken, 'get', '/products')
296
+ /* ---- field confinement on the granted generic update route ----
297
+ * 'patch:/enduser/:id' is granted for Portal V2's terms gate, so the route check alone no longer
298
+ * protects this session — SESSION_SCOPE_WRITABLE_FIELDS does. These probes are the live control:
299
+ * the consent write lands, and every contactable/identity field is refused. Without them the
300
+ * module test is the only thing standing between a scope-table edit and account takeover.
301
+ */
302
+ ];
303
+ case 22:
304
+ // 'get:/product' names one route, so it no longer leaks the whole /products namespace the way a
305
+ // bare '/product' prefix entry did — that used to reach /products/prepare-stripe-checkout too
306
+ _11.sent();
307
+ formEnduserId = publicFormSession.enduserId;
308
+ return [4 /*yield*/, request(publicFormSession.authToken, 'post', '/refresh-enduser-session', {})];
309
+ case 23:
310
+ publicRefresh = _11.sent();
311
+ refreshedEnduser_1 = (_3 = (_2 = publicRefresh.body) === null || _2 === void 0 ? void 0 : _2.enduser) !== null && _3 !== void 0 ? _3 : {};
312
+ probes.push({
313
+ scenario: S1,
314
+ label: 'public refresh exposes the session\'s own id',
315
+ path: '/refresh-enduser-session',
316
+ expected: 'allowed',
317
+ status: refreshedEnduser_1.id === formEnduserId ? 200 : 500,
318
+ pass: refreshedEnduser_1.id === formEnduserId,
319
+ note: "id=".concat(refreshedEnduser_1.id),
320
+ });
321
+ withheld = ['email', 'phone', 'dateOfBirth', 'fname', 'lname', 'note', 'insurance', 'tags']
322
+ .filter(function (f) { return refreshedEnduser_1[f] !== undefined; });
323
+ probes.push({
324
+ scenario: S1,
325
+ label: 'public refresh still withholds the patient record',
326
+ path: '/refresh-enduser-session',
327
+ expected: 'denied',
328
+ status: withheld.length ? 200 : 403,
329
+ pass: withheld.length === 0,
330
+ note: withheld.length ? "LEAKED [".concat(withheld, "]") : "keys=[".concat(Object.keys(refreshedEnduser_1), "]"),
331
+ });
332
+ return [4 /*yield*/, expectAllowed(S1, 'field-restricted: may record terms consent', publicFormSession.authToken, 'patch', "/enduser/".concat(formEnduserId), { updates: { termsSigned: new Date().toISOString(), termsVersion: '1' } })
333
+ // The write being confined is not enough: the update RESPONSE is not redacted for enduser sessions
334
+ // (applyRedactions runs only for user sessions on that path), so an unprojected echo would hand
335
+ // this session the record it can't otherwise read — GET /enduser/:id is not even in this scope.
336
+ // Assert the response carries ONLY what the session was allowed to write.
337
+ ];
338
+ case 24:
339
+ _11.sent();
340
+ return [4 /*yield*/, request(publicFormSession.authToken, 'patch', "/enduser/".concat(formEnduserId), { updates: { termsVersion: '2' } })];
341
+ case 25:
342
+ consentEcho = _11.sent();
343
+ echoedKeys = Object.keys((_4 = consentEcho.body) !== null && _4 !== void 0 ? _4 : {});
344
+ leakedKeys = echoedKeys.filter(function (k) { return !['id', 'termsSigned', 'termsVersion'].includes(k); });
345
+ probes.push({
346
+ scenario: S1,
347
+ label: 'field-restricted: update response echoes only the writable fields',
348
+ path: "/enduser/".concat(formEnduserId),
349
+ expected: 'allowed',
350
+ status: leakedKeys.length === 0 ? consentEcho.status : 500,
351
+ pass: consentEcho.status === 200 && leakedKeys.length === 0,
352
+ note: leakedKeys.length ? "LEAKED [".concat(leakedKeys, "]") : "echoed [".concat(echoedKeys, "]"),
353
+ });
354
+ fieldsThatMustBeRefused = [
355
+ ['email', "f0166-takeover-".concat(RAND(), "@tellescope.example")],
356
+ ['phone', '+15555550123'],
357
+ ['landline', '+15555550124'],
358
+ ['dateOfBirth', '1990-01-01'],
359
+ ['fname', 'F0166Overwritten'],
360
+ ];
361
+ _i = 0, fieldsThatMustBeRefused_1 = fieldsThatMustBeRefused;
362
+ _11.label = 26;
363
+ case 26:
364
+ if (!(_i < fieldsThatMustBeRefused_1.length)) return [3 /*break*/, 29];
365
+ _b = fieldsThatMustBeRefused_1[_i], field = _b[0], value = _b[1];
366
+ return [4 /*yield*/, request(publicFormSession.authToken, 'patch', "/enduser/".concat(formEnduserId), { updates: (_0 = {}, _0[field] = value, _0) })];
367
+ case 27:
368
+ r = _11.sent();
369
+ probes.push({
370
+ scenario: S1,
371
+ label: "field-restricted: cannot write enduser '".concat(field, "'"),
372
+ path: "/enduser/".concat(formEnduserId),
373
+ expected: 'denied',
374
+ status: r.status,
375
+ pass: r.status === 403,
376
+ note: 'route is granted; the field allowlist is what refuses this',
377
+ });
378
+ _11.label = 28;
379
+ case 28:
380
+ _i++;
381
+ return [3 /*break*/, 26];
382
+ case 29: return [4 /*yield*/, request(publicFormSession.authToken, 'patch', "/enduser/".concat(formEnduserId), { updates: { termsVersion: '2', email: "f0166-mixed-".concat(RAND(), "@tellescope.example") } })];
383
+ case 30:
384
+ mixed = _11.sent();
385
+ probes.push({
386
+ scenario: S1,
387
+ label: 'field-restricted: a mixed allowed+denied update is refused whole',
388
+ path: "/enduser/".concat(formEnduserId),
389
+ expected: 'denied',
390
+ status: mixed.status,
391
+ pass: mixed.status === 403,
392
+ note: 'no partial application — email must not ride along with termsVersion',
393
+ });
394
+ return [4 /*yield*/, sdk.api.endusers.getOne(formEnduserId)];
395
+ case 31:
396
+ afterProbes = _11.sent();
397
+ probes.push({
398
+ scenario: S1,
399
+ label: 'field-restricted: stored email survived every attempt',
400
+ path: "/enduser/".concat(formEnduserId),
401
+ expected: 'denied',
402
+ status: afterProbes.email === formEnduserEmail ? 403 : 200,
403
+ pass: afterProbes.email === formEnduserEmail,
404
+ note: "stored=".concat(afterProbes.email),
405
+ });
406
+ S2 = 'S2 join-link (enduser)';
407
+ return [4 /*yield*/, expectDenied(S2, 'unlisted: form responses', joinSession.authToken, baselineEnduserToken, 'get', '/form-responses')];
408
+ case 32:
409
+ _11.sent();
410
+ return [4 /*yield*/, expectDenied(S2, 'unlisted: files', joinSession.authToken, baselineEnduserToken, 'get', '/files')];
411
+ case 33:
412
+ _11.sent();
413
+ return [4 /*yield*/, expectDenied(S2, 'unlisted: portal customizations', joinSession.authToken, baselineEnduserToken, 'get', '/portal-customizations')];
414
+ case 34:
415
+ _11.sent();
416
+ return [4 /*yield*/, expectDenied(S2, 'unlisted: UNRELATED calendar event', joinSession.authToken, baselineEnduserToken, 'get', "/calendar-event/".concat(unrelatedEvent.id))];
417
+ case 35:
418
+ _11.sent();
419
+ return [4 /*yield*/, expectAllowed(S2, 'always-allowed path', joinSession.authToken, 'get', '/test-authenticated')];
420
+ case 36:
421
+ _11.sent();
422
+ return [4 /*yield*/, expectAllowed(S2, 'listed: my meetings', joinSession.authToken, 'get', '/my-meetings')
423
+ // enduserActions for organizations is {}, so an enduser 401s here regardless of confinement
424
+ ];
425
+ case 37:
426
+ _11.sent();
427
+ // enduserActions for organizations is {}, so an enduser 401s here regardless of confinement
428
+ return [4 /*yield*/, expectAllowed(S2, 'listed: organizations', joinSession.authToken, 'get', '/organizations', undefined, { allow401: true })];
429
+ case 38:
430
+ // enduserActions for organizations is {}, so an enduser 401s here regardless of confinement
431
+ _11.sent();
432
+ return [4 /*yield*/, expectAllowed(S2, 'listed: users', joinSession.authToken, 'get', '/users')];
433
+ case 39:
434
+ _11.sent();
435
+ return [4 /*yield*/, expectAllowed(S2, 'listed: pinned calendar event', joinSession.authToken, 'get', "/calendar-event/".concat(joinEvent.id))];
436
+ case 40:
437
+ _11.sent();
438
+ return [4 /*yield*/, expectAllowed(S2, 'wildcard: meeting by id', joinSession.authToken, 'get', "/meeting/".concat(OBJECT_ID))
439
+ // the two paths widened in this change so the portal video flow keeps working
440
+ ];
441
+ case 41:
442
+ _11.sent();
443
+ // the two paths widened in this change so the portal video flow keeps working
444
+ return [4 /*yield*/, expectAllowed(S2, 'widened: calendar events list', joinSession.authToken, 'get', '/calendar-events')];
445
+ case 42:
446
+ // the two paths widened in this change so the portal video flow keeps working
447
+ _11.sent();
448
+ return [4 /*yield*/, expectAllowed(S2, 'widened: join meeting for event', joinSession.authToken, 'post', '/join-meeting-for-event', { calendarEventId: joinEvent.id })
449
+ // The WaitingRoom marks attendance here. This write is deliberately NOT field-restricted: the
450
+ // portal reads the event back through useCalendarEvents, whose updateElement REPLACES the local
451
+ // record with the update response (react state.tsx), so a truncated response would drop event
452
+ // fields out of local state mid-visit and break a live Twilio call. Assert both halves of that
453
+ // contract — the write lands, AND the response is still a whole event.
454
+ ];
455
+ case 43:
456
+ _11.sent();
457
+ return [4 /*yield*/, request(joinSession.authToken, 'patch', "/calendar-event/".concat(joinEvent.id), { updates: { joinedVideoCall: [{ id: joinEnduserId, at: new Date().toISOString() }] } })];
458
+ case 44:
459
+ attendanceWrite = _11.sent();
460
+ probes.push({
461
+ scenario: S2,
462
+ label: 'marks joinedVideoCall on its pinned event',
463
+ path: "/calendar-event/".concat(joinEvent.id),
464
+ expected: 'allowed',
465
+ status: attendanceWrite.status,
466
+ pass: attendanceWrite.status === 200,
467
+ });
468
+ echoedEvent = (_5 = attendanceWrite.body) !== null && _5 !== void 0 ? _5 : {};
469
+ probes.push({
470
+ scenario: S2,
471
+ label: 'attendance write returns the WHOLE event (client replaces local state from it)',
472
+ path: "/calendar-event/".concat(joinEvent.id),
473
+ expected: 'allowed',
474
+ status: (echoedEvent.title && echoedEvent.durationInMinutes) ? 200 : 500,
475
+ pass: !!echoedEvent.title && !!echoedEvent.durationInMinutes && !!echoedEvent.joinedVideoCall,
476
+ note: "echoed ".concat(Object.keys(echoedEvent).length, " keys"),
477
+ });
478
+ return [4 /*yield*/, request(joinSession.authToken, 'post', '/refresh-enduser-session', {})];
479
+ case 45:
480
+ refreshedJoin = _11.sent();
481
+ refreshedJoinToken = (_6 = refreshedJoin.body) === null || _6 === void 0 ? void 0 : _6.authToken;
482
+ probes.push({
483
+ scenario: S2, label: 'refresh returned a new token', path: '/refresh-enduser-session',
484
+ expected: 'allowed', status: refreshedJoin.status, pass: !!refreshedJoinToken,
485
+ });
486
+ if (!refreshedJoinToken) return [3 /*break*/, 48];
487
+ return [4 /*yield*/, expectDenied(S2, 'after refresh: still denied form responses', refreshedJoinToken, baselineEnduserToken, 'get', '/form-responses')];
488
+ case 46:
489
+ _11.sent();
490
+ return [4 /*yield*/, expectAllowed(S2, 'after refresh: still allowed my meetings', refreshedJoinToken, 'get', '/my-meetings')];
491
+ case 47:
492
+ _11.sent();
493
+ _11.label = 48;
494
+ case 48:
495
+ S3 = 'S3 start-link (STAFF)';
496
+ return [4 /*yield*/, expectDenied(S3, 'unlisted: form responses', startSession.authToken, baselineUserToken, 'get', '/form-responses')];
497
+ case 49:
498
+ _11.sent();
499
+ return [4 /*yield*/, expectDenied(S3, 'unlisted: journeys', startSession.authToken, baselineUserToken, 'get', '/journeys')];
500
+ case 50:
501
+ _11.sent();
502
+ return [4 /*yield*/, expectDenied(S3, 'unlisted: tickets', startSession.authToken, baselineUserToken, 'get', '/tickets')];
503
+ case 51:
504
+ _11.sent();
505
+ return [4 /*yield*/, expectDenied(S3, 'unlisted: data sync (org-wide change firehose)', startSession.authToken, baselineUserToken, 'get', '/data-sync', { from: new Date(Date.now() - 60000).toISOString() })];
506
+ case 52:
507
+ _11.sent();
508
+ return [4 /*yield*/, expectDenied(S3, 'unlisted: file download URL', startSession.authToken, baselineUserToken, 'get', '/file-download-URL', { secureName: 'f0166-does-not-exist' })];
509
+ case 53:
510
+ _11.sent();
511
+ return [4 /*yield*/, expectDenied(S3, 'unlisted: UNRELATED calendar event', startSession.authToken, baselineUserToken, 'get', "/calendar-event/".concat(unrelatedEvent.id))];
512
+ case 54:
513
+ _11.sent();
514
+ return [4 /*yield*/, expectAllowed(S3, 'always-allowed path', startSession.authToken, 'get', '/test-authenticated')];
515
+ case 55:
516
+ _11.sent();
517
+ return [4 /*yield*/, expectAllowed(S3, 'listed: my meetings', startSession.authToken, 'get', '/my-meetings')];
518
+ case 56:
519
+ _11.sent();
520
+ return [4 /*yield*/, expectAllowed(S3, 'listed: calendar events', startSession.authToken, 'get', '/calendar-events')];
521
+ case 57:
522
+ _11.sent();
523
+ return [4 /*yield*/, expectAllowed(S3, 'listed: organizations', startSession.authToken, 'get', '/organizations')];
524
+ case 58:
525
+ _11.sent();
526
+ return [4 /*yield*/, expectAllowed(S3, 'listed: users', startSession.authToken, 'get', '/users')];
527
+ case 59:
528
+ _11.sent();
529
+ return [4 /*yield*/, expectAllowed(S3, 'listed: endusers', startSession.authToken, 'get', '/endusers')];
530
+ case 60:
531
+ _11.sent();
532
+ return [4 /*yield*/, expectAllowed(S3, 'listed: pinned calendar event', startSession.authToken, 'get', "/calendar-event/".concat(startEvent.id))];
533
+ case 61:
534
+ _11.sent();
535
+ return [4 /*yield*/, expectAllowed(S3, 'wildcard: meeting by id', startSession.authToken, 'get', "/meeting/".concat(OBJECT_ID))];
536
+ case 62:
537
+ _11.sent();
538
+ return [4 /*yield*/, expectAllowed(S3, 'prefix descendant: users bulk-read', startSession.authToken, 'post', '/users/bulk-read', { ids: [hostUser.id] })];
539
+ case 63:
540
+ _11.sent();
541
+ return [4 /*yield*/, expectAllowed(S3, 'prefix descendant: endusers bulk-read', startSession.authToken, 'post', '/endusers/bulk-read', { ids: [joinEnduser.id] })];
542
+ case 64:
543
+ _11.sent();
544
+ return [4 /*yield*/, expectAllowed(S3, 'widened: join meeting for event', startSession.authToken, 'post', '/join-meeting-for-event', { calendarEventId: startEvent.id })];
545
+ case 65:
546
+ _11.sent();
547
+ return [4 /*yield*/, expectAllowed(S3, 'widened: add attendees to meeting', startSession.authToken, 'post', '/add-attendees-to-meeting', { id: OBJECT_ID })
548
+ // login_user propagates allowedPaths implicitly through its ...toSign rest spread — the
549
+ // "(ASSUMPTION)" comments in the mint sites depend on this, so assert it explicitly
550
+ ];
551
+ case 66:
552
+ _11.sent();
553
+ return [4 /*yield*/, request(startSession.authToken, 'post', '/refresh-session', {})];
554
+ case 67:
555
+ refreshedStart = _11.sent();
556
+ refreshedStartToken = (_7 = refreshedStart.body) === null || _7 === void 0 ? void 0 : _7.authToken;
557
+ probes.push({
558
+ scenario: S3, label: 'refresh returned a new token', path: '/refresh-session',
559
+ expected: 'allowed', status: refreshedStart.status, pass: !!refreshedStartToken,
560
+ });
561
+ if (!refreshedStartToken) return [3 /*break*/, 70];
562
+ return [4 /*yield*/, expectDenied(S3, 'after refresh: still denied journeys', refreshedStartToken, baselineUserToken, 'get', '/journeys')];
563
+ case 68:
564
+ _11.sent();
565
+ return [4 /*yield*/, expectAllowed(S3, 'after refresh: still allowed my meetings', refreshedStartToken, 'get', '/my-meetings')];
566
+ case 69:
567
+ _11.sent();
568
+ _11.label = 70;
569
+ case 70:
570
+ S4 = 'S4 widening side-effects';
571
+ prefixGranted = [
572
+ ['patch', '/calendar-events/bulk-update'],
573
+ ['post', '/calendar-events/report'],
574
+ ];
575
+ _c = 0, prefixGranted_1 = prefixGranted;
576
+ _11.label = 71;
577
+ case 71:
578
+ if (!(_c < prefixGranted_1.length)) return [3 /*break*/, 74];
579
+ _d = prefixGranted_1[_c], method = _d[0], path = _d[1];
580
+ return [4 /*yield*/, axios.request({
581
+ method: method,
582
+ url: "".concat(host, "/v1").concat(path),
583
+ data: {},
584
+ headers: { Authorization: "Bearer ".concat(joinSession.authToken) },
585
+ validateStatus: function () { return true; },
586
+ })];
587
+ case 72:
588
+ r = _11.sent();
589
+ probes.push({
590
+ scenario: S4,
591
+ label: "enduser action gating blocks ".concat(method.toUpperCase(), " ").concat(path),
592
+ path: path,
593
+ expected: 'denied',
594
+ status: r.status,
595
+ pass: r.status === 401 || r.status === 403,
596
+ note: 'prefix-granted by /calendar-events; blocked by enduserActions instead',
597
+ });
598
+ _11.label = 73;
599
+ case 73:
600
+ _c++;
601
+ return [3 /*break*/, 71];
602
+ case 74:
603
+ S6 = 'S6 namespace escapes';
604
+ ABSENT_ID_A = '68a1b2c3d4e5f60718293a4b';
605
+ ABSENT_ID_B = '68a1b2c3d4e5f60718293a4c';
606
+ INVALID_EMAIL = 'f0166-not-an-email';
607
+ return [4 /*yield*/, expectDenied(S6, 'ESCAPE: /users/consent (minted an unconfined token)', startSession.authToken, baselineUserToken, 'patch', '/users/consent', { termsVersion: '1' })];
608
+ case 75:
609
+ _11.sent();
610
+ return [4 /*yield*/, expectDenied(S6, 'ESCAPE: /users/switch-account', startSession.authToken, baselineUserToken, 'post', '/users/switch-account', { targetUserId: ABSENT_ID_A })];
611
+ case 76:
612
+ _11.sent();
613
+ return [4 /*yield*/, expectDenied(S6, 'ESCAPE: /users/configure-inbox', startSession.authToken, baselineUserToken, 'post', '/users/configure-inbox', {})];
614
+ case 77:
615
+ _11.sent();
616
+ return [4 /*yield*/, expectDenied(S6, 'PHI: /endusers/load-inbox-data', startSession.authToken, baselineUserToken, 'get', '/endusers/load-inbox-data')];
617
+ case 78:
618
+ _11.sent();
619
+ return [4 /*yield*/, expectDenied(S6, 'PHI: /endusers/report', startSession.authToken, baselineUserToken, 'get', '/endusers/report')];
620
+ case 79:
621
+ _11.sent();
622
+ return [4 /*yield*/, expectDenied(S6, 'PHI: /endusers/engagement', startSession.authToken, baselineUserToken, 'get', '/endusers/engagement')];
623
+ case 80:
624
+ _11.sent();
625
+ return [4 /*yield*/, expectDenied(S6, 'DESTRUCTIVE: /endusers/merge', startSession.authToken, baselineUserToken, 'post', '/endusers/merge', { sourceEnduserId: ABSENT_ID_A, destinationEnduserId: ABSENT_ID_B })];
626
+ case 81:
627
+ _11.sent();
628
+ return [4 /*yield*/, expectDenied(S6, 'DESTRUCTIVE: /endusers/bulk-update', startSession.authToken, baselineUserToken, 'patch', '/endusers/bulk-update', { ids: [ABSENT_ID_A], fields: {} })
629
+ // method scoping, end to end: the same path is readable but not writable.
630
+ // Baselines use the ADMIN token — the throwaway host user cannot create users or endusers, so it
631
+ // would 403 on permissions and the probe could not distinguish that from a confinement denial.
632
+ ];
633
+ case 82:
634
+ _11.sent();
635
+ // method scoping, end to end: the same path is readable but not writable.
636
+ // Baselines use the ADMIN token — the throwaway host user cannot create users or endusers, so it
637
+ // would 403 on permissions and the probe could not distinguish that from a confinement denial.
638
+ return [4 /*yield*/, expectAllowed(S6, 'method scoping: GET /endusers allowed', startSession.authToken, 'get', '/endusers')];
639
+ case 83:
640
+ // method scoping, end to end: the same path is readable but not writable.
641
+ // Baselines use the ADMIN token — the throwaway host user cannot create users or endusers, so it
642
+ // would 403 on permissions and the probe could not distinguish that from a confinement denial.
643
+ _11.sent();
644
+ return [4 /*yield*/, expectDenied(S6, 'method scoping: POST /endusers (create) denied', startSession.authToken, sdk.authToken, 'post', '/endusers', { email: INVALID_EMAIL })];
645
+ case 84:
646
+ _11.sent();
647
+ return [4 /*yield*/, expectAllowed(S6, 'method scoping: GET /users allowed', startSession.authToken, 'get', '/users')];
648
+ case 85:
649
+ _11.sent();
650
+ return [4 /*yield*/, expectDenied(S6, 'method scoping: POST /users (create) denied', startSession.authToken, sdk.authToken, 'post', '/users', { email: INVALID_EMAIL })
651
+ // even if consent were reachable, the token it mints must stay confined (defense in depth)
652
+ ];
653
+ case 86:
654
+ _11.sent();
655
+ return [4 /*yield*/, request(startSession.authToken, 'patch', '/users/consent', { termsVersion: '1' })];
656
+ case 87:
657
+ consentAttempt = _11.sent();
658
+ if (!(consentAttempt.status === 200 && ((_8 = consentAttempt.body) === null || _8 === void 0 ? void 0 : _8.authToken))) return [3 /*break*/, 89];
659
+ return [4 /*yield*/, expectDenied(S6, 'consent-minted token is still confined', consentAttempt.body.authToken, baselineUserToken, 'get', '/journeys')];
660
+ case 88:
661
+ _11.sent();
662
+ _11.label = 89;
663
+ case 89: return [4 /*yield*/, request(startSession.authToken, 'post', '/refresh-session', {})];
664
+ case 90:
665
+ refreshedForScopes = _11.sent();
666
+ rescopedToken = (_9 = refreshedForScopes.body) === null || _9 === void 0 ? void 0 : _9.authToken;
667
+ probes.push({
668
+ scenario: S6, label: 'refresh returned a new token', path: '/refresh-session',
669
+ expected: 'allowed', status: refreshedForScopes.status, pass: !!rescopedToken,
670
+ });
671
+ if (!rescopedToken) return [3 /*break*/, 96];
672
+ return [4 /*yield*/, expectDenied(S6, 'after refresh: still cannot reach /users/consent', rescopedToken, baselineUserToken, 'patch', '/users/consent', { termsVersion: '1' })];
673
+ case 91:
674
+ _11.sent();
675
+ return [4 /*yield*/, expectDenied(S6, 'after refresh: still cannot reach /endusers/load-inbox-data', rescopedToken, baselineUserToken, 'get', '/endusers/load-inbox-data')];
676
+ case 92:
677
+ _11.sent();
678
+ return [4 /*yield*/, expectDenied(S6, 'after refresh: POST /endusers still denied', rescopedToken, baselineUserToken, 'post', '/endusers', { email: "f0166-should-not-exist-".concat(RAND(), "@tellescope.example") })];
679
+ case 93:
680
+ _11.sent();
681
+ return [4 /*yield*/, expectAllowed(S6, 'after refresh: GET /endusers still allowed', rescopedToken, 'get', '/endusers')];
682
+ case 94:
683
+ _11.sent();
684
+ return [4 /*yield*/, expectAllowed(S6, 'after refresh: my-meetings still allowed', rescopedToken, 'get', '/my-meetings')];
685
+ case 95:
686
+ _11.sent();
687
+ _11.label = 96;
688
+ case 96:
689
+ S7 = 'S7 appointment-booking';
690
+ return [4 /*yield*/, sdk.api.calendar_event_templates.createOne({
691
+ title: "F-0166 booking ".concat(RAND()), durationInMinutes: 30,
692
+ })];
693
+ case 97:
694
+ bookingTemplate = _11.sent();
695
+ templateId = bookingTemplate.id;
696
+ bookingEnduserEmail = "f0166-booking-".concat(RAND(), "@tellescope.example");
697
+ return [4 /*yield*/, sdk.api.endusers.createOne({
698
+ email: bookingEnduserEmail, fname: 'Booking', lname: 'Confinement',
699
+ })];
700
+ case 98:
701
+ bookingEnduser = _11.sent();
702
+ bookingEnduserId = bookingEnduser.id;
703
+ return [4 /*yield*/, new EnduserSession({ host: host, businessId: businessId })
704
+ .api.calendar_events.session_for_public_appointment_booking({
705
+ businessId: businessId,
706
+ calendarEventTemplateId: bookingTemplate.id,
707
+ email: bookingEnduserEmail,
708
+ fname: 'Booking',
709
+ lname: 'Confinement',
710
+ })];
711
+ case 99:
712
+ bookingSession = _11.sent();
713
+ return [4 /*yield*/, expectAllowed(S7, 'field-restricted: may record terms consent', bookingSession.authToken, 'patch', "/enduser/".concat(bookingEnduser.id), { updates: { termsSigned: new Date().toISOString(), termsVersion: '1' } })];
714
+ case 100:
715
+ _11.sent();
716
+ return [4 /*yield*/, request(bookingSession.authToken, 'patch', "/enduser/".concat(bookingEnduser.id), { updates: { termsVersion: '2' } })];
717
+ case 101:
718
+ bookingEcho = _11.sent();
719
+ bookingLeaked = Object.keys((_10 = bookingEcho.body) !== null && _10 !== void 0 ? _10 : {})
720
+ .filter(function (k) { return !['id', 'termsSigned', 'termsVersion'].includes(k); });
721
+ probes.push({
722
+ scenario: S7,
723
+ label: 'field-restricted: update response echoes only the writable fields',
724
+ path: "/enduser/".concat(bookingEnduser.id),
725
+ expected: 'allowed',
726
+ status: bookingLeaked.length === 0 ? bookingEcho.status : 500,
727
+ pass: bookingEcho.status === 200 && bookingLeaked.length === 0,
728
+ note: bookingLeaked.length ? "LEAKED [".concat(bookingLeaked, "]") : undefined,
729
+ });
730
+ _e = 0, _f = [['email', "f0166-bt-".concat(RAND(), "@tellescope.example")], ['phone', '+15555550188']];
731
+ _11.label = 102;
732
+ case 102:
733
+ if (!(_e < _f.length)) return [3 /*break*/, 105];
734
+ _g = _f[_e], field = _g[0], value = _g[1];
735
+ return [4 /*yield*/, request(bookingSession.authToken, 'patch', "/enduser/".concat(bookingEnduser.id), { updates: (_1 = {}, _1[field] = value, _1) })];
736
+ case 103:
737
+ r = _11.sent();
738
+ probes.push({
739
+ scenario: S7,
740
+ label: "field-restricted: cannot write enduser '".concat(field, "'"),
741
+ path: "/enduser/".concat(bookingEnduser.id),
742
+ expected: 'denied',
743
+ status: r.status,
744
+ pass: r.status === 403,
745
+ note: 'route is granted; the field allowlist is what refuses this',
746
+ });
747
+ _11.label = 104;
748
+ case 104:
749
+ _e++;
750
+ return [3 /*break*/, 102];
751
+ case 105:
752
+ // the booking scope must not have gained anything else along with the enduser write
753
+ return [4 /*yield*/, expectDenied(S7, 'unlisted: form responses', bookingSession.authToken, baselineEnduserToken, 'get', '/form-responses')];
754
+ case 106:
755
+ // the booking scope must not have gained anything else along with the enduser write
756
+ _11.sent();
757
+ return [4 /*yield*/, expectDenied(S7, 'unlisted: calendar events', bookingSession.authToken, baselineEnduserToken, 'get', '/calendar-events')];
758
+ case 107:
759
+ _11.sent();
760
+ return [4 /*yield*/, expectAllowed(S7, 'listed: refresh enduser session', bookingSession.authToken, 'post', '/refresh-enduser-session')
761
+ /* ============ S5: ordinary sessions with no allowedPaths claim ============ */
762
+ ];
763
+ case 108:
764
+ _11.sent();
765
+ S5 = 'S5 unconfined sessions';
766
+ _h = 0, _j = ['/journeys', '/form-responses', '/endusers', '/calendar-events', '/tickets'];
767
+ _11.label = 109;
768
+ case 109:
769
+ if (!(_h < _j.length)) return [3 /*break*/, 112];
770
+ path = _j[_h];
771
+ return [4 /*yield*/, expectAllowed(S5, "staff session reaches ".concat(path), baselineUserToken, 'get', path)];
772
+ case 110:
773
+ _11.sent();
774
+ _11.label = 111;
775
+ case 111:
776
+ _h++;
777
+ return [3 /*break*/, 109];
778
+ case 112: return [4 /*yield*/, expectAllowed(S5, 'staff session reaches /data-sync', baselineUserToken, 'get', '/data-sync', { from: new Date(Date.now() - 60000).toISOString() })];
779
+ case 113:
780
+ _11.sent();
781
+ _k = 0, _l = ['/form-responses', '/calendar-events', '/files'];
782
+ _11.label = 114;
783
+ case 114:
784
+ if (!(_k < _l.length)) return [3 /*break*/, 117];
785
+ path = _l[_k];
786
+ return [4 /*yield*/, expectAllowed(S5, "enduser session reaches ".concat(path), baselineEnduserToken, 'get', path)];
787
+ case 115:
788
+ _11.sent();
789
+ _11.label = 116;
790
+ case 116:
791
+ _k++;
792
+ return [3 /*break*/, 114];
793
+ case 117: return [4 /*yield*/, sdk.test_authenticated()];
794
+ case 118:
795
+ adminAuthenticated = _11.sent();
796
+ probes.push({
797
+ scenario: S5, label: 'admin sdk still authenticated', path: '/test-authenticated',
798
+ expected: 'allowed', status: 200, pass: adminAuthenticated === 'Authenticated!',
799
+ });
800
+ apiKeySDK = new Session({ host: host, apiKey: '3n5q0SCBT_iUvZz-b9BJtX7o7HQUVJ9v132PgHJNJsg.' });
801
+ _m = 0, _o = ['/journeys', '/form-responses', '/endusers', '/calendar-events', '/files'];
802
+ _11.label = 119;
803
+ case 119:
804
+ if (!(_m < _o.length)) return [3 /*break*/, 122];
805
+ path = _o[_m];
806
+ return [4 /*yield*/, axios.request({
807
+ method: 'get',
808
+ url: "".concat(host, "/v1").concat(path),
809
+ headers: { Authorization: "API_KEY ".concat(apiKeySDK.apiKey) },
810
+ validateStatus: function () { return true; },
811
+ })];
812
+ case 120:
813
+ r = _11.sent();
814
+ probes.push({
815
+ scenario: S5,
816
+ label: "API key (headless) reaches ".concat(path),
817
+ path: path,
818
+ expected: 'allowed',
819
+ status: r.status,
820
+ pass: r.status !== 403,
821
+ });
822
+ _11.label = 121;
823
+ case 121:
824
+ _m++;
825
+ return [3 /*break*/, 119];
826
+ case 122:
827
+ failures = probes.filter(function (p) { return !p.pass; });
828
+ console.log('');
829
+ console.log(' F-0166 — full probe table');
830
+ console.log(' ' + '-'.repeat(112));
831
+ scenarios = probes.map(function (p) { return p.scenario; }).filter(function (s, i, all) { return all.indexOf(s) === i; });
832
+ _loop_1 = function (scenario) {
833
+ var inScenario = probes.filter(function (p) { return p.scenario === scenario; });
834
+ var failed = inScenario.filter(function (p) { return !p.pass; }).length;
835
+ console.log(" ".concat(scenario, ": ").concat(inScenario.length - failed, "/").concat(inScenario.length, " passing").concat(failed ? " <-- ".concat(failed, " FAILING") : ''));
836
+ for (var _12 = 0, inScenario_1 = inScenario; _12 < inScenario_1.length; _12++) {
837
+ var p = inScenario_1[_12];
838
+ var baseline = p.baselineStatus !== undefined ? " baseline=".concat(p.baselineStatus) : '';
839
+ console.log(" ".concat(p.pass ? ' ok ' : 'FAIL', " want=").concat(p.expected.padEnd(7), " got=").concat(p.status).concat(baseline, " ").concat(p.label).concat(p.note ? " (".concat(p.note, ")") : ''));
840
+ }
841
+ };
842
+ for (_p = 0, scenarios_1 = scenarios; _p < scenarios_1.length; _p++) {
843
+ scenario = scenarios_1[_p];
844
+ _loop_1(scenario);
845
+ }
846
+ console.log(' ' + '-'.repeat(112));
847
+ console.log(" TOTAL: ".concat(probes.length - failures.length, "/").concat(probes.length, " passing, ").concat(failures.length, " failing"));
848
+ console.log('');
849
+ for (_q = 0, probes_1 = probes; _q < probes_1.length; _q++) {
850
+ p = probes_1[_q];
851
+ assert(p.pass, "".concat(p.scenario, " \u2014 ").concat(p.label, " (").concat(p.path, "): wanted ").concat(p.expected, ", got status ").concat(p.status)
852
+ + (p.baselineStatus !== undefined ? " (unconfined baseline got ".concat(p.baselineStatus, ")") : ''), "".concat(p.scenario, " \u2014 ").concat(p.label));
853
+ }
854
+ return [3 /*break*/, 160];
855
+ case 123:
856
+ _r = 0, eventIds_1 = eventIds;
857
+ _11.label = 124;
858
+ case 124:
859
+ if (!(_r < eventIds_1.length)) return [3 /*break*/, 129];
860
+ id = eventIds_1[_r];
861
+ _11.label = 125;
862
+ case 125:
863
+ _11.trys.push([125, 127, , 128]);
864
+ return [4 /*yield*/, sdk.api.calendar_events.deleteOne(id)];
865
+ case 126:
866
+ _11.sent();
867
+ return [3 /*break*/, 128];
868
+ case 127:
869
+ _s = _11.sent();
870
+ return [3 /*break*/, 128];
871
+ case 128:
872
+ _r++;
873
+ return [3 /*break*/, 124];
874
+ case 129:
875
+ if (!joinEnduserId) return [3 /*break*/, 133];
876
+ _11.label = 130;
877
+ case 130:
878
+ _11.trys.push([130, 132, , 133]);
879
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(joinEnduserId)];
880
+ case 131:
881
+ _11.sent();
882
+ return [3 /*break*/, 133];
883
+ case 132:
884
+ _t = _11.sent();
885
+ return [3 /*break*/, 133];
886
+ case 133:
887
+ if (!formEnduserEmail) return [3 /*break*/, 139];
888
+ _11.label = 134;
889
+ case 134:
890
+ _11.trys.push([134, 138, , 139]);
891
+ return [4 /*yield*/, sdk.api.endusers.getOne({ email: formEnduserEmail })];
892
+ case 135:
893
+ e = _11.sent();
894
+ if (!(e === null || e === void 0 ? void 0 : e.id)) return [3 /*break*/, 137];
895
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(e.id)];
896
+ case 136:
897
+ _11.sent();
898
+ _11.label = 137;
899
+ case 137: return [3 /*break*/, 139];
900
+ case 138:
901
+ _u = _11.sent();
902
+ return [3 /*break*/, 139];
903
+ case 139:
904
+ if (!hostUserId) return [3 /*break*/, 143];
905
+ _11.label = 140;
906
+ case 140:
907
+ _11.trys.push([140, 142, , 143]);
908
+ return [4 /*yield*/, sdk.api.users.deleteOne(hostUserId)];
909
+ case 141:
910
+ _11.sent();
911
+ return [3 /*break*/, 143];
912
+ case 142:
913
+ _v = _11.sent();
914
+ return [3 /*break*/, 143];
915
+ case 143:
916
+ if (!bookingEnduserId) return [3 /*break*/, 147];
917
+ _11.label = 144;
918
+ case 144:
919
+ _11.trys.push([144, 146, , 147]);
920
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(bookingEnduserId)];
921
+ case 145:
922
+ _11.sent();
923
+ return [3 /*break*/, 147];
924
+ case 146:
925
+ _w = _11.sent();
926
+ return [3 /*break*/, 147];
927
+ case 147:
928
+ if (!templateId) return [3 /*break*/, 151];
929
+ _11.label = 148;
930
+ case 148:
931
+ _11.trys.push([148, 150, , 151]);
932
+ return [4 /*yield*/, sdk.api.calendar_event_templates.deleteOne(templateId)];
933
+ case 149:
934
+ _11.sent();
935
+ return [3 /*break*/, 151];
936
+ case 150:
937
+ _x = _11.sent();
938
+ return [3 /*break*/, 151];
939
+ case 151:
940
+ if (!productId) return [3 /*break*/, 155];
941
+ _11.label = 152;
942
+ case 152:
943
+ _11.trys.push([152, 154, , 155]);
944
+ return [4 /*yield*/, sdk.api.products.deleteOne(productId)];
945
+ case 153:
946
+ _11.sent();
947
+ return [3 /*break*/, 155];
948
+ case 154:
949
+ _y = _11.sent();
950
+ return [3 /*break*/, 155];
951
+ case 155:
952
+ if (!formId) return [3 /*break*/, 159];
953
+ _11.label = 156;
954
+ case 156:
955
+ _11.trys.push([156, 158, , 159]);
956
+ return [4 /*yield*/, sdk.api.forms.deleteOne(formId)];
957
+ case 157:
958
+ _11.sent();
959
+ return [3 /*break*/, 159];
960
+ case 158:
961
+ _z = _11.sent();
962
+ return [3 /*break*/, 159];
963
+ case 159: return [7 /*endfinally*/];
964
+ case 160: return [2 /*return*/];
965
+ }
966
+ });
967
+ });
968
+ };
969
+ if (require.main === module) {
970
+ console.log("\uD83C\uDF10 Using API URL: ".concat(host));
971
+ var sdk_1 = new Session({ host: host });
972
+ var sdkNonAdmin_1 = new Session({ host: host });
973
+ var runTests = function () { return __awaiter(void 0, void 0, void 0, function () {
974
+ return __generator(this, function (_a) {
975
+ switch (_a.label) {
976
+ case 0: return [4 /*yield*/, setup_tests(sdk_1, sdkNonAdmin_1)];
977
+ case 1:
978
+ _a.sent();
979
+ return [4 /*yield*/, allowed_paths_confinement_tests({ sdk: sdk_1, sdkNonAdmin: sdkNonAdmin_1 })];
980
+ case 2:
981
+ _a.sent();
982
+ return [2 /*return*/];
983
+ }
984
+ });
985
+ }); };
986
+ runTests()
987
+ .then(function () {
988
+ console.log("✅ F-0166 allowedPaths confinement test suite completed successfully");
989
+ process.exit(0);
990
+ })
991
+ .catch(function (error) {
992
+ console.error("❌ F-0166 allowedPaths confinement test suite failed:", error);
993
+ process.exit(1);
994
+ });
995
+ }
996
+ //# sourceMappingURL=F-0166-allowed-paths-wildcard-bypass.test.js.map