@tellescope/sdk 1.250.2 → 1.252.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 (136) hide show
  1. package/lib/cjs/sdk.d.ts +9 -0
  2. package/lib/cjs/sdk.d.ts.map +1 -1
  3. package/lib/cjs/sdk.js +3 -0
  4. package/lib/cjs/sdk.js.map +1 -1
  5. package/lib/cjs/tests/api_tests/account_switcher.test.d.ts.map +1 -1
  6. package/lib/cjs/tests/api_tests/account_switcher.test.js +1700 -306
  7. package/lib/cjs/tests/api_tests/account_switcher.test.js.map +1 -1
  8. package/lib/cjs/tests/api_tests/calendar_event_webhook_template.test.d.ts +6 -0
  9. package/lib/cjs/tests/api_tests/calendar_event_webhook_template.test.d.ts.map +1 -0
  10. package/lib/cjs/tests/api_tests/calendar_event_webhook_template.test.js +337 -0
  11. package/lib/cjs/tests/api_tests/calendar_event_webhook_template.test.js.map +1 -0
  12. package/lib/cjs/tests/api_tests/enduser_login.test.d.ts +6 -0
  13. package/lib/cjs/tests/api_tests/enduser_login.test.d.ts.map +1 -0
  14. package/lib/cjs/tests/api_tests/enduser_login.test.js +315 -0
  15. package/lib/cjs/tests/api_tests/enduser_login.test.js.map +1 -0
  16. package/lib/cjs/tests/api_tests/enduser_login_rate_limits.test.d.ts +6 -0
  17. package/lib/cjs/tests/api_tests/enduser_login_rate_limits.test.d.ts.map +1 -0
  18. package/lib/cjs/tests/api_tests/enduser_login_rate_limits.test.js +287 -0
  19. package/lib/cjs/tests/api_tests/enduser_login_rate_limits.test.js.map +1 -0
  20. package/lib/cjs/tests/api_tests/push_forms_to_portal_group_completion.test.d.ts +6 -0
  21. package/lib/cjs/tests/api_tests/push_forms_to_portal_group_completion.test.d.ts.map +1 -0
  22. package/lib/cjs/tests/api_tests/push_forms_to_portal_group_completion.test.js +406 -0
  23. package/lib/cjs/tests/api_tests/push_forms_to_portal_group_completion.test.js.map +1 -0
  24. package/lib/cjs/tests/api_tests/security/F-0001-data-sync-redaction-bypass.test.d.ts +28 -0
  25. package/lib/cjs/tests/api_tests/security/F-0001-data-sync-redaction-bypass.test.d.ts.map +1 -0
  26. package/lib/cjs/tests/api_tests/security/F-0001-data-sync-redaction-bypass.test.js +349 -0
  27. package/lib/cjs/tests/api_tests/security/F-0001-data-sync-redaction-bypass.test.js.map +1 -0
  28. package/lib/cjs/tests/api_tests/security/F-0005-ai-conversations-rbac.test.d.ts +28 -0
  29. package/lib/cjs/tests/api_tests/security/F-0005-ai-conversations-rbac.test.d.ts.map +1 -0
  30. package/lib/cjs/tests/api_tests/security/F-0005-ai-conversations-rbac.test.js +247 -0
  31. package/lib/cjs/tests/api_tests/security/F-0005-ai-conversations-rbac.test.js.map +1 -0
  32. package/lib/cjs/tests/api_tests/security/F-0007-invite-user-enumeration.test.d.ts +29 -0
  33. package/lib/cjs/tests/api_tests/security/F-0007-invite-user-enumeration.test.d.ts.map +1 -0
  34. package/lib/cjs/tests/api_tests/security/F-0007-invite-user-enumeration.test.js +278 -0
  35. package/lib/cjs/tests/api_tests/security/F-0007-invite-user-enumeration.test.js.map +1 -0
  36. package/lib/cjs/tests/api_tests/security/F-0008-handle-incoming-communication-cross-tenant.test.d.ts +24 -0
  37. package/lib/cjs/tests/api_tests/security/F-0008-handle-incoming-communication-cross-tenant.test.d.ts.map +1 -0
  38. package/lib/cjs/tests/api_tests/security/F-0008-handle-incoming-communication-cross-tenant.test.js +201 -0
  39. package/lib/cjs/tests/api_tests/security/F-0008-handle-incoming-communication-cross-tenant.test.js.map +1 -0
  40. package/lib/cjs/tests/api_tests/security/F-0013-sanitize-user-html.test.d.ts +2 -0
  41. package/lib/cjs/tests/api_tests/security/F-0013-sanitize-user-html.test.d.ts.map +1 -0
  42. package/lib/cjs/tests/api_tests/security/F-0013-sanitize-user-html.test.js +148 -0
  43. package/lib/cjs/tests/api_tests/security/F-0013-sanitize-user-html.test.js.map +1 -0
  44. package/lib/cjs/tests/api_tests/security/F-0016-prototype-pollution.test.d.ts +2 -0
  45. package/lib/cjs/tests/api_tests/security/F-0016-prototype-pollution.test.d.ts.map +1 -0
  46. package/lib/cjs/tests/api_tests/security/F-0016-prototype-pollution.test.js +88 -0
  47. package/lib/cjs/tests/api_tests/security/F-0016-prototype-pollution.test.js.map +1 -0
  48. package/lib/cjs/tests/api_tests/set_fields_order_templates.test.d.ts +6 -0
  49. package/lib/cjs/tests/api_tests/set_fields_order_templates.test.d.ts.map +1 -0
  50. package/lib/cjs/tests/api_tests/set_fields_order_templates.test.js +373 -0
  51. package/lib/cjs/tests/api_tests/set_fields_order_templates.test.js.map +1 -0
  52. package/lib/cjs/tests/setup.d.ts.map +1 -1
  53. package/lib/cjs/tests/setup.js +47 -32
  54. package/lib/cjs/tests/setup.js.map +1 -1
  55. package/lib/cjs/tests/tests.d.ts.map +1 -1
  56. package/lib/cjs/tests/tests.js +215 -159
  57. package/lib/cjs/tests/tests.js.map +1 -1
  58. package/lib/esm/sdk.d.ts +9 -0
  59. package/lib/esm/sdk.d.ts.map +1 -1
  60. package/lib/esm/sdk.js +3 -0
  61. package/lib/esm/sdk.js.map +1 -1
  62. package/lib/esm/tests/api_tests/account_switcher.test.d.ts.map +1 -1
  63. package/lib/esm/tests/api_tests/account_switcher.test.js +1702 -305
  64. package/lib/esm/tests/api_tests/account_switcher.test.js.map +1 -1
  65. package/lib/esm/tests/api_tests/calendar_event_webhook_template.test.d.ts +6 -0
  66. package/lib/esm/tests/api_tests/calendar_event_webhook_template.test.d.ts.map +1 -0
  67. package/lib/esm/tests/api_tests/calendar_event_webhook_template.test.js +333 -0
  68. package/lib/esm/tests/api_tests/calendar_event_webhook_template.test.js.map +1 -0
  69. package/lib/esm/tests/api_tests/enduser_login.test.d.ts +6 -0
  70. package/lib/esm/tests/api_tests/enduser_login.test.d.ts.map +1 -0
  71. package/lib/esm/tests/api_tests/enduser_login.test.js +308 -0
  72. package/lib/esm/tests/api_tests/enduser_login.test.js.map +1 -0
  73. package/lib/esm/tests/api_tests/enduser_login_phi_disclosure.test.d.ts +6 -0
  74. package/lib/esm/tests/api_tests/enduser_login_phi_disclosure.test.d.ts.map +1 -0
  75. package/lib/esm/tests/api_tests/enduser_login_phi_disclosure.test.js +268 -0
  76. package/lib/esm/tests/api_tests/enduser_login_phi_disclosure.test.js.map +1 -0
  77. package/lib/esm/tests/api_tests/enduser_login_rate_limits.test.d.ts +6 -0
  78. package/lib/esm/tests/api_tests/enduser_login_rate_limits.test.d.ts.map +1 -0
  79. package/lib/esm/tests/api_tests/enduser_login_rate_limits.test.js +280 -0
  80. package/lib/esm/tests/api_tests/enduser_login_rate_limits.test.js.map +1 -0
  81. package/lib/esm/tests/api_tests/push_forms_to_portal_group_completion.test.d.ts +6 -0
  82. package/lib/esm/tests/api_tests/push_forms_to_portal_group_completion.test.d.ts.map +1 -0
  83. package/lib/esm/tests/api_tests/push_forms_to_portal_group_completion.test.js +402 -0
  84. package/lib/esm/tests/api_tests/push_forms_to_portal_group_completion.test.js.map +1 -0
  85. package/lib/esm/tests/api_tests/security/F-0001-data-sync-redaction-bypass.test.d.ts +28 -0
  86. package/lib/esm/tests/api_tests/security/F-0001-data-sync-redaction-bypass.test.d.ts.map +1 -0
  87. package/lib/esm/tests/api_tests/security/F-0001-data-sync-redaction-bypass.test.js +345 -0
  88. package/lib/esm/tests/api_tests/security/F-0001-data-sync-redaction-bypass.test.js.map +1 -0
  89. package/lib/esm/tests/api_tests/security/F-0005-ai-conversations-rbac.test.d.ts +28 -0
  90. package/lib/esm/tests/api_tests/security/F-0005-ai-conversations-rbac.test.d.ts.map +1 -0
  91. package/lib/esm/tests/api_tests/security/F-0005-ai-conversations-rbac.test.js +243 -0
  92. package/lib/esm/tests/api_tests/security/F-0005-ai-conversations-rbac.test.js.map +1 -0
  93. package/lib/esm/tests/api_tests/security/F-0007-invite-user-enumeration.test.d.ts +29 -0
  94. package/lib/esm/tests/api_tests/security/F-0007-invite-user-enumeration.test.d.ts.map +1 -0
  95. package/lib/esm/tests/api_tests/security/F-0007-invite-user-enumeration.test.js +271 -0
  96. package/lib/esm/tests/api_tests/security/F-0007-invite-user-enumeration.test.js.map +1 -0
  97. package/lib/esm/tests/api_tests/security/F-0008-handle-incoming-communication-cross-tenant.test.d.ts +24 -0
  98. package/lib/esm/tests/api_tests/security/F-0008-handle-incoming-communication-cross-tenant.test.d.ts.map +1 -0
  99. package/lib/esm/tests/api_tests/security/F-0008-handle-incoming-communication-cross-tenant.test.js +194 -0
  100. package/lib/esm/tests/api_tests/security/F-0008-handle-incoming-communication-cross-tenant.test.js.map +1 -0
  101. package/lib/esm/tests/api_tests/security/F-0013-sanitize-user-html.test.d.ts +2 -0
  102. package/lib/esm/tests/api_tests/security/F-0013-sanitize-user-html.test.d.ts.map +1 -0
  103. package/lib/esm/tests/api_tests/security/F-0013-sanitize-user-html.test.js +144 -0
  104. package/lib/esm/tests/api_tests/security/F-0013-sanitize-user-html.test.js.map +1 -0
  105. package/lib/esm/tests/api_tests/security/F-0016-prototype-pollution.test.d.ts +2 -0
  106. package/lib/esm/tests/api_tests/security/F-0016-prototype-pollution.test.d.ts.map +1 -0
  107. package/lib/esm/tests/api_tests/security/F-0016-prototype-pollution.test.js +84 -0
  108. package/lib/esm/tests/api_tests/security/F-0016-prototype-pollution.test.js.map +1 -0
  109. package/lib/esm/tests/api_tests/set_fields_order_templates.test.d.ts +6 -0
  110. package/lib/esm/tests/api_tests/set_fields_order_templates.test.d.ts.map +1 -0
  111. package/lib/esm/tests/api_tests/set_fields_order_templates.test.js +369 -0
  112. package/lib/esm/tests/api_tests/set_fields_order_templates.test.js.map +1 -0
  113. package/lib/esm/tests/setup.d.ts.map +1 -1
  114. package/lib/esm/tests/setup.js +47 -32
  115. package/lib/esm/tests/setup.js.map +1 -1
  116. package/lib/esm/tests/tests.d.ts.map +1 -1
  117. package/lib/esm/tests/tests.js +215 -159
  118. package/lib/esm/tests/tests.js.map +1 -1
  119. package/lib/tsconfig.tsbuildinfo +1 -1
  120. package/package.json +10 -10
  121. package/src/sdk.ts +12 -0
  122. package/src/tests/api_tests/account_switcher.test.ts +1283 -0
  123. package/src/tests/api_tests/calendar_event_webhook_template.test.ts +204 -0
  124. package/src/tests/api_tests/enduser_login.test.ts +215 -0
  125. package/src/tests/api_tests/enduser_login_rate_limits.test.ts +178 -0
  126. package/src/tests/api_tests/push_forms_to_portal_group_completion.test.ts +223 -0
  127. package/src/tests/api_tests/security/F-0001-data-sync-redaction-bypass.test.ts +236 -0
  128. package/src/tests/api_tests/security/F-0005-ai-conversations-rbac.test.ts +154 -0
  129. package/src/tests/api_tests/security/F-0007-invite-user-enumeration.test.ts +198 -0
  130. package/src/tests/api_tests/security/F-0008-handle-incoming-communication-cross-tenant.test.ts +130 -0
  131. package/src/tests/api_tests/security/F-0013-sanitize-user-html.test.ts +109 -0
  132. package/src/tests/api_tests/security/F-0016-prototype-pollution.test.ts +50 -0
  133. package/src/tests/api_tests/set_fields_order_templates.test.ts +258 -0
  134. package/src/tests/setup.ts +8 -1
  135. package/src/tests/tests.ts +35 -5
  136. package/test_generated.pdf +0 -0
