@tellescope/sdk 1.256.13 → 1.256.15

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 (352) hide show
  1. package/lib/cjs/tests/api_tests/auto_merge_form_submission.test.js +1 -1
  2. package/lib/cjs/tests/api_tests/auto_merge_form_submission.test.js.map +1 -1
  3. package/lib/cjs/tests/api_tests/enduser_observations_auto_review.test.d.ts +5 -0
  4. package/lib/cjs/tests/api_tests/enduser_observations_auto_review.test.d.ts.map +1 -0
  5. package/lib/cjs/tests/api_tests/enduser_observations_auto_review.test.js +388 -0
  6. package/lib/cjs/tests/api_tests/enduser_observations_auto_review.test.js.map +1 -0
  7. package/lib/cjs/tests/api_tests/integrations_redacted.test.js +3 -3
  8. package/lib/cjs/tests/api_tests/integrations_redacted.test.js.map +1 -1
  9. package/lib/cjs/tests/api_tests/organization_signup.test.d.ts +20 -0
  10. package/lib/cjs/tests/api_tests/organization_signup.test.d.ts.map +1 -0
  11. package/lib/cjs/tests/api_tests/organization_signup.test.js +272 -0
  12. package/lib/cjs/tests/api_tests/organization_signup.test.js.map +1 -0
  13. package/lib/cjs/tests/api_tests/purchase_made_trigger.test.d.ts.map +1 -1
  14. package/lib/cjs/tests/api_tests/purchase_made_trigger.test.js +103 -162
  15. package/lib/cjs/tests/api_tests/purchase_made_trigger.test.js.map +1 -1
  16. package/lib/cjs/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.d.ts +30 -0
  17. package/lib/cjs/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.d.ts.map +1 -0
  18. package/lib/cjs/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.js +336 -0
  19. package/lib/cjs/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.js.map +1 -0
  20. package/lib/cjs/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.d.ts +6 -0
  21. package/lib/cjs/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.d.ts.map +1 -0
  22. package/lib/cjs/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.js +354 -0
  23. package/lib/cjs/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.js.map +1 -0
  24. package/lib/cjs/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.d.ts +46 -0
  25. package/lib/cjs/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.d.ts.map +1 -0
  26. package/lib/cjs/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.js +389 -0
  27. package/lib/cjs/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.js.map +1 -0
  28. package/lib/cjs/tests/api_tests/security/F-0192-automated-actions-process-gate.test.d.ts +43 -0
  29. package/lib/cjs/tests/api_tests/security/F-0192-automated-actions-process-gate.test.d.ts.map +1 -0
  30. package/lib/cjs/tests/api_tests/security/F-0192-automated-actions-process-gate.test.js +324 -0
  31. package/lib/cjs/tests/api_tests/security/F-0192-automated-actions-process-gate.test.js.map +1 -0
  32. package/lib/cjs/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.d.ts +43 -0
  33. package/lib/cjs/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.d.ts.map +1 -0
  34. package/lib/cjs/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.js +282 -0
  35. package/lib/cjs/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.js.map +1 -0
  36. package/lib/cjs/tests/api_tests/security/F-0200-form-logic-js-execution.test.d.ts +33 -0
  37. package/lib/cjs/tests/api_tests/security/F-0200-form-logic-js-execution.test.d.ts.map +1 -0
  38. package/lib/cjs/tests/api_tests/security/F-0200-form-logic-js-execution.test.js +249 -0
  39. package/lib/cjs/tests/api_tests/security/F-0200-form-logic-js-execution.test.js.map +1 -0
  40. package/lib/cjs/tests/api_tests/security/add-to-journey-foreign-enduserid.test.d.ts +42 -0
  41. package/lib/cjs/tests/api_tests/security/add-to-journey-foreign-enduserid.test.d.ts.map +1 -0
  42. package/lib/cjs/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js +281 -0
  43. package/lib/cjs/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js.map +1 -0
  44. package/lib/cjs/tests/api_tests/security/ai-conversations-rbac.test.d.ts +38 -0
  45. package/lib/cjs/tests/api_tests/security/ai-conversations-rbac.test.d.ts.map +1 -0
  46. package/lib/cjs/tests/api_tests/security/ai-conversations-rbac.test.js +390 -0
  47. package/lib/cjs/tests/api_tests/security/ai-conversations-rbac.test.js.map +1 -0
  48. package/lib/cjs/tests/api_tests/security/allowed-paths-confinement.test.d.ts +6 -0
  49. package/lib/cjs/tests/api_tests/security/allowed-paths-confinement.test.d.ts.map +1 -0
  50. package/lib/cjs/tests/api_tests/security/allowed-paths-confinement.test.js +1003 -0
  51. package/lib/cjs/tests/api_tests/security/allowed-paths-confinement.test.js.map +1 -0
  52. package/lib/cjs/tests/api_tests/security/automated-actions-process-gate.test.d.ts +42 -0
  53. package/lib/cjs/tests/api_tests/security/automated-actions-process-gate.test.d.ts.map +1 -0
  54. package/lib/cjs/tests/api_tests/security/automated-actions-process-gate.test.js +323 -0
  55. package/lib/cjs/tests/api_tests/security/automated-actions-process-gate.test.js.map +1 -0
  56. package/lib/cjs/tests/api_tests/security/booking-info-idor-token-mint.test.d.ts +29 -0
  57. package/lib/cjs/tests/api_tests/security/booking-info-idor-token-mint.test.d.ts.map +1 -0
  58. package/lib/cjs/tests/api_tests/security/booking-info-idor-token-mint.test.js +335 -0
  59. package/lib/cjs/tests/api_tests/security/booking-info-idor-token-mint.test.js.map +1 -0
  60. package/lib/cjs/tests/api_tests/security/cascade-role-rename-cross-tenant.test.d.ts +32 -0
  61. package/lib/cjs/tests/api_tests/security/cascade-role-rename-cross-tenant.test.d.ts.map +1 -0
  62. package/lib/cjs/tests/api_tests/security/cascade-role-rename-cross-tenant.test.js +237 -0
  63. package/lib/cjs/tests/api_tests/security/cascade-role-rename-cross-tenant.test.js.map +1 -0
  64. package/lib/cjs/tests/api_tests/security/data-sync-redaction-bypass.test.d.ts +28 -0
  65. package/lib/cjs/tests/api_tests/security/data-sync-redaction-bypass.test.d.ts.map +1 -0
  66. package/lib/cjs/tests/api_tests/security/data-sync-redaction-bypass.test.js +349 -0
  67. package/lib/cjs/tests/api_tests/security/data-sync-redaction-bypass.test.js.map +1 -0
  68. package/lib/cjs/tests/api_tests/security/develop-health-benefit-verification-rbac.test.d.ts +30 -0
  69. package/lib/cjs/tests/api_tests/security/develop-health-benefit-verification-rbac.test.d.ts.map +1 -0
  70. package/lib/cjs/tests/api_tests/security/develop-health-benefit-verification-rbac.test.js +314 -0
  71. package/lib/cjs/tests/api_tests/security/develop-health-benefit-verification-rbac.test.js.map +1 -0
  72. package/lib/cjs/tests/api_tests/security/enduser-cross-create.test.d.ts +21 -0
  73. package/lib/cjs/tests/api_tests/security/enduser-cross-create.test.d.ts.map +1 -0
  74. package/lib/cjs/tests/api_tests/security/enduser-cross-create.test.js +362 -0
  75. package/lib/cjs/tests/api_tests/security/enduser-cross-create.test.js.map +1 -0
  76. package/lib/cjs/tests/api_tests/security/enduser-write-restrictions.test.d.ts +21 -0
  77. package/lib/cjs/tests/api_tests/security/enduser-write-restrictions.test.d.ts.map +1 -0
  78. package/lib/cjs/tests/api_tests/security/enduser-write-restrictions.test.js +366 -0
  79. package/lib/cjs/tests/api_tests/security/enduser-write-restrictions.test.js.map +1 -0
  80. package/lib/cjs/tests/api_tests/security/form-logic-js-execution.test.d.ts +32 -0
  81. package/lib/cjs/tests/api_tests/security/form-logic-js-execution.test.d.ts.map +1 -0
  82. package/lib/cjs/tests/api_tests/security/form-logic-js-execution.test.js +248 -0
  83. package/lib/cjs/tests/api_tests/security/form-logic-js-execution.test.js.map +1 -0
  84. package/lib/cjs/tests/api_tests/security/globalunique-conflict-redaction.test.d.ts +19 -0
  85. package/lib/cjs/tests/api_tests/security/globalunique-conflict-redaction.test.d.ts.map +1 -0
  86. package/lib/cjs/tests/api_tests/security/globalunique-conflict-redaction.test.js +138 -0
  87. package/lib/cjs/tests/api_tests/security/globalunique-conflict-redaction.test.js.map +1 -0
  88. package/lib/cjs/tests/api_tests/security/handle-incoming-communication-cross-tenant.test.d.ts +24 -0
  89. package/lib/cjs/tests/api_tests/security/handle-incoming-communication-cross-tenant.test.d.ts.map +1 -0
  90. package/lib/cjs/tests/api_tests/security/handle-incoming-communication-cross-tenant.test.js +201 -0
  91. package/lib/cjs/tests/api_tests/security/handle-incoming-communication-cross-tenant.test.js.map +1 -0
  92. package/lib/cjs/tests/api_tests/security/invite-user-enumeration.test.d.ts +29 -0
  93. package/lib/cjs/tests/api_tests/security/invite-user-enumeration.test.d.ts.map +1 -0
  94. package/lib/cjs/tests/api_tests/security/invite-user-enumeration.test.js +278 -0
  95. package/lib/cjs/tests/api_tests/security/invite-user-enumeration.test.js.map +1 -0
  96. package/lib/cjs/tests/api_tests/security/load-inbox-redaction.test.d.ts +17 -0
  97. package/lib/cjs/tests/api_tests/security/load-inbox-redaction.test.d.ts.map +1 -0
  98. package/lib/cjs/tests/api_tests/security/load-inbox-redaction.test.js +155 -0
  99. package/lib/cjs/tests/api_tests/security/load-inbox-redaction.test.js.map +1 -0
  100. package/lib/cjs/tests/api_tests/security/prototype-pollution.test.d.ts +2 -0
  101. package/lib/cjs/tests/api_tests/security/prototype-pollution.test.d.ts.map +1 -0
  102. package/lib/cjs/tests/api_tests/security/prototype-pollution.test.js +88 -0
  103. package/lib/cjs/tests/api_tests/security/prototype-pollution.test.js.map +1 -0
  104. package/lib/cjs/tests/api_tests/security/proxy-image-unauth-ssrf.test.d.ts +34 -0
  105. package/lib/cjs/tests/api_tests/security/proxy-image-unauth-ssrf.test.d.ts.map +1 -0
  106. package/lib/cjs/tests/api_tests/security/proxy-image-unauth-ssrf.test.js +230 -0
  107. package/lib/cjs/tests/api_tests/security/proxy-image-unauth-ssrf.test.js.map +1 -0
  108. package/lib/cjs/tests/api_tests/security/sanitize-user-html-with-iframes.test.d.ts +2 -0
  109. package/lib/cjs/tests/api_tests/security/sanitize-user-html-with-iframes.test.d.ts.map +1 -0
  110. package/lib/cjs/tests/api_tests/security/sanitize-user-html-with-iframes.test.js +242 -0
  111. package/lib/cjs/tests/api_tests/security/sanitize-user-html-with-iframes.test.js.map +1 -0
  112. package/lib/cjs/tests/api_tests/security/sanitize-user-html.test.d.ts +2 -0
  113. package/lib/cjs/tests/api_tests/security/sanitize-user-html.test.d.ts.map +1 -0
  114. package/lib/cjs/tests/api_tests/security/sanitize-user-html.test.js +148 -0
  115. package/lib/cjs/tests/api_tests/security/sanitize-user-html.test.js.map +1 -0
  116. package/lib/cjs/tests/api_tests/security/schema-adminonly-enforcement.test.d.ts +45 -0
  117. package/lib/cjs/tests/api_tests/security/schema-adminonly-enforcement.test.d.ts.map +1 -0
  118. package/lib/cjs/tests/api_tests/security/schema-adminonly-enforcement.test.js +388 -0
  119. package/lib/cjs/tests/api_tests/security/schema-adminonly-enforcement.test.js.map +1 -0
  120. package/lib/cjs/tests/api_tests/security/self-admin-role-assignment.test.d.ts +38 -0
  121. package/lib/cjs/tests/api_tests/security/self-admin-role-assignment.test.d.ts.map +1 -0
  122. package/lib/cjs/tests/api_tests/security/self-admin-role-assignment.test.js +222 -0
  123. package/lib/cjs/tests/api_tests/security/self-admin-role-assignment.test.js.map +1 -0
  124. package/lib/cjs/tests/api_tests/security/startlinktoken-enduser-staff-escalation.test.d.ts +6 -0
  125. package/lib/cjs/tests/api_tests/security/startlinktoken-enduser-staff-escalation.test.d.ts.map +1 -0
  126. package/lib/cjs/tests/api_tests/security/startlinktoken-enduser-staff-escalation.test.js +354 -0
  127. package/lib/cjs/tests/api_tests/security/startlinktoken-enduser-staff-escalation.test.js.map +1 -0
  128. package/lib/cjs/tests/api_tests/security/webhook-outbound-ssrf.test.d.ts +28 -0
  129. package/lib/cjs/tests/api_tests/security/webhook-outbound-ssrf.test.d.ts.map +1 -0
  130. package/lib/cjs/tests/api_tests/security/webhook-outbound-ssrf.test.js +442 -0
  131. package/lib/cjs/tests/api_tests/security/webhook-outbound-ssrf.test.js.map +1 -0
  132. package/lib/cjs/tests/api_tests/security/webhook-timeout.test.d.ts +19 -0
  133. package/lib/cjs/tests/api_tests/security/webhook-timeout.test.d.ts.map +1 -0
  134. package/lib/cjs/tests/api_tests/security/webhook-timeout.test.js +156 -0
  135. package/lib/cjs/tests/api_tests/security/webhook-timeout.test.js.map +1 -0
  136. package/lib/cjs/tests/api_tests/tickets_bulk_assign_from_queue.test.d.ts +5 -0
  137. package/lib/cjs/tests/api_tests/tickets_bulk_assign_from_queue.test.d.ts.map +1 -0
  138. package/lib/cjs/tests/api_tests/tickets_bulk_assign_from_queue.test.js +329 -0
  139. package/lib/cjs/tests/api_tests/tickets_bulk_assign_from_queue.test.js.map +1 -0
  140. package/lib/cjs/tests/api_tests/webhook_error_handling.test.js +2 -2
  141. package/lib/cjs/tests/api_tests/webhook_error_handling.test.js.map +1 -1
  142. package/lib/cjs/tests/setup_isolated.d.ts +68 -0
  143. package/lib/cjs/tests/setup_isolated.d.ts.map +1 -0
  144. package/lib/cjs/tests/setup_isolated.js +232 -0
  145. package/lib/cjs/tests/setup_isolated.js.map +1 -0
  146. package/lib/cjs/tests/tests.d.ts.map +1 -1
  147. package/lib/cjs/tests/tests.js +287 -227
  148. package/lib/cjs/tests/tests.js.map +1 -1
  149. package/lib/cjs/tests/unit_tests/do_not_send_address.test.d.ts +2 -0
  150. package/lib/cjs/tests/unit_tests/do_not_send_address.test.d.ts.map +1 -0
  151. package/lib/cjs/tests/unit_tests/do_not_send_address.test.js +89 -0
  152. package/lib/cjs/tests/unit_tests/do_not_send_address.test.js.map +1 -0
  153. package/lib/cjs/tests/unit_tests/openapi_spec_freshness.test.d.ts +17 -0
  154. package/lib/cjs/tests/unit_tests/openapi_spec_freshness.test.d.ts.map +1 -0
  155. package/lib/cjs/tests/unit_tests/openapi_spec_freshness.test.js +140 -0
  156. package/lib/cjs/tests/unit_tests/openapi_spec_freshness.test.js.map +1 -0
  157. package/lib/cjs/tsconfig.cjs.tsbuildinfo +1 -0
  158. package/lib/esm/tests/api_tests/auto_merge_form_submission.test.js +1 -1
  159. package/lib/esm/tests/api_tests/auto_merge_form_submission.test.js.map +1 -1
  160. package/lib/esm/tests/api_tests/enduser_observations_auto_review.test.d.ts +5 -0
  161. package/lib/esm/tests/api_tests/enduser_observations_auto_review.test.d.ts.map +1 -0
  162. package/lib/esm/tests/api_tests/enduser_observations_auto_review.test.js +383 -0
  163. package/lib/esm/tests/api_tests/enduser_observations_auto_review.test.js.map +1 -0
  164. package/lib/esm/tests/api_tests/integrations_redacted.test.js +3 -3
  165. package/lib/esm/tests/api_tests/integrations_redacted.test.js.map +1 -1
  166. package/lib/esm/tests/api_tests/organization_signup.test.d.ts +20 -0
  167. package/lib/esm/tests/api_tests/organization_signup.test.d.ts.map +1 -0
  168. package/lib/esm/tests/api_tests/organization_signup.test.js +264 -0
  169. package/lib/esm/tests/api_tests/organization_signup.test.js.map +1 -0
  170. package/lib/esm/tests/api_tests/purchase_made_trigger.test.d.ts.map +1 -1
  171. package/lib/esm/tests/api_tests/purchase_made_trigger.test.js +104 -163
  172. package/lib/esm/tests/api_tests/purchase_made_trigger.test.js.map +1 -1
  173. package/lib/esm/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.d.ts +30 -0
  174. package/lib/esm/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.d.ts.map +1 -0
  175. package/lib/esm/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.js +332 -0
  176. package/lib/esm/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.js.map +1 -0
  177. package/lib/esm/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.d.ts +6 -0
  178. package/lib/esm/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.d.ts.map +1 -0
  179. package/lib/esm/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.js +350 -0
  180. package/lib/esm/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.js.map +1 -0
  181. package/lib/esm/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.d.ts +46 -0
  182. package/lib/esm/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.d.ts.map +1 -0
  183. package/lib/esm/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.js +382 -0
  184. package/lib/esm/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.js.map +1 -0
  185. package/lib/esm/tests/api_tests/security/F-0192-automated-actions-process-gate.test.d.ts +43 -0
  186. package/lib/esm/tests/api_tests/security/F-0192-automated-actions-process-gate.test.d.ts.map +1 -0
  187. package/lib/esm/tests/api_tests/security/F-0192-automated-actions-process-gate.test.js +317 -0
  188. package/lib/esm/tests/api_tests/security/F-0192-automated-actions-process-gate.test.js.map +1 -0
  189. package/lib/esm/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.d.ts +43 -0
  190. package/lib/esm/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.d.ts.map +1 -0
  191. package/lib/esm/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.js +277 -0
  192. package/lib/esm/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.js.map +1 -0
  193. package/lib/esm/tests/api_tests/security/F-0200-form-logic-js-execution.test.d.ts +33 -0
  194. package/lib/esm/tests/api_tests/security/F-0200-form-logic-js-execution.test.d.ts.map +1 -0
  195. package/lib/esm/tests/api_tests/security/F-0200-form-logic-js-execution.test.js +244 -0
  196. package/lib/esm/tests/api_tests/security/F-0200-form-logic-js-execution.test.js.map +1 -0
  197. package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.d.ts +42 -0
  198. package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.d.ts.map +1 -0
  199. package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js +276 -0
  200. package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js.map +1 -0
  201. package/lib/esm/tests/api_tests/security/ai-conversations-rbac.test.d.ts +38 -0
  202. package/lib/esm/tests/api_tests/security/ai-conversations-rbac.test.d.ts.map +1 -0
  203. package/lib/esm/tests/api_tests/security/ai-conversations-rbac.test.js +386 -0
  204. package/lib/esm/tests/api_tests/security/ai-conversations-rbac.test.js.map +1 -0
  205. package/lib/esm/tests/api_tests/security/allowed-paths-confinement.test.d.ts +6 -0
  206. package/lib/esm/tests/api_tests/security/allowed-paths-confinement.test.d.ts.map +1 -0
  207. package/lib/esm/tests/api_tests/security/allowed-paths-confinement.test.js +996 -0
  208. package/lib/esm/tests/api_tests/security/allowed-paths-confinement.test.js.map +1 -0
  209. package/lib/esm/tests/api_tests/security/automated-actions-process-gate.test.d.ts +42 -0
  210. package/lib/esm/tests/api_tests/security/automated-actions-process-gate.test.d.ts.map +1 -0
  211. package/lib/esm/tests/api_tests/security/automated-actions-process-gate.test.js +316 -0
  212. package/lib/esm/tests/api_tests/security/automated-actions-process-gate.test.js.map +1 -0
  213. package/lib/esm/tests/api_tests/security/booking-info-idor-token-mint.test.d.ts +29 -0
  214. package/lib/esm/tests/api_tests/security/booking-info-idor-token-mint.test.d.ts.map +1 -0
  215. package/lib/esm/tests/api_tests/security/booking-info-idor-token-mint.test.js +331 -0
  216. package/lib/esm/tests/api_tests/security/booking-info-idor-token-mint.test.js.map +1 -0
  217. package/lib/esm/tests/api_tests/security/cascade-role-rename-cross-tenant.test.d.ts +32 -0
  218. package/lib/esm/tests/api_tests/security/cascade-role-rename-cross-tenant.test.d.ts.map +1 -0
  219. package/lib/esm/tests/api_tests/security/cascade-role-rename-cross-tenant.test.js +233 -0
  220. package/lib/esm/tests/api_tests/security/cascade-role-rename-cross-tenant.test.js.map +1 -0
  221. package/lib/esm/tests/api_tests/security/data-sync-redaction-bypass.test.d.ts +28 -0
  222. package/lib/esm/tests/api_tests/security/data-sync-redaction-bypass.test.d.ts.map +1 -0
  223. package/lib/esm/tests/api_tests/security/data-sync-redaction-bypass.test.js +345 -0
  224. package/lib/esm/tests/api_tests/security/data-sync-redaction-bypass.test.js.map +1 -0
  225. package/lib/esm/tests/api_tests/security/develop-health-benefit-verification-rbac.test.d.ts +30 -0
  226. package/lib/esm/tests/api_tests/security/develop-health-benefit-verification-rbac.test.d.ts.map +1 -0
  227. package/lib/esm/tests/api_tests/security/develop-health-benefit-verification-rbac.test.js +310 -0
  228. package/lib/esm/tests/api_tests/security/develop-health-benefit-verification-rbac.test.js.map +1 -0
  229. package/lib/esm/tests/api_tests/security/enduser-cross-create.test.d.ts +21 -0
  230. package/lib/esm/tests/api_tests/security/enduser-cross-create.test.d.ts.map +1 -0
  231. package/lib/esm/tests/api_tests/security/enduser-cross-create.test.js +358 -0
  232. package/lib/esm/tests/api_tests/security/enduser-cross-create.test.js.map +1 -0
  233. package/lib/esm/tests/api_tests/security/enduser-write-restrictions.test.d.ts +21 -0
  234. package/lib/esm/tests/api_tests/security/enduser-write-restrictions.test.d.ts.map +1 -0
  235. package/lib/esm/tests/api_tests/security/enduser-write-restrictions.test.js +362 -0
  236. package/lib/esm/tests/api_tests/security/enduser-write-restrictions.test.js.map +1 -0
  237. package/lib/esm/tests/api_tests/security/form-logic-js-execution.test.d.ts +32 -0
  238. package/lib/esm/tests/api_tests/security/form-logic-js-execution.test.d.ts.map +1 -0
  239. package/lib/esm/tests/api_tests/security/form-logic-js-execution.test.js +243 -0
  240. package/lib/esm/tests/api_tests/security/form-logic-js-execution.test.js.map +1 -0
  241. package/lib/esm/tests/api_tests/security/globalunique-conflict-redaction.test.d.ts +19 -0
  242. package/lib/esm/tests/api_tests/security/globalunique-conflict-redaction.test.d.ts.map +1 -0
  243. package/lib/esm/tests/api_tests/security/globalunique-conflict-redaction.test.js +134 -0
  244. package/lib/esm/tests/api_tests/security/globalunique-conflict-redaction.test.js.map +1 -0
  245. package/lib/esm/tests/api_tests/security/handle-incoming-communication-cross-tenant.test.d.ts +24 -0
  246. package/lib/esm/tests/api_tests/security/handle-incoming-communication-cross-tenant.test.d.ts.map +1 -0
  247. package/lib/esm/tests/api_tests/security/handle-incoming-communication-cross-tenant.test.js +194 -0
  248. package/lib/esm/tests/api_tests/security/handle-incoming-communication-cross-tenant.test.js.map +1 -0
  249. package/lib/esm/tests/api_tests/security/invite-user-enumeration.test.d.ts +29 -0
  250. package/lib/esm/tests/api_tests/security/invite-user-enumeration.test.d.ts.map +1 -0
  251. package/lib/esm/tests/api_tests/security/invite-user-enumeration.test.js +271 -0
  252. package/lib/esm/tests/api_tests/security/invite-user-enumeration.test.js.map +1 -0
  253. package/lib/esm/tests/api_tests/security/load-inbox-redaction.test.d.ts +17 -0
  254. package/lib/esm/tests/api_tests/security/load-inbox-redaction.test.d.ts.map +1 -0
  255. package/lib/esm/tests/api_tests/security/load-inbox-redaction.test.js +151 -0
  256. package/lib/esm/tests/api_tests/security/load-inbox-redaction.test.js.map +1 -0
  257. package/lib/esm/tests/api_tests/security/prototype-pollution.test.d.ts +2 -0
  258. package/lib/esm/tests/api_tests/security/prototype-pollution.test.d.ts.map +1 -0
  259. package/lib/esm/tests/api_tests/security/prototype-pollution.test.js +84 -0
  260. package/lib/esm/tests/api_tests/security/prototype-pollution.test.js.map +1 -0
  261. package/lib/esm/tests/api_tests/security/proxy-image-unauth-ssrf.test.d.ts +34 -0
  262. package/lib/esm/tests/api_tests/security/proxy-image-unauth-ssrf.test.d.ts.map +1 -0
  263. package/lib/esm/tests/api_tests/security/proxy-image-unauth-ssrf.test.js +223 -0
  264. package/lib/esm/tests/api_tests/security/proxy-image-unauth-ssrf.test.js.map +1 -0
  265. package/lib/esm/tests/api_tests/security/sanitize-user-html-with-iframes.test.d.ts +2 -0
  266. package/lib/esm/tests/api_tests/security/sanitize-user-html-with-iframes.test.d.ts.map +1 -0
  267. package/lib/esm/tests/api_tests/security/sanitize-user-html-with-iframes.test.js +238 -0
  268. package/lib/esm/tests/api_tests/security/sanitize-user-html-with-iframes.test.js.map +1 -0
  269. package/lib/esm/tests/api_tests/security/sanitize-user-html.test.d.ts +2 -0
  270. package/lib/esm/tests/api_tests/security/sanitize-user-html.test.d.ts.map +1 -0
  271. package/lib/esm/tests/api_tests/security/sanitize-user-html.test.js +144 -0
  272. package/lib/esm/tests/api_tests/security/sanitize-user-html.test.js.map +1 -0
  273. package/lib/esm/tests/api_tests/security/schema-adminonly-enforcement.test.d.ts +45 -0
  274. package/lib/esm/tests/api_tests/security/schema-adminonly-enforcement.test.d.ts.map +1 -0
  275. package/lib/esm/tests/api_tests/security/schema-adminonly-enforcement.test.js +381 -0
  276. package/lib/esm/tests/api_tests/security/schema-adminonly-enforcement.test.js.map +1 -0
  277. package/lib/esm/tests/api_tests/security/self-admin-role-assignment.test.d.ts +38 -0
  278. package/lib/esm/tests/api_tests/security/self-admin-role-assignment.test.d.ts.map +1 -0
  279. package/lib/esm/tests/api_tests/security/self-admin-role-assignment.test.js +218 -0
  280. package/lib/esm/tests/api_tests/security/self-admin-role-assignment.test.js.map +1 -0
  281. package/lib/esm/tests/api_tests/security/startlinktoken-enduser-staff-escalation.test.d.ts +6 -0
  282. package/lib/esm/tests/api_tests/security/startlinktoken-enduser-staff-escalation.test.d.ts.map +1 -0
  283. package/lib/esm/tests/api_tests/security/startlinktoken-enduser-staff-escalation.test.js +350 -0
  284. package/lib/esm/tests/api_tests/security/startlinktoken-enduser-staff-escalation.test.js.map +1 -0
  285. package/lib/esm/tests/api_tests/security/webhook-outbound-ssrf.test.d.ts +28 -0
  286. package/lib/esm/tests/api_tests/security/webhook-outbound-ssrf.test.d.ts.map +1 -0
  287. package/lib/esm/tests/api_tests/security/webhook-outbound-ssrf.test.js +435 -0
  288. package/lib/esm/tests/api_tests/security/webhook-outbound-ssrf.test.js.map +1 -0
  289. package/lib/esm/tests/api_tests/security/webhook-timeout.test.d.ts +19 -0
  290. package/lib/esm/tests/api_tests/security/webhook-timeout.test.d.ts.map +1 -0
  291. package/lib/esm/tests/api_tests/security/webhook-timeout.test.js +149 -0
  292. package/lib/esm/tests/api_tests/security/webhook-timeout.test.js.map +1 -0
  293. package/lib/esm/tests/api_tests/tickets_bulk_assign_from_queue.test.d.ts +5 -0
  294. package/lib/esm/tests/api_tests/tickets_bulk_assign_from_queue.test.d.ts.map +1 -0
  295. package/lib/esm/tests/api_tests/tickets_bulk_assign_from_queue.test.js +324 -0
  296. package/lib/esm/tests/api_tests/tickets_bulk_assign_from_queue.test.js.map +1 -0
  297. package/lib/esm/tests/api_tests/webhook_error_handling.test.js +2 -2
  298. package/lib/esm/tests/api_tests/webhook_error_handling.test.js.map +1 -1
  299. package/lib/esm/tests/setup_isolated.d.ts +68 -0
  300. package/lib/esm/tests/setup_isolated.d.ts.map +1 -0
  301. package/lib/esm/tests/setup_isolated.js +221 -0
  302. package/lib/esm/tests/setup_isolated.js.map +1 -0
  303. package/lib/esm/tests/tests.d.ts.map +1 -1
  304. package/lib/esm/tests/tests.js +287 -227
  305. package/lib/esm/tests/tests.js.map +1 -1
  306. package/lib/esm/tests/unit_tests/do_not_send_address.test.d.ts +2 -0
  307. package/lib/esm/tests/unit_tests/do_not_send_address.test.d.ts.map +1 -0
  308. package/lib/esm/tests/unit_tests/do_not_send_address.test.js +85 -0
  309. package/lib/esm/tests/unit_tests/do_not_send_address.test.js.map +1 -0
  310. package/lib/esm/tests/unit_tests/openapi_spec_freshness.test.d.ts +17 -0
  311. package/lib/esm/tests/unit_tests/openapi_spec_freshness.test.d.ts.map +1 -0
  312. package/lib/esm/tests/unit_tests/openapi_spec_freshness.test.js +113 -0
  313. package/lib/esm/tests/unit_tests/openapi_spec_freshness.test.js.map +1 -0
  314. package/lib/tsconfig.tsbuildinfo +1 -1
  315. package/package.json +10 -10
  316. package/src/tests/api_tests/auto_merge_form_submission.test.ts +1 -1
  317. package/src/tests/api_tests/enduser_observations_auto_review.test.ts +358 -0
  318. package/src/tests/api_tests/integrations_redacted.test.ts +1 -1
  319. package/src/tests/api_tests/organization_signup.test.ts +240 -0
  320. package/src/tests/api_tests/purchase_made_trigger.test.ts +29 -41
  321. package/src/tests/api_tests/security/add-to-journey-foreign-enduserid.test.ts +207 -0
  322. package/src/tests/api_tests/security/{F-0005-ai-conversations-rbac.test.ts → ai-conversations-rbac.test.ts} +14 -14
  323. package/src/tests/api_tests/security/{F-0166-allowed-paths-wildcard-bypass.test.ts → allowed-paths-confinement.test.ts} +11 -11
  324. package/src/tests/api_tests/security/automated-actions-process-gate.test.ts +237 -0
  325. package/src/tests/api_tests/security/booking-info-idor-token-mint.test.ts +224 -0
  326. package/src/tests/api_tests/security/{F-0053-cascade-role-rename-cross-tenant.test.ts → cascade-role-rename-cross-tenant.test.ts} +10 -10
  327. package/src/tests/api_tests/security/{F-0001-data-sync-redaction-bypass.test.ts → data-sync-redaction-bypass.test.ts} +9 -9
  328. package/src/tests/api_tests/security/{F-0006-develop-health-benefit-verification-rbac.test.ts → develop-health-benefit-verification-rbac.test.ts} +7 -8
  329. package/src/tests/api_tests/security/{F-0116-F-0119-enduser-cross-create.test.ts → enduser-cross-create.test.ts} +25 -25
  330. package/src/tests/api_tests/security/{F-0106-F-0110-enduser-write-restrictions.test.ts → enduser-write-restrictions.test.ts} +12 -14
  331. package/src/tests/api_tests/security/form-logic-js-execution.test.ts +183 -0
  332. package/src/tests/api_tests/security/{F-0145-globalunique-leak.test.ts → globalunique-conflict-redaction.test.ts} +5 -6
  333. package/src/tests/api_tests/security/{F-0008-handle-incoming-communication-cross-tenant.test.ts → handle-incoming-communication-cross-tenant.test.ts} +8 -8
  334. package/src/tests/api_tests/security/{F-0007-invite-user-enumeration.test.ts → invite-user-enumeration.test.ts} +8 -8
  335. package/src/tests/api_tests/security/{F-0151-load-inbox-redaction.test.ts → load-inbox-redaction.test.ts} +5 -6
  336. package/src/tests/api_tests/security/{F-0016-prototype-pollution.test.ts → prototype-pollution.test.ts} +4 -4
  337. package/src/tests/api_tests/security/{F-0025-proxy-image-unauth-ssrf.test.ts → proxy-image-unauth-ssrf.test.ts} +11 -12
  338. package/src/tests/api_tests/security/{F-0013-sanitize-user-html-with-iframes.test.ts → sanitize-user-html-with-iframes.test.ts} +5 -5
  339. package/src/tests/api_tests/security/{F-0013-sanitize-user-html.test.ts → sanitize-user-html.test.ts} +4 -4
  340. package/src/tests/api_tests/security/schema-adminonly-enforcement.test.ts +287 -0
  341. package/src/tests/api_tests/security/{F-0076-self-admin-role-assignment.test.ts → self-admin-role-assignment.test.ts} +13 -13
  342. package/src/tests/api_tests/security/startlinktoken-enduser-staff-escalation.test.ts +162 -0
  343. package/src/tests/api_tests/security/{F-0015-webhook-outbound-ssrf.test.ts → webhook-outbound-ssrf.test.ts} +13 -14
  344. package/src/tests/api_tests/security/{F-0155-webhook-timeout.test.ts → webhook-timeout.test.ts} +6 -7
  345. package/src/tests/api_tests/tickets_bulk_assign_from_queue.test.ts +279 -0
  346. package/src/tests/api_tests/webhook_error_handling.test.ts +1 -1
  347. package/src/tests/setup_isolated.ts +170 -0
  348. package/src/tests/tests.ts +60 -18
  349. package/src/tests/unit_tests/do_not_send_address.test.ts +60 -0
  350. package/src/tests/unit_tests/openapi_spec_freshness.test.ts +88 -0
  351. package/test_generated.pdf +0 -0
  352. package/src/tests/api_tests/security/readonly-enduser-preview.test.ts +0 -301
