@tellescope/sdk 1.256.13 → 1.256.14

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 (106) hide show
  1. package/lib/cjs/tests/api_tests/organization_signup.test.d.ts +20 -0
  2. package/lib/cjs/tests/api_tests/organization_signup.test.d.ts.map +1 -0
  3. package/lib/cjs/tests/api_tests/organization_signup.test.js +272 -0
  4. package/lib/cjs/tests/api_tests/organization_signup.test.js.map +1 -0
  5. package/lib/cjs/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.d.ts +30 -0
  6. package/lib/cjs/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.d.ts.map +1 -0
  7. package/lib/cjs/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.js +336 -0
  8. package/lib/cjs/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.js.map +1 -0
  9. package/lib/cjs/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.d.ts +6 -0
  10. package/lib/cjs/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.d.ts.map +1 -0
  11. package/lib/cjs/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.js +354 -0
  12. package/lib/cjs/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.js.map +1 -0
  13. package/lib/cjs/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.d.ts +46 -0
  14. package/lib/cjs/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.d.ts.map +1 -0
  15. package/lib/cjs/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.js +389 -0
  16. package/lib/cjs/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.js.map +1 -0
  17. package/lib/cjs/tests/api_tests/security/F-0192-automated-actions-process-gate.test.d.ts +43 -0
  18. package/lib/cjs/tests/api_tests/security/F-0192-automated-actions-process-gate.test.d.ts.map +1 -0
  19. package/lib/cjs/tests/api_tests/security/F-0192-automated-actions-process-gate.test.js +324 -0
  20. package/lib/cjs/tests/api_tests/security/F-0192-automated-actions-process-gate.test.js.map +1 -0
  21. package/lib/cjs/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.d.ts +43 -0
  22. package/lib/cjs/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.d.ts.map +1 -0
  23. package/lib/cjs/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.js +282 -0
  24. package/lib/cjs/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.js.map +1 -0
  25. package/lib/cjs/tests/api_tests/tickets_bulk_assign_from_queue.test.d.ts +5 -0
  26. package/lib/cjs/tests/api_tests/tickets_bulk_assign_from_queue.test.d.ts.map +1 -0
  27. package/lib/cjs/tests/api_tests/tickets_bulk_assign_from_queue.test.js +329 -0
  28. package/lib/cjs/tests/api_tests/tickets_bulk_assign_from_queue.test.js.map +1 -0
  29. package/lib/cjs/tests/setup_isolated.d.ts +68 -0
  30. package/lib/cjs/tests/setup_isolated.d.ts.map +1 -0
  31. package/lib/cjs/tests/setup_isolated.js +232 -0
  32. package/lib/cjs/tests/setup_isolated.js.map +1 -0
  33. package/lib/cjs/tests/tests.d.ts.map +1 -1
  34. package/lib/cjs/tests/tests.js +257 -209
  35. package/lib/cjs/tests/tests.js.map +1 -1
  36. package/lib/cjs/tests/unit_tests/do_not_send_address.test.d.ts +2 -0
  37. package/lib/cjs/tests/unit_tests/do_not_send_address.test.d.ts.map +1 -0
  38. package/lib/cjs/tests/unit_tests/do_not_send_address.test.js +89 -0
  39. package/lib/cjs/tests/unit_tests/do_not_send_address.test.js.map +1 -0
  40. package/lib/cjs/tests/unit_tests/openapi_spec_freshness.test.d.ts +17 -0
  41. package/lib/cjs/tests/unit_tests/openapi_spec_freshness.test.d.ts.map +1 -0
  42. package/lib/cjs/tests/unit_tests/openapi_spec_freshness.test.js +140 -0
  43. package/lib/cjs/tests/unit_tests/openapi_spec_freshness.test.js.map +1 -0
  44. package/lib/esm/enduser.d.ts +1 -0
  45. package/lib/esm/enduser.d.ts.map +1 -1
  46. package/lib/esm/sdk.d.ts +3 -2
  47. package/lib/esm/sdk.d.ts.map +1 -1
  48. package/lib/esm/session.d.ts +1 -0
  49. package/lib/esm/session.d.ts.map +1 -1
  50. package/lib/esm/tests/api_tests/organization_signup.test.d.ts +20 -0
  51. package/lib/esm/tests/api_tests/organization_signup.test.d.ts.map +1 -0
  52. package/lib/esm/tests/api_tests/organization_signup.test.js +264 -0
  53. package/lib/esm/tests/api_tests/organization_signup.test.js.map +1 -0
  54. package/lib/esm/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.d.ts +30 -0
  55. package/lib/esm/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.d.ts.map +1 -0
  56. package/lib/esm/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.js +332 -0
  57. package/lib/esm/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.js.map +1 -0
  58. package/lib/esm/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.d.ts +6 -0
  59. package/lib/esm/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.d.ts.map +1 -0
  60. package/lib/esm/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.js +350 -0
  61. package/lib/esm/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.js.map +1 -0
  62. package/lib/esm/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.d.ts +46 -0
  63. package/lib/esm/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.d.ts.map +1 -0
  64. package/lib/esm/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.js +382 -0
  65. package/lib/esm/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.js.map +1 -0
  66. package/lib/esm/tests/api_tests/security/F-0192-automated-actions-process-gate.test.d.ts +43 -0
  67. package/lib/esm/tests/api_tests/security/F-0192-automated-actions-process-gate.test.d.ts.map +1 -0
  68. package/lib/esm/tests/api_tests/security/F-0192-automated-actions-process-gate.test.js +317 -0
  69. package/lib/esm/tests/api_tests/security/F-0192-automated-actions-process-gate.test.js.map +1 -0
  70. package/lib/esm/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.d.ts +43 -0
  71. package/lib/esm/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.d.ts.map +1 -0
  72. package/lib/esm/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.js +277 -0
  73. package/lib/esm/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.js.map +1 -0
  74. package/lib/esm/tests/api_tests/tickets_bulk_assign_from_queue.test.d.ts +5 -0
  75. package/lib/esm/tests/api_tests/tickets_bulk_assign_from_queue.test.d.ts.map +1 -0
  76. package/lib/esm/tests/api_tests/tickets_bulk_assign_from_queue.test.js +324 -0
  77. package/lib/esm/tests/api_tests/tickets_bulk_assign_from_queue.test.js.map +1 -0
  78. package/lib/esm/tests/setup_isolated.d.ts +68 -0
  79. package/lib/esm/tests/setup_isolated.d.ts.map +1 -0
  80. package/lib/esm/tests/setup_isolated.js +221 -0
  81. package/lib/esm/tests/setup_isolated.js.map +1 -0
  82. package/lib/esm/tests/tests.d.ts.map +1 -1
  83. package/lib/esm/tests/tests.js +257 -209
  84. package/lib/esm/tests/tests.js.map +1 -1
  85. package/lib/esm/tests/unit_tests/do_not_send_address.test.d.ts +2 -0
  86. package/lib/esm/tests/unit_tests/do_not_send_address.test.d.ts.map +1 -0
  87. package/lib/esm/tests/unit_tests/do_not_send_address.test.js +85 -0
  88. package/lib/esm/tests/unit_tests/do_not_send_address.test.js.map +1 -0
  89. package/lib/esm/tests/unit_tests/openapi_spec_freshness.test.d.ts +17 -0
  90. package/lib/esm/tests/unit_tests/openapi_spec_freshness.test.d.ts.map +1 -0
  91. package/lib/esm/tests/unit_tests/openapi_spec_freshness.test.js +113 -0
  92. package/lib/esm/tests/unit_tests/openapi_spec_freshness.test.js.map +1 -0
  93. package/lib/tsconfig.tsbuildinfo +1 -1
  94. package/package.json +10 -10
  95. package/src/tests/api_tests/organization_signup.test.ts +240 -0
  96. package/src/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.ts +225 -0
  97. package/src/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.ts +162 -0
  98. package/src/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.ts +288 -0
  99. package/src/tests/api_tests/security/F-0192-automated-actions-process-gate.test.ts +238 -0
  100. package/src/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.ts +208 -0
  101. package/src/tests/api_tests/tickets_bulk_assign_from_queue.test.ts +279 -0
  102. package/src/tests/setup_isolated.ts +170 -0
  103. package/src/tests/tests.ts +25 -0
  104. package/src/tests/unit_tests/do_not_send_address.test.ts +60 -0
  105. package/src/tests/unit_tests/openapi_spec_freshness.test.ts +88 -0
  106. package/test_generated.pdf +0 -0