@@ -0,0 +1,29 @@
1
+ import { Session } from "../../../sdk";
2
+ /**
3
+ * Regression test for F-0007 (security-audit/findings/F-0007-invite-user-cross-tenant-email-enumeration.md).
4
+ *
5
+ * `users.invite_user` previously used `buildAllQueries({ unrestricted: true, organizationIds: [] }).users.findOne({ email })`
6
+ * to enforce platform-wide email uniqueness and threw the distinctive `"A user with this email already exists"`
7
+ * error on duplicate — regardless of which tenant the existing user was in. Any authenticated user could
8
+ * therefore probe whether email X is registered to any tenant on the platform.
9
+ *
10
+ * **Tests only the negative case** — never drives a successful invite (which would create a real user
11
+ * record and send a real transactional email). All assertions use either:
12
+ * - An email that already exists in the test tenant (the admin's own email), so each call short-circuits
13
+ * at the same-tenant duplicate check or rate-limit check before any invite work happens, OR
14
+ * - The cross-org infrastructure (CROSS_ORG_API_KEY env var) targeting an email that exists in a different
15
+ * tenant — verifies the post-fix response does NOT distinguish "exists elsewhere" from a generic outcome.
16
+ *
17
+ * Assertions:
18
+ * 1. Rate-limit defense-in-depth: rapid same-tenant duplicate requests trip 429 within ~12 attempts.
19
+ * 2. Same-tenant duplicate: returns the same `"already exists"` error pre/post-fix (this branch is
20
+ * unchanged by the fix; asserted for regression-safety).
21
+ * 3. Cross-tenant duplicate (env-gated): post-fix response shape does NOT contain the `"already exists"`
22
+ * string and matches the silent-no-op shape `{ created: { id: ... } }`. Skipped when CROSS_ORG_*
23
+ * env vars are not set, mirroring cross_org_api_key.test.ts convention.
24
+ */
25
+ export declare const invite_user_enumeration_tests: ({ sdk, sdkNonAdmin }: {
26
+ sdk: Session;
27
+ sdkNonAdmin: Session;
28
+ }) => Promise<void>;
29
+ //# sourceMappingURL=F-0007-invite-user-enumeration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"F-0007-invite-user-enumeration.test.d.ts","sourceRoot":"","sources":["../../../../../src/tests/api_tests/security/F-0007-invite-user-enumeration.test.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAyBtC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,6BAA6B;SAAwC,OAAO;iBAAe,OAAO;mBA6H9G,CAAA"}
@@ -0,0 +1,271 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ var __generator = (this && this.__generator) || function (thisArg, body) {
11
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
12
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
13
+ function verb(n) { return function (v) { return step([n, v]); }; }
14
+ function step(op) {
15
+ if (f) throw new TypeError("Generator is already executing.");
16
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
17
+ 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;
18
+ if (y = 0, t) op = [op[0] & 2, t.value];
19
+ switch (op[0]) {
20
+ case 0: case 1: t = op; break;
21
+ case 4: _.label++; return { value: op[1], done: false };
22
+ case 5: _.label++; y = op[1]; op = [0]; continue;
23
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
24
+ default:
25
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
26
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
27
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
28
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
29
+ if (t[2]) _.ops.pop();
30
+ _.trys.pop(); continue;
31
+ }
32
+ op = body.call(thisArg, _);
33
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
34
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
35
+ }
36
+ };
37
+ require('source-map-support').install();
38
+ import axios from "axios";
39
+ import { Session } from "../../../sdk";
40
+ import { async_test, log_header, wait, } from "@tellescope/testing";
41
+ import { setup_tests } from "../../setup";
42
+ var host = process.env.API_URL || 'http://localhost:8080';
43
+ var CROSS_ORG_API_KEY = process.env.CROSS_ORG_API_KEY;
44
+ var CROSS_ORG_TARGET_BUSINESS_ID = process.env.CROSS_ORG_TARGET_BUSINESS_ID;
45
+ var post = function (path, body, headers) {
46
+ if (headers === void 0) { headers = {}; }
47
+ return __awaiter(void 0, void 0, void 0, function () {
48
+ var res, err_1;
49
+ var _a, _b;
50
+ return __generator(this, function (_c) {
51
+ switch (_c.label) {
52
+ case 0:
53
+ _c.trys.push([0, 2, , 3]);
54
+ return [4 /*yield*/, axios.post("".concat(host).concat(path), body, {
55
+ validateStatus: function () { return true; },
56
+ headers: headers,
57
+ })];
58
+ case 1:
59
+ res = _c.sent();
60
+ return [2 /*return*/, { status: res.status, data: res.data }];
61
+ case 2:
62
+ err_1 = _c.sent();
63
+ return [2 /*return*/, { status: (_a = err_1 === null || err_1 === void 0 ? void 0 : err_1.response) === null || _a === void 0 ? void 0 : _a.status, data: (_b = err_1 === null || err_1 === void 0 ? void 0 : err_1.response) === null || _b === void 0 ? void 0 : _b.data }];
64
+ case 3: return [2 /*return*/];
65
+ }
66
+ });
67
+ });
68
+ };
69
+ /**
70
+ * Regression test for F-0007 (security-audit/findings/F-0007-invite-user-cross-tenant-email-enumeration.md).
71
+ *
72
+ * `users.invite_user` previously used `buildAllQueries({ unrestricted: true, organizationIds: [] }).users.findOne({ email })`
73
+ * to enforce platform-wide email uniqueness and threw the distinctive `"A user with this email already exists"`
74
+ * error on duplicate — regardless of which tenant the existing user was in. Any authenticated user could
75
+ * therefore probe whether email X is registered to any tenant on the platform.
76
+ *
77
+ * **Tests only the negative case** — never drives a successful invite (which would create a real user
78
+ * record and send a real transactional email). All assertions use either:
79
+ * - An email that already exists in the test tenant (the admin's own email), so each call short-circuits
80
+ * at the same-tenant duplicate check or rate-limit check before any invite work happens, OR
81
+ * - The cross-org infrastructure (CROSS_ORG_API_KEY env var) targeting an email that exists in a different
82
+ * tenant — verifies the post-fix response does NOT distinguish "exists elsewhere" from a generic outcome.
83
+ *
84
+ * Assertions:
85
+ * 1. Rate-limit defense-in-depth: rapid same-tenant duplicate requests trip 429 within ~12 attempts.
86
+ * 2. Same-tenant duplicate: returns the same `"already exists"` error pre/post-fix (this branch is
87
+ * unchanged by the fix; asserted for regression-safety).
88
+ * 3. Cross-tenant duplicate (env-gated): post-fix response shape does NOT contain the `"already exists"`
89
+ * string and matches the silent-no-op shape `{ created: { id: ... } }`. Skipped when CROSS_ORG_*
90
+ * env vars are not set, mirroring cross_org_api_key.test.ts convention.
91
+ */
92
+ export var invite_user_enumeration_tests = function (_a) {
93
+ var sdk = _a.sdk, sdkNonAdmin = _a.sdkNonAdmin;
94
+ return __awaiter(void 0, void 0, void 0, function () {
95
+ var organizationId, sameTenantExistingEmail, rateLimitedAt, i, r, sameTenantRes, sdkCrossOrg, targetOrgId, orgs, _b, crossRes;
96
+ var _c, _d, _e;
97
+ return __generator(this, function (_f) {
98
+ switch (_f.label) {
99
+ case 0:
100
+ log_header("F-0007: users.invite_user cross-tenant enumeration regression");
101
+ // Reset state so prior tests' rate-limit accounting doesn't leak in.
102
+ return [4 /*yield*/, sdk.reset_db()];
103
+ case 1:
104
+ // Reset state so prior tests' rate-limit accounting doesn't leak in.
105
+ _f.sent();
106
+ organizationId = (_d = (_c = sdk.userInfo.organizationIds) === null || _c === void 0 ? void 0 : _c[0]) !== null && _d !== void 0 ? _d : sdk.userInfo.businessId;
107
+ sameTenantExistingEmail = process.env.TEST_EMAIL;
108
+ rateLimitedAt = -1;
109
+ i = 0;
110
+ _f.label = 2;
111
+ case 2:
112
+ if (!(i < 15)) return [3 /*break*/, 5];
113
+ return [4 /*yield*/, post('/v1/invite-user-to-organization', {
114
+ email: sameTenantExistingEmail,
115
+ fname: 'F0007', lname: 'RateLimit',
116
+ organizationId: organizationId,
117
+ }, { Authorization: "Bearer ".concat(sdk.authToken) })];
118
+ case 3:
119
+ r = _f.sent();
120
+ if (r.status === 429) {
121
+ rateLimitedAt = i;
122
+ return [3 /*break*/, 5];
123
+ }
124
+ _f.label = 4;
125
+ case 4:
126
+ i++;
127
+ return [3 /*break*/, 2];
128
+ case 5: return [4 /*yield*/, async_test("F-0007: invite_user rate-limits within ~12 rapid requests (defense-in-depth; no invites sent)", function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
129
+ return [2 /*return*/, ({ rateLimitedAt: rateLimitedAt })];
130
+ }); }); }, { onResult: function (r) { return r.rateLimitedAt >= 0 && r.rateLimitedAt <= 12; } })
131
+ // ====================================================================
132
+ // Assertion 2: same-tenant duplicate returns the descriptive error
133
+ // (unchanged by the fix; regression guard so a future change to the
134
+ // duplicate-detection path doesn't accidentally suppress same-tenant
135
+ // errors that operators rely on).
136
+ // ====================================================================
137
+ // Let rate limit decay so this single call isn't blocked.
138
+ ];
139
+ case 6:
140
+ _f.sent();
141
+ // ====================================================================
142
+ // Assertion 2: same-tenant duplicate returns the descriptive error
143
+ // (unchanged by the fix; regression guard so a future change to the
144
+ // duplicate-detection path doesn't accidentally suppress same-tenant
145
+ // errors that operators rely on).
146
+ // ====================================================================
147
+ // Let rate limit decay so this single call isn't blocked.
148
+ return [4 /*yield*/, wait(undefined, 5000)];
149
+ case 7:
150
+ // ====================================================================
151
+ // Assertion 2: same-tenant duplicate returns the descriptive error
152
+ // (unchanged by the fix; regression guard so a future change to the
153
+ // duplicate-detection path doesn't accidentally suppress same-tenant
154
+ // errors that operators rely on).
155
+ // ====================================================================
156
+ // Let rate limit decay so this single call isn't blocked.
157
+ _f.sent();
158
+ return [4 /*yield*/, sdk.reset_db()];
159
+ case 8:
160
+ _f.sent();
161
+ return [4 /*yield*/, post('/v1/invite-user-to-organization', {
162
+ email: sameTenantExistingEmail,
163
+ fname: 'F0007', lname: 'SameTenant',
164
+ organizationId: organizationId,
165
+ }, { Authorization: "Bearer ".concat(sdk.authToken) })];
166
+ case 9:
167
+ sameTenantRes = _f.sent();
168
+ return [4 /*yield*/, async_test("F-0007: invite_user same-tenant duplicate returns 400 'already exists' (unchanged)", function () { return __awaiter(void 0, void 0, void 0, function () {
169
+ var _a, _b;
170
+ return __generator(this, function (_c) {
171
+ return [2 /*return*/, ({
172
+ status: sameTenantRes.status,
173
+ message: (_b = (_a = sameTenantRes.data) === null || _a === void 0 ? void 0 : _a.message) !== null && _b !== void 0 ? _b : '',
174
+ })];
175
+ });
176
+ }); }, { onResult: function (r) { return r.status === 400 && r.message.toLowerCase().includes('already exists'); } })
177
+ // ====================================================================
178
+ // Assertion 3: cross-tenant duplicate must NOT reveal existence
179
+ // Env-gated; skipped when cross-org infra not configured.
180
+ // ====================================================================
181
+ ];
182
+ case 10:
183
+ _f.sent();
184
+ // ====================================================================
185
+ // Assertion 3: cross-tenant duplicate must NOT reveal existence
186
+ // Env-gated; skipped when cross-org infra not configured.
187
+ // ====================================================================
188
+ if (!(CROSS_ORG_API_KEY && CROSS_ORG_TARGET_BUSINESS_ID)) {
189
+ console.log(" [F-0007] Skipping cross-tenant silent no-op assertion — CROSS_ORG_* env vars not set");
190
+ return [2 /*return*/];
191
+ }
192
+ sdkCrossOrg = new Session({
193
+ host: host,
194
+ apiKey: CROSS_ORG_API_KEY,
195
+ headers: { 'x-tellescope-organization': CROSS_ORG_TARGET_BUSINESS_ID },
196
+ });
197
+ targetOrgId = CROSS_ORG_TARGET_BUSINESS_ID;
198
+ _f.label = 11;
199
+ case 11:
200
+ _f.trys.push([11, 13, , 14]);
201
+ return [4 /*yield*/, sdkCrossOrg.api.organizations.getSome({ limit: 1 })];
202
+ case 12:
203
+ orgs = _f.sent();
204
+ if ((_e = orgs === null || orgs === void 0 ? void 0 : orgs[0]) === null || _e === void 0 ? void 0 : _e.id)
205
+ targetOrgId = orgs[0].id;
206
+ return [3 /*break*/, 14];
207
+ case 13:
208
+ _b = _f.sent();
209
+ return [3 /*break*/, 14];
210
+ case 14: return [4 /*yield*/, post('/v1/invite-user-to-organization', {
211
+ email: sameTenantExistingEmail,
212
+ fname: 'F0007', lname: 'CrossTenantProbe',
213
+ organizationId: targetOrgId,
214
+ }, {
215
+ Authorization: "API_KEY ".concat(CROSS_ORG_API_KEY),
216
+ 'x-tellescope-organization': CROSS_ORG_TARGET_BUSINESS_ID,
217
+ })];
218
+ case 15:
219
+ crossRes = _f.sent();
220
+ return [4 /*yield*/, async_test("F-0007: cross-tenant invite of existing email must NOT return 'already exists' (no enumeration)", function () { return __awaiter(void 0, void 0, void 0, function () {
221
+ var _a, _b, _c, _d;
222
+ return __generator(this, function (_e) {
223
+ return [2 /*return*/, ({
224
+ status: crossRes.status,
225
+ message: (_b = (_a = crossRes.data) === null || _a === void 0 ? void 0 : _a.message) !== null && _b !== void 0 ? _b : '',
226
+ hasCreatedShape: !!((_d = (_c = crossRes.data) === null || _c === void 0 ? void 0 : _c.created) === null || _d === void 0 ? void 0 : _d.id),
227
+ })];
228
+ });
229
+ }); }, {
230
+ onResult: function (r) {
231
+ // The response MUST NOT contain the "already exists" string regardless of status.
232
+ // Acceptable post-fix shapes: 200 with `{ created: { id: ... } }` (silent no-op), or
233
+ // 200 with a generic ack. Rate-limit 429 also OK if it slipped through to here.
234
+ return !r.message.toLowerCase().includes('already exists');
235
+ },
236
+ })];
237
+ case 16:
238
+ _f.sent();
239
+ return [2 /*return*/];
240
+ }
241
+ });
242
+ });
243
+ };
244
+ if (require.main === module) {
245
+ console.log("\uD83C\uDF10 Using API URL: ".concat(host));
246
+ var sdk_1 = new Session({ host: host });
247
+ var sdkNonAdmin_1 = new Session({ host: host });
248
+ var runTests = function () { return __awaiter(void 0, void 0, void 0, function () {
249
+ return __generator(this, function (_a) {
250
+ switch (_a.label) {
251
+ case 0: return [4 /*yield*/, setup_tests(sdk_1, sdkNonAdmin_1)];
252
+ case 1:
253
+ _a.sent();
254
+ return [4 /*yield*/, invite_user_enumeration_tests({ sdk: sdk_1, sdkNonAdmin: sdkNonAdmin_1 })];
255
+ case 2:
256
+ _a.sent();
257
+ return [2 /*return*/];
258
+ }
259
+ });
260
+ }); };
261
+ runTests()
262
+ .then(function () {
263
+ console.log("✅ F-0007 invite_user enumeration test suite completed successfully");
264
+ process.exit(0);
265
+ })
266
+ .catch(function (error) {
267
+ console.error("❌ F-0007 invite_user enumeration test suite failed:", error);
268
+ process.exit(1);
269
+ });
270
+ }
271
+ //# sourceMappingURL=F-0007-invite-user-enumeration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"F-0007-invite-user-enumeration.test.js","sourceRoot":"","sources":["../../../../../src/tests/api_tests/security/F-0007-invite-user-enumeration.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { Session } from "../../../sdk";
2
+ /**
3
+ * Regression test for F-0008 (security-audit/findings/F-0008-handle-incoming-communication-cross-tenant-enduser-lookup.md).
4
+ *
5
+ * `journeys.handle_incoming_communication` previously used `buildAllQueries({ unrestricted: true, organizationIds: [] }).endusers.findById(enduserId)`,
6
+ * permitting cross-tenant lookup of any enduser by id. The handler then called `handleIncomingCommunication(...)`
7
+ * against the matched enduser, triggering journey progression and automated actions on someone else's tenant.
8
+ *
9
+ * The fix switches to the standard tenant-scoped `DB.endusers.findById(enduserId)` wrapper, which automatically
10
+ * filters by `req.session.businessId`. Cross-tenant lookups now return null → handler returns 404 → no side effect.
11
+ *
12
+ * Note: the same-tenant happy path is already covered by the existing test at
13
+ * `packages/public/sdk/src/tests/tests.ts:7588` ("handle_incoming_communication test for other enduser") — that
14
+ * test creates endusers in the test tenant, sets up journeys, calls handle_incoming_communication, and asserts
15
+ * journey-step cancellation. This file covers the negative cases only.
16
+ *
17
+ * **Negative-only by design**: the test never drives `handleIncomingCommunication` against a cross-tenant
18
+ * enduser — the post-fix code returns 404 before any side effects fire, and the assertion confirms that.
19
+ */
20
+ export declare const handle_incoming_communication_cross_tenant_tests: ({ sdk, sdkNonAdmin }: {
21
+ sdk: Session;
22
+ sdkNonAdmin: Session;
23
+ }) => Promise<void>;
24
+ //# sourceMappingURL=F-0008-handle-incoming-communication-cross-tenant.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"F-0008-handle-incoming-communication-cross-tenant.test.d.ts","sourceRoot":"","sources":["../../../../../src/tests/api_tests/security/F-0008-handle-incoming-communication-cross-tenant.test.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAwBtC;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,gDAAgD;SAAwC,OAAO;iBAAe,OAAO;mBA8DjI,CAAA"}
@@ -0,0 +1,194 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ var __generator = (this && this.__generator) || function (thisArg, body) {
11
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
12
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
13
+ function verb(n) { return function (v) { return step([n, v]); }; }
14
+ function step(op) {
15
+ if (f) throw new TypeError("Generator is already executing.");
16
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
17
+ 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;
18
+ if (y = 0, t) op = [op[0] & 2, t.value];
19
+ switch (op[0]) {
20
+ case 0: case 1: t = op; break;
21
+ case 4: _.label++; return { value: op[1], done: false };
22
+ case 5: _.label++; y = op[1]; op = [0]; continue;
23
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
24
+ default:
25
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
26
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
27
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
28
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
29
+ if (t[2]) _.ops.pop();
30
+ _.trys.pop(); continue;
31
+ }
32
+ op = body.call(thisArg, _);
33
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
34
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
35
+ }
36
+ };
37
+ require('source-map-support').install();
38
+ import axios from "axios";
39
+ import { ObjectId } from 'bson';
40
+ import { Session } from "../../../sdk";
41
+ import { async_test, log_header, } from "@tellescope/testing";
42
+ import { setup_tests } from "../../setup";
43
+ var host = process.env.API_URL || 'http://localhost:8080';
44
+ var CROSS_ORG_API_KEY = process.env.CROSS_ORG_API_KEY;
45
+ var CROSS_ORG_TARGET_BUSINESS_ID = process.env.CROSS_ORG_TARGET_BUSINESS_ID;
46
+ var post = function (path, body, headers) {
47
+ if (headers === void 0) { headers = {}; }
48
+ return __awaiter(void 0, void 0, void 0, function () {
49
+ var res, err_1;
50
+ var _a, _b;
51
+ return __generator(this, function (_c) {
52
+ switch (_c.label) {
53
+ case 0:
54
+ _c.trys.push([0, 2, , 3]);
55
+ return [4 /*yield*/, axios.post("".concat(host).concat(path), body, {
56
+ validateStatus: function () { return true; },
57
+ headers: headers,
58
+ })];
59
+ case 1:
60
+ res = _c.sent();
61
+ return [2 /*return*/, { status: res.status, data: res.data }];
62
+ case 2:
63
+ err_1 = _c.sent();
64
+ return [2 /*return*/, { status: (_a = err_1 === null || err_1 === void 0 ? void 0 : err_1.response) === null || _a === void 0 ? void 0 : _a.status, data: (_b = err_1 === null || err_1 === void 0 ? void 0 : err_1.response) === null || _b === void 0 ? void 0 : _b.data }];
65
+ case 3: return [2 /*return*/];
66
+ }
67
+ });
68
+ });
69
+ };
70
+ /**
71
+ * Regression test for F-0008 (security-audit/findings/F-0008-handle-incoming-communication-cross-tenant-enduser-lookup.md).
72
+ *
73
+ * `journeys.handle_incoming_communication` previously used `buildAllQueries({ unrestricted: true, organizationIds: [] }).endusers.findById(enduserId)`,
74
+ * permitting cross-tenant lookup of any enduser by id. The handler then called `handleIncomingCommunication(...)`
75
+ * against the matched enduser, triggering journey progression and automated actions on someone else's tenant.
76
+ *
77
+ * The fix switches to the standard tenant-scoped `DB.endusers.findById(enduserId)` wrapper, which automatically
78
+ * filters by `req.session.businessId`. Cross-tenant lookups now return null → handler returns 404 → no side effect.
79
+ *
80
+ * Note: the same-tenant happy path is already covered by the existing test at
81
+ * `packages/public/sdk/src/tests/tests.ts:7588` ("handle_incoming_communication test for other enduser") — that
82
+ * test creates endusers in the test tenant, sets up journeys, calls handle_incoming_communication, and asserts
83
+ * journey-step cancellation. This file covers the negative cases only.
84
+ *
85
+ * **Negative-only by design**: the test never drives `handleIncomingCommunication` against a cross-tenant
86
+ * enduser — the post-fix code returns 404 before any side effects fire, and the assertion confirms that.
87
+ */
88
+ export var handle_incoming_communication_cross_tenant_tests = function (_a) {
89
+ var sdk = _a.sdk, sdkNonAdmin = _a.sdkNonAdmin;
90
+ return __awaiter(void 0, void 0, void 0, function () {
91
+ var nonexistentId, nonexistentRes, sdkCrossOrg, ts, crossEnduser, crossRes_1, _b;
92
+ return __generator(this, function (_c) {
93
+ switch (_c.label) {
94
+ case 0:
95
+ log_header("F-0008: handle_incoming_communication cross-tenant rejection");
96
+ nonexistentId = new ObjectId().toHexString();
97
+ return [4 /*yield*/, post('/v1/journeys/handle-incoming-communication', { enduserId: nonexistentId }, { Authorization: "Bearer ".concat(sdk.authToken) })];
98
+ case 1:
99
+ nonexistentRes = _c.sent();
100
+ return [4 /*yield*/, async_test("F-0008: handle_incoming_communication with nonexistent enduserId returns 404", function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
101
+ return [2 /*return*/, ({ status: nonexistentRes.status })];
102
+ }); }); }, { onResult: function (r) { return r.status === 404; } })
103
+ // ====================================================================
104
+ // Assertion 2: cross-tenant enduserId returns 404 (the actual F-0008 fix).
105
+ // Env-gated; skipped when cross-org infra isn't configured.
106
+ // Safe to run post-fix because the tenant-scoped DB returns null for
107
+ // cross-tenant lookups — no handleIncomingCommunication side effect fires.
108
+ // ====================================================================
109
+ ];
110
+ case 2:
111
+ _c.sent();
112
+ // ====================================================================
113
+ // Assertion 2: cross-tenant enduserId returns 404 (the actual F-0008 fix).
114
+ // Env-gated; skipped when cross-org infra isn't configured.
115
+ // Safe to run post-fix because the tenant-scoped DB returns null for
116
+ // cross-tenant lookups — no handleIncomingCommunication side effect fires.
117
+ // ====================================================================
118
+ if (!(CROSS_ORG_API_KEY && CROSS_ORG_TARGET_BUSINESS_ID)) {
119
+ console.log(" [F-0008] Skipping cross-tenant rejection assertion — CROSS_ORG_* env vars not set");
120
+ return [2 /*return*/];
121
+ }
122
+ sdkCrossOrg = new Session({
123
+ host: host,
124
+ apiKey: CROSS_ORG_API_KEY,
125
+ headers: { 'x-tellescope-organization': CROSS_ORG_TARGET_BUSINESS_ID },
126
+ });
127
+ ts = Date.now();
128
+ return [4 /*yield*/, sdkCrossOrg.api.endusers.createOne({
129
+ fname: 'F0008CrossTenant', lname: 'Sentinel',
130
+ email: "f0008-cross-".concat(ts, "@tellescope.com"),
131
+ })];
132
+ case 3:
133
+ crossEnduser = _c.sent();
134
+ _c.label = 4;
135
+ case 4:
136
+ _c.trys.push([4, , 7, 11]);
137
+ return [4 /*yield*/, post('/v1/journeys/handle-incoming-communication', { enduserId: crossEnduser.id }, { Authorization: "Bearer ".concat(sdk.authToken) })];
138
+ case 5:
139
+ crossRes_1 = _c.sent();
140
+ return [4 /*yield*/, async_test("F-0008: handle_incoming_communication with cross-tenant enduserId returns 404 (no side effect)", function () { return __awaiter(void 0, void 0, void 0, function () {
141
+ var _a, _b;
142
+ return __generator(this, function (_c) {
143
+ return [2 /*return*/, ({
144
+ status: crossRes_1.status,
145
+ message: (_b = (_a = crossRes_1.data) === null || _a === void 0 ? void 0 : _a.message) !== null && _b !== void 0 ? _b : null,
146
+ })];
147
+ });
148
+ }); }, { onResult: function (r) { return r.status === 404; } })];
149
+ case 6:
150
+ _c.sent();
151
+ return [3 /*break*/, 11];
152
+ case 7:
153
+ _c.trys.push([7, 9, , 10]);
154
+ return [4 /*yield*/, sdkCrossOrg.api.endusers.deleteOne(crossEnduser.id)];
155
+ case 8:
156
+ _c.sent();
157
+ return [3 /*break*/, 10];
158
+ case 9:
159
+ _b = _c.sent();
160
+ return [3 /*break*/, 10];
161
+ case 10: return [7 /*endfinally*/];
162
+ case 11: return [2 /*return*/];
163
+ }
164
+ });
165
+ });
166
+ };
167
+ if (require.main === module) {
168
+ console.log("\uD83C\uDF10 Using API URL: ".concat(host));
169
+ var sdk_1 = new Session({ host: host });
170
+ var sdkNonAdmin_1 = new Session({ host: host });
171
+ var runTests = function () { return __awaiter(void 0, void 0, void 0, function () {
172
+ return __generator(this, function (_a) {
173
+ switch (_a.label) {
174
+ case 0: return [4 /*yield*/, setup_tests(sdk_1, sdkNonAdmin_1)];
175
+ case 1:
176
+ _a.sent();
177
+ return [4 /*yield*/, handle_incoming_communication_cross_tenant_tests({ sdk: sdk_1, sdkNonAdmin: sdkNonAdmin_1 })];
178
+ case 2:
179
+ _a.sent();
180
+ return [2 /*return*/];
181
+ }
182
+ });
183
+ }); };
184
+ runTests()
185
+ .then(function () {
186
+ console.log("✅ F-0008 handle_incoming_communication cross-tenant test suite completed successfully");
187
+ process.exit(0);
188
+ })
189
+ .catch(function (error) {
190
+ console.error("❌ F-0008 handle_incoming_communication cross-tenant test suite failed:", error);
191
+ process.exit(1);
192
+ });
193
+ }
194
+ //# sourceMappingURL=F-0008-handle-incoming-communication-cross-tenant.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"F-0008-handle-incoming-communication-cross-tenant.test.js","sourceRoot":"","sources":["../../../../../src/tests/api_tests/security/F-0008-handle-incoming-communication-cross-tenant.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare const sanitize_user_html_xss_tests: () => Promise<void>;
2
+ //# sourceMappingURL=F-0013-sanitize-user-html.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"F-0013-sanitize-user-html.test.d.ts","sourceRoot":"","sources":["../../../../../src/tests/api_tests/security/F-0013-sanitize-user-html.test.ts"],"names":[],"mappings":"AAgCA,eAAO,MAAM,4BAA4B,qBAsExC,CAAA"}