@@ -0,0 +1,237 @@
1
+ require('source-map-support').install();
2
+
3
+ import axios from "axios"
4
+
5
+ import { Session } from "../../../sdk"
6
+ import {
7
+ assert,
8
+ log_header,
9
+ } from "@tellescope/testing"
10
+ import { PROVIDER_PERMISSIONS, PLACEHOLDER_ID } from "@tellescope/constants"
11
+ import { setup_tests } from "../../setup"
12
+
13
+ const host = process.env.API_URL || 'http://localhost:8080' as const
14
+
15
+ const RAND = () => Math.random().toString(36).slice(2, 10)
16
+
17
+ const ALL = 'All' as const
18
+ const FULL_ACCESS = { create: ALL, read: ALL, update: ALL, delete: ALL }
19
+
20
+ const PROCESS_PATH = '/v1/automated-actions/process'
21
+
22
+ // A single collected check. Everything is gathered first and asserted at the end, because the
23
+ // testing harness exits the process on the first failed assert — collecting first means a RED run
24
+ // prints the full evidence table (and reaches `finally` cleanup) before the first assert exits.
25
+ type Check = {
26
+ label: string,
27
+ pass: boolean,
28
+ detail: string,
29
+ }
30
+
31
+ /**
32
+ * Regression test: `automated_actions.process` must be admin-gated, single-fire and audited.
33
+ *
34
+ * `automated_actions.process` (api.ts:3902) is the WORKER's action runner — its schema description
35
+ * reads "Used by worker for new action types". It was registered with only
36
+ * `{ requiresSubscription: true }`, so the framework gated it on the ordinary
37
+ * `automated_actions.update` verb, which the default provider role holds ('Assigned'). The handler
38
+ * then dispatched `automatedAction.action` through a 14-branch if-chain reaching real vendors:
39
+ * chargebeeChargeCardOnFile (card charge), belugaAutoRx / belugaTriggerRefill / mdiTriggerRefill
40
+ * (prescriptions and refills), createScriptSureDraft (medication draft), healthiePushForms /
41
+ * metriportPushFormResponse (PHI export). It checked neither the row's `status` nor whether it had
42
+ * already run, so any row was replayable: MDI advances to the next scheduled refill each call,
43
+ * Chargebee charges again. And there was no storeUserLog, so the action was attributed to the
44
+ * worker rather than the caller.
45
+ *
46
+ * The fix has three parts:
47
+ * 1. `adminOnly: true` on the api.ts registration (routing.ts:944 reads the flag from that
48
+ * options object — a schema-only flag is inert, which is a separate issue).
49
+ * 2. A single-fire claim for human callers: an atomic findOneAndUpdate with `status: 'active'` in
50
+ * the filter, stamping `finished` + `processedAt` before the vendor dispatch. The worker
51
+ * (`wasAutomated`) is exempt because its own claim in
52
+ * worker/modules/database.ts:114 is already atomic and it needs the row to stay 'active'
53
+ * across the call so a retryable failure (worker/modules/automations.ts:2053) can be
54
+ * re-claimed after the lock times out.
55
+ * 3. storeUserLog on the human path, recording the caller and the automatedActionId.
56
+ *
57
+ * The seeded row uses `setEnduserStatus`, which is NOT one of the API's if-chain branches, so it
58
+ * falls through to `{ success: false, unimplemented: true }` (api.ts) — no vendor call, no Bedrock,
59
+ * no PHI export. `processAfter` is set an hour out so the live worker cannot race the row (the same
60
+ * trick mdi_trigger_refill / beluga_trigger_refill use).
61
+ *
62
+ * RED (pre-fix): A1 fails (a non-admin holding only `automated_actions.update` reaches the handler)
63
+ * and C1 fails (the second call on the same row executes again instead of being rejected).
64
+ * B1 (admin positive control) passes pre-fix and must keep passing.
65
+ * GREEN (post-fix): all five checks pass.
66
+ */
67
+ export const automated_actions_process_gate_tests = async ({ sdk } : { sdk: Session, sdkNonAdmin: Session }) => {
68
+ log_header("automated_actions.process must be admin-only and single-fire")
69
+
70
+ const checks: Check[] = []
71
+ const record = (label: string, pass: boolean, detail: string) => checks.push({ label, pass, detail })
72
+
73
+ // Raw axios: the SDK's parseError discards the HTTP status, and the whole point here is WHICH
74
+ // status the gate returns.
75
+ const post = async (authToken: string, automatedActionId: string) => {
76
+ const r = await axios.request({
77
+ method: 'post',
78
+ url: `${host}${PROCESS_PATH}`,
79
+ data: { automatedActionId },
80
+ headers: { Authorization: `Bearer ${authToken}` },
81
+ validateStatus: () => true,
82
+ })
83
+ return { status: r.status, body: r.data }
84
+ }
85
+
86
+ let roleId = ''
87
+ let attackerId = ''
88
+ let enduserId = ''
89
+ const automatedActionIds: string[] = []
90
+
91
+ try {
92
+ /* ================= fixtures ================= */
93
+
94
+ // A non-admin role that explicitly HOLDS automated_actions update — the point of the finding is
95
+ // that holding the declared verb was enough to fire vendor actions.
96
+ const role = `f0192-automations-${RAND()}`
97
+ const rbap = await sdk.api.role_based_access_permissions.createOne({
98
+ role,
99
+ permissions: { ...PROVIDER_PERMISSIONS, automated_actions: FULL_ACCESS },
100
+ } as any)
101
+ roleId = rbap.id
102
+
103
+ // Throwaway staff user — never mutate an existing user's roles. verifiedEmail must be set at
104
+ // create time (it is rejected on update), otherwise calls are blocked on email verification
105
+ // rather than on the gate under test.
106
+ const attacker = await sdk.api.users.createOne({
107
+ email: `f0192-attacker-${RAND()}@tellescope.example`,
108
+ fname: 'F0192', lname: 'Staff',
109
+ verifiedEmail: true,
110
+ notificationEmailsDisabled: true,
111
+ roles: [role],
112
+ } as any)
113
+ attackerId = attacker.id
114
+
115
+ // Admin-minted token, not .authenticate — /login is IP rate-limited and the suite shares an IP.
116
+ const attackerToken = (await sdk.api.users.generate_auth_token({ id: attackerId })).authToken
117
+ const adminToken = (await sdk.api.users.generate_auth_token({ id: sdk.userInfo.id })).authToken
118
+
119
+ const attackerSDK = new Session({ host, authToken: attackerToken })
120
+ await attackerSDK.refresh_session()
121
+ record(
122
+ 'setup: attacker session is a non-admin holding automated_actions.update',
123
+ attackerSDK.userInfo.id === attackerId && !(attackerSDK.userInfo.roles ?? []).includes('Admin'),
124
+ `id=${attackerSDK.userInfo.id} roles=${JSON.stringify(attackerSDK.userInfo.roles)}`,
125
+ )
126
+
127
+ const enduser = await sdk.api.endusers.createOne({ fname: 'F0192', lname: 'Patient' })
128
+ enduserId = enduser.id
129
+
130
+ // `setEnduserStatus` is handled by the worker, NOT by the API's process if-chain, so it falls
131
+ // through to `unimplemented: true`. processAfter is an hour out so the live worker cannot claim
132
+ // the row before the explicit calls below.
133
+ const seedAction = async () => {
134
+ const a = await sdk.api.automated_actions.createOne({
135
+ enduserId,
136
+ journeyId: PLACEHOLDER_ID,
137
+ automationStepId: PLACEHOLDER_ID,
138
+ event: { type: 'onJourneyStart', info: {} },
139
+ action: { type: 'setEnduserStatus', info: { status: 'Working' } },
140
+ status: 'active',
141
+ processAfter: Date.now() + 60 * 60 * 1000,
142
+ } as any)
143
+ automatedActionIds.push(a.id)
144
+ return a
145
+ }
146
+
147
+ /* ===== A: the privilege gap — a non-admin with the declared verb must be rejected ===== */
148
+
149
+ const attackerRow = await seedAction()
150
+ const attackerAttempt = await post(attackerToken, attackerRow.id)
151
+
152
+ // Baseline on the same path with an admin token, so a blanket 401 (expired subscription,
153
+ // bad route, broken fixture) cannot masquerade as a working gate.
154
+ const baselineRow = await seedAction()
155
+ const baseline = await post(adminToken, baselineRow.id)
156
+ const badProbe = baseline.status === 401 || baseline.status === 403
157
+
158
+ record(
159
+ 'A1: a non-admin holding automated_actions.update cannot call process',
160
+ !badProbe && (attackerAttempt.status === 401 || attackerAttempt.status === 403),
161
+ `attacker=${attackerAttempt.status} ${JSON.stringify(attackerAttempt.body)} baseline(admin)=${baseline.status}`
162
+ + (badProbe ? ' BAD PROBE: admin baseline was also rejected' : ''),
163
+ )
164
+
165
+ // The attacker's row must be untouched — the gate has to fire before the claim and the dispatch.
166
+ const attackerRowAfter = await sdk.api.automated_actions.getOne(attackerRow.id)
167
+ record(
168
+ 'A2: a rejected non-admin call leaves the row unfired (status still active)',
169
+ attackerRowAfter.status === 'active',
170
+ `status=${attackerRowAfter.status} (expected active)`,
171
+ )
172
+
173
+ /* ===== B: positive control — an admin can still call it ===== */
174
+
175
+ record(
176
+ 'B1: an admin can still process a pending row',
177
+ baseline.status === 200 && baseline.body?.unimplemented === true,
178
+ `status=${baseline.status} body=${JSON.stringify(baseline.body)}`,
179
+ )
180
+
181
+ /* ===== C: the replay gate — the same row must not fire twice ===== */
182
+
183
+ const replay = await post(adminToken, baselineRow.id)
184
+ record(
185
+ 'C1: processing the same row twice is rejected (no replay)',
186
+ replay.status === 409,
187
+ `second call status=${replay.status} body=${JSON.stringify(replay.body)} (expected 409)`,
188
+ )
189
+
190
+ const baselineRowAfter = await sdk.api.automated_actions.getOne(baselineRow.id)
191
+ record(
192
+ 'C2: a processed row is marked finished so neither a replay nor the worker re-runs it',
193
+ baselineRowAfter.status === 'finished',
194
+ `status=${baselineRowAfter.status} (expected finished)`,
195
+ )
196
+
197
+ /* ===== report: print full table first, then assert (harness exits on first failure) ===== */
198
+ console.log('\n check summary:')
199
+ for (const c of checks) {
200
+ console.log(` ${c.pass ? 'PASS' : 'FAIL'} ${c.label} — ${c.detail}`)
201
+ }
202
+ console.log('')
203
+
204
+ for (const c of checks) {
205
+ assert(c.pass, `${c.label} — ${c.detail}`, c.label)
206
+ }
207
+ } finally {
208
+ // Reverse order. The automated_actions go first: they hang off enduserId with
209
+ // onDependencyDelete 'delete', so deleting the enduser would take them with it.
210
+ for (const id of automatedActionIds) { try { await sdk.api.automated_actions.deleteOne(id) } catch {} }
211
+ if (enduserId) { try { await sdk.api.endusers.deleteOne(enduserId) } catch {} }
212
+ if (attackerId) { try { await sdk.api.users.deleteOne(attackerId) } catch {} }
213
+ if (roleId) { try { await sdk.api.role_based_access_permissions.deleteOne(roleId) } catch {} }
214
+ }
215
+ }
216
+
217
+ // Allow running this test file independently
218
+ if (require.main === module) {
219
+ console.log(`🌐 Using API URL: ${host}`)
220
+ const sdk = new Session({ host })
221
+ const sdkNonAdmin = new Session({ host })
222
+
223
+ const runTests = async () => {
224
+ await setup_tests(sdk, sdkNonAdmin)
225
+ await automated_actions_process_gate_tests({ sdk, sdkNonAdmin })
226
+ }
227
+
228
+ runTests()
229
+ .then(() => {
230
+ console.log("✅ automated_actions.process gate test suite completed successfully")
231
+ process.exit(0)
232
+ })
233
+ .catch((error) => {
234
+ console.error("❌ automated_actions.process gate test suite failed:", error)
235
+ process.exit(1)
236
+ })
237
+ }
@@ -0,0 +1,224 @@
1
+ require('source-map-support').install();
2
+
3
+ import { Session, EnduserSession } from "../../../sdk"
4
+ import {
5
+ assert,
6
+ log_header,
7
+ } from "@tellescope/testing"
8
+ import { authenticate_enduser_via_token, setup_tests } from "../../setup"
9
+
10
+ const host = process.env.API_URL || 'http://localhost:8080' as const
11
+ const businessId = '60398b1131a295e64f084ff6'
12
+
13
+ const RAND = () => Math.random().toString(36).slice(2, 10)
14
+
15
+ const VICTIM_STATE = 'CA'
16
+ const ATTACKER_STATE_WRITE = 'NY'
17
+
18
+ type Check = {
19
+ label: string,
20
+ pass: boolean,
21
+ detail: string,
22
+ }
23
+
24
+ /**
25
+ * Regression test: `form_fields.booking_info` must not mint a booking token for another patient.
26
+ *
27
+ * `form_fields.booking_info` resolved its target patient as
28
+ * `DB.endusers.findById(enduserId || req.session.id)` with no ownership check for enduser
29
+ * sessions. The enduser DB branch builds the endusers collection with `enduserAccess: true`
30
+ * (database.ts), so a FOREIGN in-tenant id resolves — and the handler then writes
31
+ * `enduserFields.state` onto that record and calls `login_enduser` with
32
+ * `sessionScopes: ['appointment-booking']`, returning a live 24h token for the victim inside
33
+ * `bookingURL`. booking_info is enduser-callable AND granted to the 'public-form' scope, so a
34
+ * portal patient or an anonymous form-filler could mint a working session as any co-tenant patient.
35
+ *
36
+ * The fix mirrors `prepare_form_response` (api.ts): reject when an enduser session supplies an
37
+ * `enduserId` other than its own, before the booking-page lookup and before the `state` write.
38
+ *
39
+ * RED (pre-fix): A1 and A2 fail — the exploit returns 200 with a bookingURL whose token decodes to
40
+ * the VICTIM's id, and the victim's `state` is overwritten. B/C (positive controls) pass pre-fix.
41
+ * GREEN (post-fix): all six checks pass.
42
+ *
43
+ * Checks are collected and printed before any assert runs: the testing harness has
44
+ * EXIT_ON_FIRST_ERROR, so asserting inline would kill the process before the evidence table and
45
+ * before `finally` cleanup.
46
+ */
47
+ export const booking_info_idor_tests = async ({ sdk } : { sdk: Session, sdkNonAdmin: Session }) => {
48
+ log_header("booking_info must not mint an appointment-booking token for another enduser")
49
+
50
+ const checks: Check[] = []
51
+ const record = (label: string, pass: boolean, detail: string) => checks.push({ label, pass, detail })
52
+
53
+ // The minted JWT carries `id: enduser._id.toString()` (authentication.ts login_enduser). Decode
54
+ // it straight out of the returned URL — the whole point of the finding is WHOSE session it is.
55
+ const token_enduser_id = (bookingURL: string) => {
56
+ const token = /[?&]token=([^&]+)/.exec(bookingURL || '')?.[1]
57
+ const payload = token?.split('.')[1]
58
+ if (!payload) return ''
59
+ try {
60
+ return JSON.parse(Buffer.from(payload, 'base64').toString()).id as string
61
+ } catch {
62
+ return ''
63
+ }
64
+ }
65
+
66
+ let templateId = ''
67
+ let bookingPageId = ''
68
+ const enduserIds: string[] = []
69
+
70
+ try {
71
+ /* ================= fixtures ================= */
72
+
73
+ const template = await sdk.api.calendar_event_templates.createOne({
74
+ title: `F0179 template ${RAND()}`, durationInMinutes: 30,
75
+ } as any)
76
+ templateId = template.id
77
+
78
+ const bookingPage = await sdk.api.appointment_booking_pages.createOne({
79
+ title: `F0179 booking page ${RAND()}`,
80
+ calendarEventTemplateIds: [templateId],
81
+ locationIds: [],
82
+ } as any)
83
+ bookingPageId = bookingPage.id
84
+
85
+ const attacker = await sdk.api.endusers.createOne({
86
+ email: `f0179-attacker-${RAND()}@tellescope.example`, fname: 'F0179', lname: 'Attacker',
87
+ })
88
+ enduserIds.push(attacker.id)
89
+
90
+ const victim = await sdk.api.endusers.createOne({
91
+ email: `f0179-victim-${RAND()}@tellescope.example`, fname: 'F0179', lname: 'Victim',
92
+ state: VICTIM_STATE,
93
+ })
94
+ enduserIds.push(victim.id)
95
+
96
+ // Token mint, NOT enduserSDK.authenticate — /login-enduser is IP rate-limited and the suite
97
+ // shares one IP.
98
+ const attackerSDK = new EnduserSession({ host, businessId })
99
+ await authenticate_enduser_via_token(sdk, attackerSDK, { id: attacker.id })
100
+
101
+ /* ===== A: the exploit — enduser mints a booking session as another patient ===== */
102
+
103
+ let a1Detail = ''
104
+ let a1Pass = false
105
+ try {
106
+ const r = await attackerSDK.api.form_fields.booking_info({ bookingPageId, enduserId: victim.id })
107
+ const mintedFor = token_enduser_id(r.bookingURL)
108
+ a1Detail = (
109
+ `EXPLOITED: 200 with a bookingURL; token subject=${mintedFor} `
110
+ + `(victim=${victim.id}, attacker=${attacker.id})`
111
+ + (mintedFor === victim.id ? ' — a live appointment-booking session AS THE VICTIM' : '')
112
+ )
113
+ } catch (e: any) {
114
+ a1Pass = true
115
+ a1Detail = `rejected: ${e?.message || e?.toString()}`
116
+ }
117
+ record('A1: enduser cannot mint a booking token for another enduser', a1Pass, a1Detail)
118
+
119
+ // A2: the same call also writes enduserFields.state onto the target before minting.
120
+ let a2Attempt = ''
121
+ try {
122
+ await attackerSDK.api.form_fields.booking_info({
123
+ bookingPageId, enduserId: victim.id, enduserFields: { state: ATTACKER_STATE_WRITE },
124
+ })
125
+ a2Attempt = 'call returned 200'
126
+ } catch (e: any) {
127
+ a2Attempt = `call rejected: ${e?.message || e?.toString()}`
128
+ }
129
+ const victimAfter = await sdk.api.endusers.getOne(victim.id)
130
+ record(
131
+ 'A2: enduser cannot write `state` onto another enduser via enduserFields',
132
+ victimAfter.state === VICTIM_STATE,
133
+ `${a2Attempt}; victim.state=${victimAfter.state} (expected ${VICTIM_STATE})`,
134
+ )
135
+
136
+ /* ===== B: positive controls — the patient's own booking flow still works ===== */
137
+
138
+ let b1Detail = ''
139
+ let b1Pass = false
140
+ try {
141
+ const r = await attackerSDK.api.form_fields.booking_info({ bookingPageId })
142
+ const mintedFor = token_enduser_id(r.bookingURL)
143
+ b1Pass = mintedFor === attacker.id
144
+ b1Detail = `token subject=${mintedFor} (self=${attacker.id})`
145
+ } catch (e: any) {
146
+ b1Detail = `unexpectedly rejected: ${e?.message || e?.toString()}`
147
+ }
148
+ record('B1: enduser can still load booking info with no enduserId', b1Pass, b1Detail)
149
+
150
+ let b2Detail = ''
151
+ let b2Pass = false
152
+ try {
153
+ const r = await attackerSDK.api.form_fields.booking_info({ bookingPageId, enduserId: attacker.id })
154
+ const mintedFor = token_enduser_id(r.bookingURL)
155
+ b2Pass = mintedFor === attacker.id
156
+ b2Detail = `token subject=${mintedFor} (self=${attacker.id})`
157
+ } catch (e: any) {
158
+ b2Detail = `unexpectedly rejected: ${e?.message || e?.toString()}`
159
+ }
160
+ record('B2: enduser can still load booking info for its OWN enduserId', b2Pass, b2Detail)
161
+
162
+ /* ===== C: positive controls — the staff branches are untouched ===== */
163
+
164
+ let c1Detail = ''
165
+ let c1Pass = false
166
+ try {
167
+ const r = await sdk.api.form_fields.booking_info({ bookingPageId, enduserId: victim.id })
168
+ const mintedFor = token_enduser_id(r.bookingURL)
169
+ c1Pass = mintedFor === victim.id
170
+ c1Detail = `token subject=${mintedFor} (target=${victim.id})`
171
+ } catch (e: any) {
172
+ c1Detail = `unexpectedly rejected: ${e?.message || e?.toString()}`
173
+ }
174
+ record('C1: staff can still load booking info for a specific enduser', c1Pass, c1Detail)
175
+
176
+ let c2Detail = ''
177
+ let c2Pass = false
178
+ try {
179
+ const r = await sdk.api.form_fields.booking_info({ bookingPageId })
180
+ c2Pass = !!r.bookingURL && !!r.warningMessage
181
+ c2Detail = `warningMessage=${JSON.stringify(r.warningMessage)} bookingURL=${!!r.bookingURL}`
182
+ } catch (e: any) {
183
+ c2Detail = `unexpectedly rejected: ${e?.message || e?.toString()}`
184
+ }
185
+ record('C2: staff test-contact preview branch still works', c2Pass, c2Detail)
186
+
187
+ /* ===== report: print full table first, then assert (harness exits on first failure) ===== */
188
+ console.log('\n check summary:')
189
+ for (const c of checks) {
190
+ console.log(` ${c.pass ? 'PASS' : 'FAIL'} ${c.label} — ${c.detail}`)
191
+ }
192
+ console.log('')
193
+
194
+ for (const c of checks) {
195
+ assert(c.pass, `${c.label} — ${c.detail}`, c.label)
196
+ }
197
+ } finally {
198
+ for (const id of enduserIds) { try { await sdk.api.endusers.deleteOne(id) } catch {} }
199
+ if (bookingPageId) { try { await sdk.api.appointment_booking_pages.deleteOne(bookingPageId) } catch {} }
200
+ if (templateId) { try { await sdk.api.calendar_event_templates.deleteOne(templateId) } catch {} }
201
+ }
202
+ }
203
+
204
+ // Allow running this test file independently
205
+ if (require.main === module) {
206
+ console.log(`🌐 Using API URL: ${host}`)
207
+ const sdk = new Session({ host })
208
+ const sdkNonAdmin = new Session({ host })
209
+
210
+ const runTests = async () => {
211
+ await setup_tests(sdk, sdkNonAdmin)
212
+ await booking_info_idor_tests({ sdk, sdkNonAdmin })
213
+ }
214
+
215
+ runTests()
216
+ .then(() => {
217
+ console.log("✅ booking_info IDOR test suite completed successfully")
218
+ process.exit(0)
219
+ })
220
+ .catch((error) => {
221
+ console.error("❌ booking_info IDOR test suite failed:", error)
222
+ process.exit(1)
223
+ })
224
+ }
@@ -16,14 +16,14 @@ const host = process.env.API_URL || 'http://localhost:8080' as const
16
16
  const OTHER_TENANT_API_KEY = "ba745e25162bb95a795c5fa1af70df188d93c4d3aac9c48b34a5c8c9dd7b80f7"