@@ -0,0 +1,354 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
14
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
15
+ return new (P || (P = Promise))(function (resolve, reject) {
16
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
17
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
18
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
19
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
20
+ });
21
+ };
22
+ var __generator = (this && this.__generator) || function (thisArg, body) {
23
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
24
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
25
+ function verb(n) { return function (v) { return step([n, v]); }; }
26
+ function step(op) {
27
+ if (f) throw new TypeError("Generator is already executing.");
28
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
29
+ 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;
30
+ if (y = 0, t) op = [op[0] & 2, t.value];
31
+ switch (op[0]) {
32
+ case 0: case 1: t = op; break;
33
+ case 4: _.label++; return { value: op[1], done: false };
34
+ case 5: _.label++; y = op[1]; op = [0]; continue;
35
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
36
+ default:
37
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
38
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
39
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
40
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
41
+ if (t[2]) _.ops.pop();
42
+ _.trys.pop(); continue;
43
+ }
44
+ op = body.call(thisArg, _);
45
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
46
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
47
+ }
48
+ };
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.startlinktoken_escalation_tests = void 0;
51
+ require('source-map-support').install();
52
+ var sdk_1 = require("../../../sdk");
53
+ var testing_1 = require("@tellescope/testing");
54
+ var setup_1 = require("../../setup");
55
+ var constants_1 = require("@tellescope/constants");
56
+ var host = process.env.API_URL || 'http://localhost:8080';
57
+ /**
58
+ * Regression test for F-0185 — `calendar_events.startLinkToken` privilege escalation.
59
+ *
60
+ * startLinkToken is a DOCUMENTED feature ("Video Call Start Links"): a tenant's staff / API-key
61
+ * integration PATCHes a CalendarEvent to set a random token, then sends the host user the
62
+ * `/video/startlink?token=...` link, which `session_for_start_link` exchanges for a scope-limited
63
+ * User (staff) authToken for the first `type:'user'` attendee, MFA bypassed. Nothing in the codebase
64
+ * reads the token value back (the redeem matches server-side; the integration holds its own copy), so
65
+ * the field is write-only-by-design.
66
+ *
67
+ * TWO escalation vectors let a patient (or a low-privilege staff user) obtain the host's session:
68
+ * WRITE — `enduserActions.update` + no `enduserUpdatesDisabled` let an enduser attendee PATCH
69
+ * `startLinkToken` to a value they choose, then redeem it.
70
+ * READ — no `redactions` let an enduser attendee (or a non-admin staff user with calendar read)
71
+ * read a LEGITIMATELY staff-set token and redeem it, with no write.
72
+ *
73
+ * Complete fix = TWO flags on schema.ts `startLinkToken`:
74
+ * enduserUpdatesDisabled: true (closes WRITE; preserves the staff/API-key write the feature needs)
75
+ * redactions: ['all'] (closes READ for endusers AND non-admin users; safe — nothing reads
76
+ * the value back). NOT `readonly` (would break the staff write — A2).
77
+ *
78
+ * Checks (collected, then asserted, so a RED run shows every failing vector):
79
+ * A1 enduser attendee PATCH of startLinkToken is REJECTED (WRITE gate)
80
+ * A2 a staff-set token still redeems for the host's session (feature preserved)
81
+ * A3 an enduser-set token (if the write lands) does NOT redeem (defence-in-depth)
82
+ * A4 an enduser attendee does NOT receive startLinkToken on read (READ gate, endusers)
83
+ * A5 a non-admin staff user does NOT receive startLinkToken on read (READ gate, users)
84
+ *
85
+ * No vendor, no Bedrock; the "host" is a throwaway user. Cleans up every created record.
86
+ */
87
+ var RAND = function () { return Math.random().toString(36).slice(2, 10); };
88
+ var startlinktoken_escalation_tests = function (_a) {
89
+ var sdk = _a.sdk;
90
+ return __awaiter(void 0, void 0, void 0, function () {
91
+ var businessId, userIds, enduserIds, eventIds, roleIds, checks, record, host_user, patient, event_1, patientSDK, attackerToken, wrote, writeErr, err_1, adminView, redeemed, _b, staffToken, staffWriteErr, e_1, legit, e_2, enduserRead, _c, rbap, nonAdmin, authToken, nonAdminSDK, staffRead, _d, _i, checks_1, c, _e, checks_2, c, _f, eventIds_1, id, _g, _h, enduserIds_1, id, _j, _k, userIds_1, id, _l, _m, roleIds_1, id, _o;
92
+ var _p, _q, _r, _s, _t;
93
+ return __generator(this, function (_u) {
94
+ switch (_u.label) {
95
+ case 0:
96
+ (0, testing_1.log_header)("F-0185: startLinkToken enduser-writable / readable → staff session escalation");
97
+ businessId = sdk.userInfo.businessId;
98
+ userIds = [];
99
+ enduserIds = [];
100
+ eventIds = [];
101
+ roleIds = [];
102
+ checks = [];
103
+ record = function (label, pass, detail) { return checks.push({ label: label, pass: pass, detail: detail }); };
104
+ _u.label = 1;
105
+ case 1:
106
+ _u.trys.push([1, , 38, 63]);
107
+ return [4 /*yield*/, sdk.api.users.createOne({
108
+ email: "f0185-host-".concat(RAND(), "@tellescope.example"), fname: 'F0185', lname: 'Host',
109
+ verifiedEmail: true, notificationEmailsDisabled: true,
110
+ })];
111
+ case 2:
112
+ host_user = _u.sent();
113
+ userIds.push(host_user.id);
114
+ return [4 /*yield*/, sdk.api.endusers.createOne({
115
+ email: "f0185-patient-".concat(RAND(), "@tellescope.example"), fname: 'F0185', lname: 'Patient',
116
+ })];
117
+ case 3:
118
+ patient = _u.sent();
119
+ enduserIds.push(patient.id);
120
+ return [4 /*yield*/, sdk.api.calendar_events.createOne({
121
+ title: 'F-0185 appointment', durationInMinutes: 30, startTimeInMS: Date.now() + 3600000,
122
+ attendees: [{ type: 'user', id: host_user.id }, { type: 'enduser', id: patient.id }],
123
+ })];
124
+ case 4:
125
+ event_1 = _u.sent();
126
+ eventIds.push(event_1.id);
127
+ patientSDK = new sdk_1.EnduserSession({ host: host, businessId: businessId });
128
+ return [4 /*yield*/, (0, setup_1.authenticate_enduser_via_token)(sdk, patientSDK, { id: patient.id })
129
+ // ---- A1: WRITE gate — enduser attendee cannot PATCH startLinkToken ----
130
+ ];
131
+ case 5:
132
+ _u.sent();
133
+ attackerToken = "f0185-attacker-".concat(RAND()).concat(RAND());
134
+ wrote = false, writeErr = '';
135
+ _u.label = 6;
136
+ case 6:
137
+ _u.trys.push([6, 8, , 9]);
138
+ return [4 /*yield*/, patientSDK.api.calendar_events.updateOne(event_1.id, { startLinkToken: attackerToken })];
139
+ case 7:
140
+ _u.sent();
141
+ wrote = true;
142
+ return [3 /*break*/, 9];
143
+ case 8:
144
+ err_1 = _u.sent();
145
+ writeErr = (_p = err_1 === null || err_1 === void 0 ? void 0 : err_1.message) !== null && _p !== void 0 ? _p : String(err_1);
146
+ return [3 /*break*/, 9];
147
+ case 9:
148
+ record('A1: enduser cannot write startLinkToken', !wrote, wrote ? 'PATCH ACCEPTED — patient set a staff start-link token' : "rejected (".concat(writeErr, ")"));
149
+ return [4 /*yield*/, sdk.api.calendar_events.getOne(event_1.id)];
150
+ case 10:
151
+ adminView = _u.sent();
152
+ if (!(adminView.startLinkToken === attackerToken)) return [3 /*break*/, 15];
153
+ redeemed = null;
154
+ _u.label = 11;
155
+ case 11:
156
+ _u.trys.push([11, 13, , 14]);
157
+ return [4 /*yield*/, new sdk_1.Session({ host: host }).api.calendar_events.session_for_start_link({ token: attackerToken })];
158
+ case 12:
159
+ redeemed = _u.sent();
160
+ return [3 /*break*/, 14];
161
+ case 13:
162
+ _b = _u.sent();
163
+ return [3 /*break*/, 14];
164
+ case 14:
165
+ record('A3: enduser-set token does not mint a staff session', !(redeemed === null || redeemed === void 0 ? void 0 : redeemed.authToken), (redeemed === null || redeemed === void 0 ? void 0 : redeemed.authToken) ? "minted a STAFF authToken for host ".concat(host_user.id) : 'not redeemable');
166
+ return [3 /*break*/, 16];
167
+ case 15:
168
+ record('A3: enduser-set token does not mint a staff session', true, '(n/a — write blocked, value never landed)');
169
+ _u.label = 16;
170
+ case 16:
171
+ staffToken = "f0185-staff-".concat(RAND()).concat(RAND());
172
+ staffWriteErr = '';
173
+ _u.label = 17;
174
+ case 17:
175
+ _u.trys.push([17, 19, , 20]);
176
+ return [4 /*yield*/, sdk.api.calendar_events.updateOne(event_1.id, { startLinkToken: staffToken })];
177
+ case 18:
178
+ _u.sent();
179
+ return [3 /*break*/, 20];
180
+ case 19:
181
+ e_1 = _u.sent();
182
+ staffWriteErr = (_q = e_1 === null || e_1 === void 0 ? void 0 : e_1.message) !== null && _q !== void 0 ? _q : String(e_1);
183
+ return [3 /*break*/, 20];
184
+ case 20:
185
+ legit = null;
186
+ _u.label = 21;
187
+ case 21:
188
+ _u.trys.push([21, 23, , 24]);
189
+ return [4 /*yield*/, new sdk_1.Session({ host: host }).api.calendar_events.session_for_start_link({ token: staffToken })];
190
+ case 22:
191
+ legit = _u.sent();
192
+ return [3 /*break*/, 24];
193
+ case 23:
194
+ e_2 = _u.sent();
195
+ legit = { error: (_r = e_2 === null || e_2 === void 0 ? void 0 : e_2.message) !== null && _r !== void 0 ? _r : String(e_2) };
196
+ return [3 /*break*/, 24];
197
+ case 24:
198
+ record('A2: staff-set start link still works', !!(legit === null || legit === void 0 ? void 0 : legit.authToken) && legit.eventId === event_1.id, "staffWrite=".concat(staffWriteErr ? 'REJECTED(' + staffWriteErr + ')' : 'ok', " authToken=").concat(!!(legit === null || legit === void 0 ? void 0 : legit.authToken), " eventId=").concat(legit === null || legit === void 0 ? void 0 : legit.eventId, " ").concat((legit === null || legit === void 0 ? void 0 : legit.error) ? '(' + legit.error + ')' : ''));
199
+ enduserRead = null;
200
+ _u.label = 25;
201
+ case 25:
202
+ _u.trys.push([25, 27, , 28]);
203
+ return [4 /*yield*/, patientSDK.api.calendar_events.getOne(event_1.id)];
204
+ case 26:
205
+ enduserRead = _u.sent();
206
+ return [3 /*break*/, 28];
207
+ case 27:
208
+ _c = _u.sent();
209
+ return [3 /*break*/, 28];
210
+ case 28:
211
+ record('A4: startLinkToken redacted from enduser reads', !enduserRead || enduserRead.startLinkToken === undefined, "enduser read startLinkToken=".concat((_s = enduserRead === null || enduserRead === void 0 ? void 0 : enduserRead.startLinkToken) !== null && _s !== void 0 ? _s : '(absent)', " \u2014 could redeem a staff-set token"));
212
+ return [4 /*yield*/, sdk.api.role_based_access_permissions.createOne({
213
+ role: "F0185NonAdmin-".concat(RAND()),
214
+ // Non-admin (non-Admin-role) staff with tenant-wide calendar READ — enough to load the event,
215
+ // so the assertion isolates redaction (not access). Spread a valid full permissions shape.
216
+ permissions: __assign(__assign({}, constants_1.PROVIDER_PERMISSIONS), { calendar_events: __assign(__assign({}, constants_1.PROVIDER_PERMISSIONS.calendar_events), { read: 'All', readMany: 'All' }) }),
217
+ }).catch(function (e) { var _a; console.warn('A5 role create failed:', (_a = e === null || e === void 0 ? void 0 : e.message) !== null && _a !== void 0 ? _a : e); return null; })];
218
+ case 29:
219
+ rbap = _u.sent();
220
+ if (!rbap) return [3 /*break*/, 36];
221
+ roleIds.push(rbap.id);
222
+ return [4 /*yield*/, sdk.api.users.createOne({
223
+ email: "f0185-nonadmin-".concat(RAND(), "@tellescope.example"), fname: 'F0185', lname: 'NonAdmin',
224
+ verifiedEmail: true, notificationEmailsDisabled: true, roles: [rbap.role],
225
+ })];
226
+ case 30:
227
+ nonAdmin = _u.sent();
228
+ userIds.push(nonAdmin.id);
229
+ return [4 /*yield*/, sdk.api.users.generate_auth_token({ id: nonAdmin.id })];
230
+ case 31:
231
+ authToken = (_u.sent()).authToken;
232
+ nonAdminSDK = new sdk_1.Session({ host: host, authToken: authToken });
233
+ staffRead = null;
234
+ _u.label = 32;
235
+ case 32:
236
+ _u.trys.push([32, 34, , 35]);
237
+ return [4 /*yield*/, nonAdminSDK.api.calendar_events.getOne(event_1.id)];
238
+ case 33:
239
+ staffRead = _u.sent();
240
+ return [3 /*break*/, 35];
241
+ case 34:
242
+ _d = _u.sent();
243
+ return [3 /*break*/, 35];
244
+ case 35:
245
+ record('A5: startLinkToken redacted from non-admin user reads', !staffRead || staffRead.startLinkToken === undefined, "non-admin user read startLinkToken=".concat((_t = staffRead === null || staffRead === void 0 ? void 0 : staffRead.startLinkToken) !== null && _t !== void 0 ? _t : '(absent)', " \u2014 staff\u2192host escalation via read"));
246
+ return [3 /*break*/, 37];
247
+ case 36:
248
+ record('A5: startLinkToken redacted from non-admin user reads', true, '(n/a — could not create a non-admin role in this env)');
249
+ _u.label = 37;
250
+ case 37:
251
+ // Report the full table before asserting (the harness exits on the first failed assert).
252
+ console.log('\n F-0185 check summary:');
253
+ for (_i = 0, checks_1 = checks; _i < checks_1.length; _i++) {
254
+ c = checks_1[_i];
255
+ console.log(" ".concat(c.pass ? 'PASS' : 'FAIL', " ").concat(c.label, " \u2014 ").concat(c.detail));
256
+ }
257
+ console.log('');
258
+ for (_e = 0, checks_2 = checks; _e < checks_2.length; _e++) {
259
+ c = checks_2[_e];
260
+ (0, testing_1.assert)(c.pass, "".concat(c.label, " \u2014 ").concat(c.detail), c.label);
261
+ }
262
+ return [3 /*break*/, 63];
263
+ case 38:
264
+ _f = 0, eventIds_1 = eventIds;
265
+ _u.label = 39;
266
+ case 39:
267
+ if (!(_f < eventIds_1.length)) return [3 /*break*/, 44];
268
+ id = eventIds_1[_f];
269
+ _u.label = 40;
270
+ case 40:
271
+ _u.trys.push([40, 42, , 43]);
272
+ return [4 /*yield*/, sdk.api.calendar_events.deleteOne(id)];
273
+ case 41:
274
+ _u.sent();
275
+ return [3 /*break*/, 43];
276
+ case 42:
277
+ _g = _u.sent();
278
+ return [3 /*break*/, 43];
279
+ case 43:
280
+ _f++;
281
+ return [3 /*break*/, 39];
282
+ case 44:
283
+ _h = 0, enduserIds_1 = enduserIds;
284
+ _u.label = 45;
285
+ case 45:
286
+ if (!(_h < enduserIds_1.length)) return [3 /*break*/, 50];
287
+ id = enduserIds_1[_h];
288
+ _u.label = 46;
289
+ case 46:
290
+ _u.trys.push([46, 48, , 49]);
291
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(id)];
292
+ case 47:
293
+ _u.sent();
294
+ return [3 /*break*/, 49];
295
+ case 48:
296
+ _j = _u.sent();
297
+ return [3 /*break*/, 49];
298
+ case 49:
299
+ _h++;
300
+ return [3 /*break*/, 45];
301
+ case 50:
302
+ _k = 0, userIds_1 = userIds;
303
+ _u.label = 51;
304
+ case 51:
305
+ if (!(_k < userIds_1.length)) return [3 /*break*/, 56];
306
+ id = userIds_1[_k];
307
+ _u.label = 52;
308
+ case 52:
309
+ _u.trys.push([52, 54, , 55]);
310
+ return [4 /*yield*/, sdk.api.users.deleteOne(id)];
311
+ case 53:
312
+ _u.sent();
313
+ return [3 /*break*/, 55];
314
+ case 54:
315
+ _l = _u.sent();
316
+ return [3 /*break*/, 55];
317
+ case 55:
318
+ _k++;
319
+ return [3 /*break*/, 51];
320
+ case 56:
321
+ _m = 0, roleIds_1 = roleIds;
322
+ _u.label = 57;
323
+ case 57:
324
+ if (!(_m < roleIds_1.length)) return [3 /*break*/, 62];
325
+ id = roleIds_1[_m];
326
+ _u.label = 58;
327
+ case 58:
328
+ _u.trys.push([58, 60, , 61]);
329
+ return [4 /*yield*/, sdk.api.role_based_access_permissions.deleteOne(id)];
330
+ case 59:
331
+ _u.sent();
332
+ return [3 /*break*/, 61];
333
+ case 60:
334
+ _o = _u.sent();
335
+ return [3 /*break*/, 61];
336
+ case 61:
337
+ _m++;
338
+ return [3 /*break*/, 57];
339
+ case 62: return [7 /*endfinally*/];
340
+ case 63: return [2 /*return*/];
341
+ }
342
+ });
343
+ });
344
+ };
345
+ exports.startlinktoken_escalation_tests = startlinktoken_escalation_tests;
346
+ if (require.main === module) {
347
+ var sdk_2 = new sdk_1.Session({ host: host });
348
+ var sdkNonAdmin_1 = new sdk_1.Session({ host: host });
349
+ (0, setup_1.setup_tests)(sdk_2, sdkNonAdmin_1)
350
+ .then(function () { return (0, exports.startlinktoken_escalation_tests)({ sdk: sdk_2, sdkNonAdmin: sdkNonAdmin_1 }); })
351
+ .then(function () { console.log("✅ F-0185 startLinkToken escalation test suite completed successfully"); process.exit(0); })
352
+ .catch(function (error) { console.error("❌ F-0185 startLinkToken escalation test suite failed:", error); process.exit(1); });
353
+ }
354
+ //# sourceMappingURL=F-0185-startlinktoken-enduser-staff-escalation.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"F-0185-startlinktoken-enduser-staff-escalation.test.js","sourceRoot":"","sources":["../../../../../src/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import { Session } from "../../../sdk";
2
+ /**
3
+ * Regression test for F-0191
4
+ * (security-audit/findings/F-0191-schema-adminonly-not-enforced.md).
5
+ *
6
+ * `adminOnly: true` on a CUSTOM action in schema.ts was documentation-only. The runtime gate
7
+ * (routing.ts:944) reads the flag off the options object built by getCustomEndpointFields, and
8
+ * api.ts:609 set `adminOnly: o.adminOnly` without ever consulting `action.adminOnly`. So every
9
+ * admin-only custom action had to declare the flag twice, and the copies desynced: 11 of 20
10
+ * declared custom actions were unenforced at runtime while both public doc artifacts
11
+ * (openapi.json and the unauthenticated GET /documentation feed, which read the SCHEMA flag)
12
+ * advertised "**Admin only.**".
13
+ *
14
+ * DEFAULT CRUD actions never had this bug — routing.ts:2434 etc. read
15
+ * model.defaultActions.<op>.adminOnly straight off the schema. The fix
16
+ * (`adminOnly: o.adminOnly ?? action.adminOnly`) makes custom actions behave the same way.
17
+ *
18
+ * Five of the 11 were de-flagged in the schema instead, because the flag itself was wrong and
19
+ * enforcing it would revert a deliberate decision: users.invite_user carries an explicit
20
+ * "no longer admin only, but controlled by users.create" and send_invitation_to_existing is its
21
+ * sibling; organizations.create_and_join is the self-service signup path and would 401 every new
22
+ * org (a fresh user has no roles — the handler is what grants Admin); sync_note_to_canvas is
23
+ * designed around `access: 'read'` for non-admin callers; background_errors.mark_read substitutes
24
+ * its own RBAC check that intentionally admits non-admins holding the verb.
25
+ *
26
+ * SAFETY: every probe fails EARLY inside its handler — after the adminOnly gate but before any
27
+ * vendor call. Pre-fix these requests really do reach the handler, and these handlers talk to
28
+ * Twilio, Athena and Canvas:
29
+ * - trigger_events : no `action` key + an absent triggerId => the loop `continue`s (inert)
30
+ * - athena-subscription: frequency 1 trips the `frequency < 5` guard on the handler's first line
31
+ * - sync-athena : 404s on the missing Athena integration (skipped if one exists)
32
+ * - link-twilio : takes NO parameters and can CREATE A REAL TWILIO SUBACCOUNT, so it is
33
+ * probed only when the org already has a twilioSID (handler short-circuits)
34
+ * - twilio-embed : no phoneNumberSid => 404 no-config or 400, both before any Twilio call
35
+ * - handle-autoreply : absent enduserId => `if (!enduser) return` (communications.ts:42)
36
+ * Input validation runs BEFORE the adminOnly gate (routing.ts:921 orders access, then validate),
37
+ * so every probe body must be schema-VALID or the test would prove nothing.
38
+ *
39
+ * RED (pre-fix): the six group-B rows fail — each reaches its handler and returns a non-401.
40
+ * GREEN (post-fix): all six return 401 "Admin access only"; the group-A controls are unchanged.
41
+ */
42
+ export declare const schema_adminonly_enforcement_tests: ({ sdk }: {
43
+ sdk: Session;
44
+ sdkNonAdmin: Session;
45
+ }) => Promise<void>;
46
+ //# sourceMappingURL=F-0191-schema-adminonly-enforcement.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"F-0191-schema-adminonly-enforcement.test.d.ts","sourceRoot":"","sources":["../../../../../src/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAqBtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,eAAO,MAAM,kCAAkC;SAA2B,OAAO;iBAAe,OAAO;mBAwMtG,CAAA"}