17
17
 
18
18
  /**
19
- * Tenant-boundary guard for cascade_role_rename (relates to security-audit finding F-0053,
19
+ * Tenant-boundary guard for cascade_role_rename (relates to a prior internal review,
20
20
  * which was investigated and closed as a FALSE POSITIVE — see that file for the code trace).
21
21
  *
22
22
  * The `cascade_role_rename` side-effect handler
23
23
  * ([event_handlers_v2/role_based_access_permissions.ts](packages/private/api/api/v1/event_handlers_v2/role_based_access_permissions.ts))
24
24
  * runs when a `role_based_access_permissions` doc's `role` field changes. It finds every user
25
25
  * with the old role name (via `DBUnrestricted.users`) and rewrites their `roles` array, then
26
- * deauthenticates them. F-0053 hypothesized this query was globally cross-tenant. It is NOT:
26
+ * deauthenticates them. An earlier review hypothesized this query was globally cross-tenant. It is NOT:
27
27
  * `DBUnrestricted` bypasses per-user/per-role RBAC but is STILL scoped to the acting session's
28
28
  * `businessId` (see `modifyFilterForAccessConstraint` injecting `{ businessId }` at
29
29
  * database.ts:1761-1763, reached via the `unrestricted: true` branch at database.ts:2137-2144).
@@ -42,7 +42,7 @@ const OTHER_TENANT_API_KEY = "ba745e25162bb95a795c5fa1af70df188d93c4d3aac9c48b34
42
42
  * A collision-proof unique role name (timestamped) is used so the test never touches real roles.
43
43
  */
44
44
  export const cascade_role_rename_cross_tenant_tests = async ({ sdk } : { sdk: Session, sdkNonAdmin: Session }) => {
45
- log_header("F-0053: cascade role rename cross-tenant regression")
45
+ log_header("cascade role rename cross-tenant regression")
46
46
 
47
47
  const stamp = Date.now()
48
48
  const ROLE_OLD = `XTenantRename_${stamp}`
@@ -55,7 +55,7 @@ export const cascade_role_rename_cross_tenant_tests = async ({ sdk } : { sdk: Se
55
55
  let rbapId: string | undefined
56
56
  // We create dedicated throwaway users in BOTH tenants and delete them in `finally`. We never
57
57
  // mutate any pre-existing user (an earlier version of this test clobbered the apiKey's own
58
- // admin user via getSome+replaceObjectFields — see F-0053 finding notes). controlUser lives in
58
+ // admin user via getSome+replaceObjectFields). controlUser lives in
59
59
  // Tenant A and SHOULD be renamed by the same-tenant cascade; victimUser lives in Tenant B and
60
60
  // must be UNTOUCHED.
61
61
  let controlUserId: string | undefined
@@ -92,7 +92,7 @@ export const cascade_role_rename_cross_tenant_tests = async ({ sdk } : { sdk: Se
92
92
  assert(
93
93
  victimUser.businessId !== tenantABusinessId,
94
94
  `Victim user shares businessId with Tenant A (${victimUser.businessId}) — not a cross-tenant scenario`,
95
- 'F-0053 setup: tenants are distinct',
95
+ 'setup: tenants are distinct',
96
96
  )
97
97
  const victimBefore = await sdkOther.api.users.getOne(victimUserId)
98
98
  const controlBefore = await sdk.api.users.getOne(controlUserId)
@@ -100,7 +100,7 @@ export const cascade_role_rename_cross_tenant_tests = async ({ sdk } : { sdk: Se
100
100
  JSON.stringify(victimBefore.roles) === JSON.stringify([ROLE_OLD])
101
101
  && JSON.stringify(controlBefore.roles) === JSON.stringify([ROLE_OLD]),
102
102
  `Setup failed: expected both users to hold [${ROLE_OLD}] (victim=${JSON.stringify(victimBefore.roles)}, control=${JSON.stringify(controlBefore.roles)})`,
103
- 'F-0053 setup: both users hold ROLE_OLD',
103
+ 'setup: both users hold ROLE_OLD',
104
104
  )
105
105
 
106
106
  // 5. Tenant A renames the role. This triggers cascade_role_rename.
@@ -113,7 +113,7 @@ export const cascade_role_rename_cross_tenant_tests = async ({ sdk } : { sdk: Se
113
113
  JSON.stringify(victimAfter.roles) === JSON.stringify([ROLE_OLD]),
114
114
  `CROSS-TENANT LEAK: Tenant B victim roles changed to ${JSON.stringify(victimAfter.roles)} `
115
115
  + `after Tenant A renamed its role. Expected [${ROLE_OLD}].`,
116
- 'F-0053: Tenant B user roles unaffected by other-tenant role rename',
116
+ 'Tenant B user roles unaffected by other-tenant role rename',
117
117
  )
118
118
 
119
119
  // 7. POSITIVE CONTROL — the Tenant A control user SHOULD be renamed (same-tenant cascade intact).
@@ -122,7 +122,7 @@ export const cascade_role_rename_cross_tenant_tests = async ({ sdk } : { sdk: Se
122
122
  JSON.stringify(controlAfter.roles) === JSON.stringify([ROLE_NEW]),
123
123
  `Same-tenant cascade broken: Tenant A control roles are ${JSON.stringify(controlAfter.roles)}, `
124
124
  + `expected [${ROLE_NEW}].`,
125
- 'F-0053: Tenant A user roles renamed by same-tenant cascade',
125
+ 'Tenant A user roles renamed by same-tenant cascade',
126
126
  )
127
127
  } finally {
128
128
  // Cleanup: delete both throwaway users and the role doc. Never touches pre-existing users.
@@ -151,11 +151,11 @@ if (require.main === module) {
151
151
 
152
152
  runTests()
153
153
  .then(() => {
154
- console.log("✅ F-0053 cascade role rename cross-tenant test suite completed successfully")
154
+ console.log("✅ cascade role rename cross-tenant test suite completed successfully")
155
155
  process.exit(0)
156
156
  })
157
157
  .catch((error) => {
158
- console.error("❌ F-0053 cascade role rename cross-tenant test suite failed:", error)
158
+ console.error("❌ cascade role rename cross-tenant test suite failed:", error)
159
159
  process.exit(1)
160
160
  })
161
161
  }
@@ -41,7 +41,7 @@ const collectViolations = (results: { modelName: string, recordId: string, data:
41
41
  }
42
42
 
43
43
  /**
44
- * Regression test for F-0001 (security-audit/findings/F-0001-data-sync-bypasses-applyRedactions.md).
44
+ * Regression test: `/v1/data-sync` redaction.
45
45
  *
46
46
  * The /v1/data-sync handler must apply the central applyRedactions() pipeline to
47
47
  * every non-deleted record. The original bug: redactions were gated behind
@@ -63,7 +63,7 @@ const collectViolations = (results: { modelName: string, recordId: string, data:
63
63
  * Post-fix: assertion passes.
64
64
  */
65
65
  export const data_sync_redaction_bypass_tests = async ({ sdk, sdkNonAdmin } : { sdk: Session, sdkNonAdmin: Session }) => {
66
- log_header("F-0001: /v1/data-sync field-redaction bypass regression")
66
+ log_header("/v1/data-sync field-redaction bypass regression")
67
67
 
68
68
  const roleName = `f0001-data-sync-bypass-${Date.now()}`
69
69
  let testEnduserId: string | undefined
@@ -124,13 +124,13 @@ export const data_sync_redaction_bypass_tests = async ({ sdk, sdkNonAdmin } : {
124
124
  // would pass vacuously — guard against that.
125
125
  const userRecordsInStream = sync.results.filter(r => r.modelName === 'users').length
126
126
  await async_test(
127
- "F-0001 guard: /v1/data-sync sync stream contains at least one user record",
127
+ "guard: /v1/data-sync sync stream contains at least one user record",
128
128
  async () => ({ userRecords: userRecordsInStream, totalRecords: sync.results.length }),
129
129
  { onResult: r => r.userRecords >= 1 },
130
130
  )
131
131
 
132
132
  await async_test(
133
- "F-0001: /v1/data-sync must NOT return hashedPass / hashedPassword / hashedInviteCode (see security-audit/findings/F-0001)",
133
+ "/v1/data-sync must NOT return hashedPass / hashedPassword / hashedInviteCode",
134
134
  async () => ({
135
135
  violationCount: violations.length,
136
136
  violations: violations.slice(0, 10),
@@ -143,7 +143,7 @@ export const data_sync_redaction_bypass_tests = async ({ sdk, sdkNonAdmin } : {
143
143
  // ========================================================================
144
144
  // Additional coverage for applyRedactions code paths reachable via /v1/data-sync.
145
145
  // Each of these is a distinct branch in applyRedactions (routing.ts:1165-1238)
146
- // and could regress independently of the F-0001 fix.
146
+ // and could regress independently of this fix.
147
147
  // ========================================================================
148
148
 
149
149
  // Case A: schema-level `redactions: ['all']` must apply to ADMIN sessions too.
@@ -153,7 +153,7 @@ export const data_sync_redaction_bypass_tests = async ({ sdk, sdkNonAdmin } : {
153
153
  const adminSync = await sdk.sync({ from: new Date(0) })
154
154
  const adminViolations = collectViolations(adminSync.results)
155
155
  await async_test(
156
- "F-0001 coverage A: admin /v1/data-sync also strips redactions:['all'] fields (hashedPass etc.)",
156
+ "coverage A: admin /v1/data-sync also strips redactions:['all'] fields (hashedPass etc.)",
157
157
  async () => ({
158
158
  violationCount: adminViolations.length,
159
159
  violations: adminViolations.slice(0, 10),
@@ -180,7 +180,7 @@ export const data_sync_redaction_bypass_tests = async ({ sdk, sdkNonAdmin } : {
180
180
  } catch {}
181
181
  }
182
182
  await async_test(
183
- "F-0001 coverage B: /v1/data-sync strips linkedAccountAccess from other users' records",
183
+ "coverage B: /v1/data-sync strips linkedAccountAccess from other users' records",
184
184
  async () => ({
185
185
  otherUserRecords: otherUsersInStream.length,
186
186
  leakCount: linkedAccountLeaks.length,
@@ -226,11 +226,11 @@ if (require.main === module) {
226
226
 
227
227
  runTests()
228
228
  .then(() => {
229
- console.log("✅ F-0001 data-sync redaction-bypass test suite completed successfully")
229
+ console.log("✅ data-sync redaction-bypass test suite completed successfully")
230
230
  process.exit(0)
231
231
  })
232
232
  .catch((error) => {
233
- console.error("❌ F-0001 data-sync redaction-bypass test suite failed:", error)
233
+ console.error("❌ data-sync redaction-bypass test suite failed:", error)
234
234
  process.exit(1)
235
235
  })
236
236
  }