@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,277 @@
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 { assert_checks, setup_isolated_tenant, } from "../../setup_isolated";
39
+ var host = process.env.API_URL || 'http://localhost:8080';
40
+ var RAND = function () { return Math.random().toString(36).slice(2, 10); };
41
+ // far enough out that a locally running worker never claims the rows we inspect
42
+ var FUTURE_START = function () { return new Date(Date.now() + 60 * 60 * 1000); };
43
+ /**
44
+ * Regression test for F-0196
45
+ * (security-audit/findings/F-0196-add-to-journey-plants-foreign-enduserid.md).
46
+ *
47
+ * `endusers.add_to_journey` inserted an `automated_actions` row for EACH caller-supplied `enduserId`
48
+ * with only mongoId-shape validation and no tenant-membership check. The preceding
49
+ * `DB.endusers.updateMany` is scoped but its result was discarded, and
50
+ * `DB.automated_actions.insertMany` is a direct write that bypasses the `ensureValidDependentKeys`
51
+ * foreign-key check the generic CRUD route gets. The resulting row is stamped
52
+ * `businessId: <caller's tenant>` while carrying `enduserId: <a victim in another tenant>`.
53
+ *
54
+ * The worker then dereferences that enduser with the raw driver and no `businessId`
55
+ * (worker/modules/automations.ts, the F-0074/F-0111/F-0159 family), so the victim's record loads
56
+ * under the attacker's automation: `pushFormsToPortal` copies the victim's PHI into the attacker's
57
+ * `form_responses` (readable by their staff) and `iterableCustomEvent` writes an attacker-authored
58
+ * note into the victim's chart. F-0196 is the reachability that made those three exploitable.
59
+ *
60
+ * There are TWO independent plant sites and a fix to only one is a half fix:
61
+ * - inline branch, taken when `automationStepId` is supplied (api.ts add_to_journey handler)
62
+ * - module branch, taken when it is not: `handle_automation_event`'s
63
+ * `validEnduserIds = conditionsQuery ? <scoped find> : event.enduserIds` passes caller ids
64
+ * straight through for a step with NO conditions.
65
+ * Both are covered below (A1/A2).
66
+ *
67
+ * Design notes:
68
+ * - Two provisioned tenants, so the victim is a real foreign enduser and we can also assert their
69
+ * record was not mutated. Both must come from provisioning: `add_to_journey` is
70
+ * `requiresSubscription: true`, and a `create_and_join` org is born with an expired subscription.
71
+ * - The steps use `setEnduserStatus` — no vendor, no Bedrock — and `startAt` is an hour out so a
72
+ * running worker cannot process the rows before we read them.
73
+ * - `throttle` is deliberately omitted: it is the only way add_to_journey's per-pair rate limit can
74
+ * throw.
75
+ * - Everything is observable through `automated_actions` reads, so the worker is never needed.
76
+ *
77
+ * RED (pre-fix): A1, A2 and B2 fail — the victim's id appears in the attacker tenant's
78
+ * automated_actions. GREEN (post-fix): the foreign id is silently dropped and the caller's own
79
+ * endusers still enroll normally.
80
+ */
81
+ export var add_to_journey_foreign_enduserid_checks = function () { return __awaiter(void 0, void 0, void 0, function () {
82
+ var checks, record, attacker, victimTenant, victim, own, makeJourney, inline, modulePath, mixed, rowsFor_1, enduserIdsFor, inlineResult, inlineIds, moduleResult, moduleIds, victimAfter, victimJourneys, inlineOwn, moduleOwn, mixedResult, mixedIds, _i, _a, t, e_1;
83
+ var _b;
84
+ var _c;
85
+ return __generator(this, function (_d) {
86
+ switch (_d.label) {
87
+ case 0:
88
+ checks = [];
89
+ record = function (label, pass, detail) { return checks.push({ label: label, pass: pass, detail: detail }); };
90
+ _d.label = 1;
91
+ case 1:
92
+ _d.trys.push([1, , 19, 26]);
93
+ /* ================= fixtures: two isolated tenants ================= */
94
+ ;
95
+ return [4 /*yield*/, Promise.all([
96
+ setup_isolated_tenant({ name: "F0196 attacker ".concat(RAND()), users: [{ isAdmin: true }], apiKeys: 0 }),
97
+ setup_isolated_tenant({ name: "F0196 victim ".concat(RAND()), users: [{ isAdmin: true }], apiKeys: 0 }),
98
+ ])];
99
+ case 2:
100
+ _b = _d.sent(), attacker = _b[0], victimTenant = _b[1];
101
+ return [4 /*yield*/, victimTenant.sdk.api.endusers.createOne({ fname: 'F0196', lname: 'Victim' })];
102
+ case 3:
103
+ victim = _d.sent();
104
+ return [4 /*yield*/, attacker.sdk.api.endusers.createOne({ fname: 'F0196', lname: 'OwnPatient' })];
105
+ case 4:
106
+ own = _d.sent();
107
+ record('setup: two separate tenants, victim belongs to the other one', attacker.businessId !== victimTenant.businessId && !!victim.id && !!own.id, "attacker=".concat(attacker.businessId, " victim tenant=").concat(victimTenant.businessId, " victimEnduser=").concat(victim.id));
108
+ makeJourney = function (title) { return __awaiter(void 0, void 0, void 0, function () {
109
+ var journey, step;
110
+ return __generator(this, function (_a) {
111
+ switch (_a.label) {
112
+ case 0: return [4 /*yield*/, attacker.sdk.api.journeys.createOne({ title: "".concat(title, " ").concat(RAND()) })];
113
+ case 1:
114
+ journey = _a.sent();
115
+ return [4 /*yield*/, attacker.sdk.api.automation_steps.createOne({
116
+ journeyId: journey.id,
117
+ // benign and worker-local: no vendor, no Bedrock. NO `conditions`, which is precisely the
118
+ // vulnerable branch in handle_automation_event.
119
+ action: { type: 'setEnduserStatus', info: { status: 'F0196' } },
120
+ events: [{ type: 'onJourneyStart', info: {} }],
121
+ })];
122
+ case 2:
123
+ step = _a.sent();
124
+ return [2 /*return*/, { journey: journey, step: step }];
125
+ }
126
+ });
127
+ }); };
128
+ return [4 /*yield*/, makeJourney('F0196 inline')];
129
+ case 5:
130
+ inline = _d.sent();
131
+ return [4 /*yield*/, makeJourney('F0196 module')];
132
+ case 6:
133
+ modulePath = _d.sent();
134
+ return [4 /*yield*/, makeJourney('F0196 mixed')
135
+ /** every automated_actions row the attacker tenant can see for a journey */
136
+ ];
137
+ case 7:
138
+ mixed = _d.sent();
139
+ rowsFor_1 = function (journeyId) { return __awaiter(void 0, void 0, void 0, function () {
140
+ return __generator(this, function (_a) {
141
+ switch (_a.label) {
142
+ case 0: return [4 /*yield*/, attacker.sdk.api.automated_actions.getSome({ filter: { journeyId: journeyId } })];
143
+ case 1: return [2 /*return*/, (_a.sent())];
144
+ }
145
+ });
146
+ }); };
147
+ enduserIdsFor = function (journeyId) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
148
+ switch (_a.label) {
149
+ case 0: return [4 /*yield*/, rowsFor_1(journeyId)];
150
+ case 1: return [2 /*return*/, (_a.sent()).map(function (r) { return r.enduserId; })
151
+ /* ===== A: the foreign id must never be planted, on EITHER branch ===== */
152
+ // A1 — inline branch: automationStepId supplied
153
+ ];
154
+ }
155
+ }); }); };
156
+ return [4 /*yield*/, attacker.sdk.api.endusers.add_to_journey({
157
+ enduserIds: [victim.id],
158
+ journeyId: inline.journey.id,
159
+ automationStepId: inline.step.id,
160
+ startAt: FUTURE_START(),
161
+ }).then(function () { return 'resolved'; }).catch(function (e) { return "rejected: ".concat((e === null || e === void 0 ? void 0 : e.message) || e); })];
162
+ case 8:
163
+ inlineResult = _d.sent();
164
+ return [4 /*yield*/, enduserIdsFor(inline.journey.id)];
165
+ case 9:
166
+ inlineIds = _d.sent();
167
+ record('A1: inline branch (automationStepId) does not plant a foreign enduserId', !inlineIds.includes(victim.id), "call=".concat(inlineResult, "; automated_actions enduserIds=").concat(JSON.stringify(inlineIds), " (must not contain ").concat(victim.id, ")"));
168
+ return [4 /*yield*/, attacker.sdk.api.endusers.add_to_journey({
169
+ enduserIds: [victim.id],
170
+ journeyId: modulePath.journey.id,
171
+ startAt: FUTURE_START(),
172
+ }).then(function () { return 'resolved'; }).catch(function (e) { return "rejected: ".concat((e === null || e === void 0 ? void 0 : e.message) || e); })];
173
+ case 10:
174
+ moduleResult = _d.sent();
175
+ return [4 /*yield*/, enduserIdsFor(modulePath.journey.id)];
176
+ case 11:
177
+ moduleIds = _d.sent();
178
+ record('A2: module branch (no automationStepId) does not plant a foreign enduserId', !moduleIds.includes(victim.id), "call=".concat(moduleResult, "; automated_actions enduserIds=").concat(JSON.stringify(moduleIds), " (must not contain ").concat(victim.id, ")"));
179
+ return [4 /*yield*/, victimTenant.sdk.api.endusers.getOne(victim.id)];
180
+ case 12:
181
+ victimAfter = _d.sent();
182
+ victimJourneys = (_c = victimAfter === null || victimAfter === void 0 ? void 0 : victimAfter.journeys) !== null && _c !== void 0 ? _c : {};
183
+ record('A3: the victim enduser was not enrolled in the attacker\'s journey', !victimJourneys[inline.journey.id] && !victimJourneys[modulePath.journey.id], "victim journeys=".concat(JSON.stringify(victimJourneys)));
184
+ /* ===== B: legitimate use must keep working ===== */
185
+ // B1 — positive control on both branches, so a blanket failure can't masquerade as a fix
186
+ return [4 /*yield*/, attacker.sdk.api.endusers.add_to_journey({
187
+ enduserIds: [own.id],
188
+ journeyId: inline.journey.id,
189
+ automationStepId: inline.step.id,
190
+ startAt: FUTURE_START(),
191
+ })];
192
+ case 13:
193
+ /* ===== B: legitimate use must keep working ===== */
194
+ // B1 — positive control on both branches, so a blanket failure can't masquerade as a fix
195
+ _d.sent();
196
+ return [4 /*yield*/, attacker.sdk.api.endusers.add_to_journey({
197
+ enduserIds: [own.id],
198
+ journeyId: modulePath.journey.id,
199
+ startAt: FUTURE_START(),
200
+ })];
201
+ case 14:
202
+ _d.sent();
203
+ return [4 /*yield*/, enduserIdsFor(inline.journey.id)];
204
+ case 15:
205
+ inlineOwn = _d.sent();
206
+ return [4 /*yield*/, enduserIdsFor(modulePath.journey.id)];
207
+ case 16:
208
+ moduleOwn = _d.sent();
209
+ record('B1: the caller\'s OWN enduser still enrolls on both branches', inlineOwn.includes(own.id) && moduleOwn.includes(own.id), "inline=".concat(JSON.stringify(inlineOwn), " module=").concat(JSON.stringify(moduleOwn), " (both must contain ").concat(own.id, ")"));
210
+ return [4 /*yield*/, attacker.sdk.api.endusers.add_to_journey({
211
+ enduserIds: [own.id, victim.id],
212
+ journeyId: mixed.journey.id,
213
+ automationStepId: mixed.step.id,
214
+ startAt: FUTURE_START(),
215
+ }).then(function () { return 'resolved'; }).catch(function (e) { return "rejected: ".concat((e === null || e === void 0 ? void 0 : e.message) || e); })];
216
+ case 17:
217
+ mixedResult = _d.sent();
218
+ return [4 /*yield*/, enduserIdsFor(mixed.journey.id)];
219
+ case 18:
220
+ mixedIds = _d.sent();
221
+ record('B2: a mixed list enrolls the visible enduser and drops only the foreign one', mixedIds.includes(own.id) && !mixedIds.includes(victim.id), "call=".concat(mixedResult, "; enduserIds=").concat(JSON.stringify(mixedIds), " (want ").concat(own.id, ", not ").concat(victim.id, ")"));
222
+ return [3 /*break*/, 26];
223
+ case 19:
224
+ _i = 0, _a = [attacker, victimTenant];
225
+ _d.label = 20;
226
+ case 20:
227
+ if (!(_i < _a.length)) return [3 /*break*/, 25];
228
+ t = _a[_i];
229
+ if (!t) return [3 /*break*/, 24];
230
+ _d.label = 21;
231
+ case 21:
232
+ _d.trys.push([21, 23, , 24]);
233
+ return [4 /*yield*/, t.teardown()];
234
+ case 22:
235
+ _d.sent();
236
+ return [3 /*break*/, 24];
237
+ case 23:
238
+ e_1 = _d.sent();
239
+ console.error('teardown failed', e_1);
240
+ return [3 /*break*/, 24];
241
+ case 24:
242
+ _i++;
243
+ return [3 /*break*/, 20];
244
+ case 25: return [7 /*endfinally*/];
245
+ case 26: return [2 /*return*/, checks];
246
+ }
247
+ });
248
+ }); };
249
+ /** Asserting wrapper, for standalone runs and for sequential use in the suite. */
250
+ export var add_to_journey_foreign_enduserid_tests = function () { return __awaiter(void 0, void 0, void 0, function () {
251
+ var _a, _b;
252
+ return __generator(this, function (_c) {
253
+ switch (_c.label) {
254
+ case 0:
255
+ _a = assert_checks;
256
+ _b = ['F-0196 add_to_journey foreign enduserId'];
257
+ return [4 /*yield*/, add_to_journey_foreign_enduserid_checks()];
258
+ case 1:
259
+ _a.apply(void 0, _b.concat([_c.sent()]));
260
+ return [2 /*return*/];
261
+ }
262
+ });
263
+ }); };
264
+ // Allow running this test file independently
265
+ if (require.main === module) {
266
+ console.log("\uD83C\uDF10 Using API URL: ".concat(host));
267
+ add_to_journey_foreign_enduserid_tests()
268
+ .then(function () {
269
+ console.log("✅ F-0196 add_to_journey foreign enduserId test suite completed successfully");
270
+ process.exit(0);
271
+ })
272
+ .catch(function (error) {
273
+ console.error("❌ F-0196 add_to_journey foreign enduserId test suite failed:", error);
274
+ process.exit(1);
275
+ });
276
+ }
277
+ //# sourceMappingURL=F-0196-add-to-journey-foreign-enduserid.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"F-0196-add-to-journey-foreign-enduserid.test.js","sourceRoot":"","sources":["../../../../../src/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import { Check } from "../../setup_isolated";
2
+ /**
3
+ * Regression test for F-0200
4
+ * (security-audit/findings/F-0200-form-logic-js-dynamic-code-execution.md).
5
+ *
6
+ * `responses_satisfy_conditions` (utilities/src/utils.ts) compiled a stored condition value of the
7
+ * form `$JS(...)` with `new Function()` and executed it — in every form-filler's browser, including
8
+ * anonymous visitors on public intake pages, AND inside the API process (automation_triggers.ts
9
+ * trigger matching on every form submission, ai_agent/form_filling.ts voice-agent navigation). The
10
+ * API process holds every tenant's database handle, so a condition authored by staff in one tenant
11
+ * reached all of them. Any staff user or API key with form_fields write access could plant one by
12
+ * typing into the free-text $gt/$lt box — no API call required.
13
+ *
14
+ * The sink is removed. This asserts the second half of the fix: `compoundFilterValidator` rejects the
15
+ * syntax at write time on every field that feeds that evaluator, so it cannot be reintroduced through
16
+ * stored configuration. The runtime half — that a `$JS` value is never evaluated — is covered by the
17
+ * pure-function cases in `form_conditional_logic_tests` in tests.ts.
18
+ *
19
+ * The five condition locations were established by enumerating every `responses_satisfy_conditions`
20
+ * call site, and all five are covered here. That completeness matters: the first run of this test
21
+ * caught a half fix. `event.conditions` is wrapped in an `orValidator`, which returns the first arm
22
+ * that does not throw, and its permissive `optional: optionalAnyObjectValidator` arm was declared
23
+ * before the guarded one — so the payload was accepted and stored even though the validator had been
24
+ * swapped. Pointing a field at the guard is not the same as the guard being reachable, which is why
25
+ * every location gets an assertion rather than an argument.
26
+ *
27
+ * Each rejection is paired with a positive control using the same shape without `$JS(`, so a passing
28
+ * test cannot be explained by the write failing for an unrelated reason.
29
+ */
30
+ export declare const form_logic_js_execution_checks: () => Promise<Check[]>;
31
+ /** Asserting wrapper, for standalone runs and for sequential use in the suite. */
32
+ export declare const form_logic_js_execution_tests: () => Promise<void>;
33
+ //# sourceMappingURL=F-0200-form-logic-js-execution.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"F-0200-form-logic-js-execution.test.d.ts","sourceRoot":"","sources":["../../../../../src/tests/api_tests/security/F-0200-form-logic-js-execution.test.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,EAEN,MAAM,sBAAsB,CAAA;AAM7B;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,8BAA8B,QAAa,QAAQ,KAAK,EAAE,CA4HtE,CAAA;AAED,kFAAkF;AAClF,eAAO,MAAM,6BAA6B,qBAEzC,CAAA"}
@@ -0,0 +1,244 @@
1
+ var __assign = (this && this.__assign) || function () {
2
+ __assign = Object.assign || function(t) {
3
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
4
+ s = arguments[i];
5
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
+ t[p] = s[p];
7
+ }
8
+ return t;
9
+ };
10
+ return __assign.apply(this, arguments);
11
+ };
12
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
13
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
14
+ return new (P || (P = Promise))(function (resolve, reject) {
15
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
16
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
17
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
18
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
19
+ });
20
+ };
21
+ var __generator = (this && this.__generator) || function (thisArg, body) {
22
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
23
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
24
+ function verb(n) { return function (v) { return step([n, v]); }; }
25
+ function step(op) {
26
+ if (f) throw new TypeError("Generator is already executing.");
27
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
28
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
29
+ if (y = 0, t) op = [op[0] & 2, t.value];
30
+ switch (op[0]) {
31
+ case 0: case 1: t = op; break;
32
+ case 4: _.label++; return { value: op[1], done: false };
33
+ case 5: _.label++; y = op[1]; op = [0]; continue;
34
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
35
+ default:
36
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
37
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
38
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
39
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
40
+ if (t[2]) _.ops.pop();
41
+ _.trys.pop(); continue;
42
+ }
43
+ op = body.call(thisArg, _);
44
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
45
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
46
+ }
47
+ };
48
+ require('source-map-support').install();
49
+ import { assert_checks, setup_isolated_tenant, } from "../../setup_isolated";
50
+ var host = process.env.API_URL || 'http://localhost:8080';
51
+ var RAND = function () { return Math.random().toString(36).slice(2, 10); };
52
+ /**
53
+ * Regression test for F-0200
54
+ * (security-audit/findings/F-0200-form-logic-js-dynamic-code-execution.md).
55
+ *
56
+ * `responses_satisfy_conditions` (utilities/src/utils.ts) compiled a stored condition value of the
57
+ * form `$JS(...)` with `new Function()` and executed it — in every form-filler's browser, including
58
+ * anonymous visitors on public intake pages, AND inside the API process (automation_triggers.ts
59
+ * trigger matching on every form submission, ai_agent/form_filling.ts voice-agent navigation). The
60
+ * API process holds every tenant's database handle, so a condition authored by staff in one tenant
61
+ * reached all of them. Any staff user or API key with form_fields write access could plant one by
62
+ * typing into the free-text $gt/$lt box — no API call required.
63
+ *
64
+ * The sink is removed. This asserts the second half of the fix: `compoundFilterValidator` rejects the
65
+ * syntax at write time on every field that feeds that evaluator, so it cannot be reintroduced through
66
+ * stored configuration. The runtime half — that a `$JS` value is never evaluated — is covered by the
67
+ * pure-function cases in `form_conditional_logic_tests` in tests.ts.
68
+ *
69
+ * The five condition locations were established by enumerating every `responses_satisfy_conditions`
70
+ * call site, and all five are covered here. That completeness matters: the first run of this test
71
+ * caught a half fix. `event.conditions` is wrapped in an `orValidator`, which returns the first arm
72
+ * that does not throw, and its permissive `optional: optionalAnyObjectValidator` arm was declared
73
+ * before the guarded one — so the payload was accepted and stored even though the validator had been
74
+ * swapped. Pointing a field at the guard is not the same as the guard being reachable, which is why
75
+ * every location gets an assertion rather than an argument.
76
+ *
77
+ * Each rejection is paired with a positive control using the same shape without `$JS(`, so a passing
78
+ * test cannot be explained by the write failing for an unrelated reason.
79
+ */
80
+ export var form_logic_js_execution_checks = function () { return __awaiter(void 0, void 0, void 0, function () {
81
+ var checks, record, JS_PAYLOAD, SAFE_VALUE, condition, previousFields, attempt, tenant, sdk_1, form_1, field_1, controlCreate, payloadCreate, payloadUpdate, groupControl, groupPayload, optionControl, optionPayload, productControl, productPayload, trigger_1, triggerControl, triggerPayload, byFormIdPayload, e_1;
82
+ return __generator(this, function (_a) {
83
+ switch (_a.label) {
84
+ case 0:
85
+ checks = [];
86
+ record = function (label, pass, detail) { return checks.push({ label: label, pass: pass, detail: detail }); };
87
+ JS_PAYLOAD = '$JS(return 0)';
88
+ SAFE_VALUE = '18';
89
+ condition = function (value) { return ({ $and: [{ condition: { 'someFieldId': { $gt: value } } }] }); };
90
+ previousFields = function (value) { return ([{
91
+ type: 'compoundLogic',
92
+ info: { fieldId: '000000000000000000000000', priority: 5, label: '', condition: condition(value) },
93
+ }]); };
94
+ attempt = function (fn) { return __awaiter(void 0, void 0, void 0, function () {
95
+ return __generator(this, function (_a) {
96
+ return [2 /*return*/, (fn().then(function () { return ({ accepted: true, detail: 'accepted' }); })
97
+ .catch(function (e) { return ({ accepted: false, detail: "rejected: ".concat((e === null || e === void 0 ? void 0 : e.message) || e) }); }))];
98
+ });
99
+ }); };
100
+ _a.label = 1;
101
+ case 1:
102
+ _a.trys.push([1, , 17, 22]);
103
+ return [4 /*yield*/, setup_isolated_tenant({ name: "F0200 ".concat(RAND()), users: [{ isAdmin: true }], apiKeys: 0 })];
104
+ case 2:
105
+ tenant = _a.sent();
106
+ sdk_1 = tenant.sdk;
107
+ return [4 /*yield*/, sdk_1.api.forms.createOne({ title: "F-0200 ".concat(RAND()) })];
108
+ case 3:
109
+ form_1 = _a.sent();
110
+ return [4 /*yield*/, sdk_1.api.form_fields.createOne({
111
+ formId: form_1.id, title: 'F-0200 anchor', type: 'string', previousFields: [{ type: 'root', info: {} }],
112
+ })
113
+ /* ===== 1. previousFields[].info.condition — the field the finding was reported against ===== */
114
+ ];
115
+ case 4:
116
+ field_1 = _a.sent();
117
+ return [4 /*yield*/, attempt(function () { return sdk_1.api.form_fields.createOne({
118
+ formId: form_1.id, title: 'F-0200 control', type: 'string',
119
+ previousFields: previousFields(SAFE_VALUE),
120
+ }); })];
121
+ case 5:
122
+ controlCreate = _a.sent();
123
+ record('control: form_fields.createOne accepts a normal $gt condition', controlCreate.accepted, controlCreate.detail);
124
+ return [4 /*yield*/, attempt(function () { return sdk_1.api.form_fields.createOne({
125
+ formId: form_1.id, title: 'F-0200 payload', type: 'string',
126
+ previousFields: previousFields(JS_PAYLOAD),
127
+ }); })];
128
+ case 6:
129
+ payloadCreate = _a.sent();
130
+ record('1a: form_fields.createOne rejects $JS( in previousFields condition', !payloadCreate.accepted, payloadCreate.detail);
131
+ return [4 /*yield*/, attempt(function () { return sdk_1.api.form_fields.updateOne(field_1.id, {
132
+ previousFields: previousFields(JS_PAYLOAD),
133
+ }); })];
134
+ case 7:
135
+ payloadUpdate = _a.sent();
136
+ record('1b: form_fields.updateOne rejects $JS( in previousFields condition', !payloadUpdate.accepted, payloadUpdate.detail);
137
+ return [4 /*yield*/, attempt(function () { return sdk_1.api.form_fields.updateOne(field_1.id, {
138
+ groupShowCondition: condition(SAFE_VALUE),
139
+ }); })];
140
+ case 8:
141
+ groupControl = _a.sent();
142
+ record('control: groupShowCondition accepts a normal condition', groupControl.accepted, groupControl.detail);
143
+ return [4 /*yield*/, attempt(function () { return sdk_1.api.form_fields.updateOne(field_1.id, {
144
+ groupShowCondition: condition(JS_PAYLOAD),
145
+ }); })];
146
+ case 9:
147
+ groupPayload = _a.sent();
148
+ record('2: form_fields.updateOne rejects $JS( in groupShowCondition', !groupPayload.accepted, groupPayload.detail);
149
+ return [4 /*yield*/, attempt(function () { return sdk_1.api.form_fields.updateOne(field_1.id, {
150
+ options: { optionDetails: [{ option: 'A', showCondition: condition(SAFE_VALUE) }] },
151
+ }); })];
152
+ case 10:
153
+ optionControl = _a.sent();
154
+ record('control: optionDetails showCondition accepts a normal condition', optionControl.accepted, optionControl.detail);
155
+ return [4 /*yield*/, attempt(function () { return sdk_1.api.form_fields.updateOne(field_1.id, {
156
+ options: { optionDetails: [{ option: 'A', showCondition: condition(JS_PAYLOAD) }] },
157
+ }); })];
158
+ case 11:
159
+ optionPayload = _a.sent();
160
+ record('3: form_fields.updateOne rejects $JS( in optionDetails showCondition', !optionPayload.accepted, optionPayload.detail);
161
+ return [4 /*yield*/, attempt(function () { return sdk_1.api.form_fields.updateOne(field_1.id, {
162
+ options: { productConditions: [{ productId: '000000000000000000000000', showCondition: condition(SAFE_VALUE) }] },
163
+ }); })];
164
+ case 12:
165
+ productControl = _a.sent();
166
+ record('control: productConditions showCondition accepts a normal condition', productControl.accepted, productControl.detail);
167
+ return [4 /*yield*/, attempt(function () { return sdk_1.api.form_fields.updateOne(field_1.id, {
168
+ options: { productConditions: [{ productId: '000000000000000000000000', showCondition: condition(JS_PAYLOAD) }] },
169
+ }); })];
170
+ case 13:
171
+ productPayload = _a.sent();
172
+ record('4: form_fields.updateOne rejects $JS( in productConditions showCondition', !productPayload.accepted, productPayload.detail);
173
+ trigger_1 = function (conditions, extraInfo) {
174
+ if (extraInfo === void 0) { extraInfo = {}; }
175
+ return ({
176
+ title: "F-0200 ".concat(RAND()),
177
+ status: 'Inactive',
178
+ event: __assign({ type: 'Form Submitted', info: __assign({ formId: form_1.id }, extraInfo) }, conditions),
179
+ action: { type: 'Add Tags', info: { tags: ['F-0200'] } },
180
+ });
181
+ };
182
+ return [4 /*yield*/, attempt(function () { return sdk_1.api.automation_triggers.createOne(trigger_1({ conditions: condition(SAFE_VALUE) })); })];
183
+ case 14:
184
+ triggerControl = _a.sent();
185
+ record('control: automation_triggers accepts normal event conditions', triggerControl.accepted, triggerControl.detail);
186
+ return [4 /*yield*/, attempt(function () { return sdk_1.api.automation_triggers.createOne(trigger_1({ conditions: condition(JS_PAYLOAD) })); })];
187
+ case 15:
188
+ triggerPayload = _a.sent();
189
+ record('5a: automation_triggers rejects $JS( in event.conditions', !triggerPayload.accepted, triggerPayload.detail);
190
+ return [4 /*yield*/, attempt(function () {
191
+ var _a;
192
+ return sdk_1.api.automation_triggers.createOne(trigger_1({}, { conditionsByFormId: (_a = {}, _a[form_1.id] = condition(JS_PAYLOAD), _a) }));
193
+ })];
194
+ case 16:
195
+ byFormIdPayload = _a.sent();
196
+ record('5b: automation_triggers rejects $JS( in event.info.conditionsByFormId', !byFormIdPayload.accepted, byFormIdPayload.detail);
197
+ return [3 /*break*/, 22];
198
+ case 17:
199
+ if (!tenant) return [3 /*break*/, 21];
200
+ _a.label = 18;
201
+ case 18:
202
+ _a.trys.push([18, 20, , 21]);
203
+ return [4 /*yield*/, tenant.teardown()];
204
+ case 19:
205
+ _a.sent();
206
+ return [3 /*break*/, 21];
207
+ case 20:
208
+ e_1 = _a.sent();
209
+ console.error('teardown failed', e_1);
210
+ return [3 /*break*/, 21];
211
+ case 21: return [7 /*endfinally*/];
212
+ case 22: return [2 /*return*/, checks];
213
+ }
214
+ });
215
+ }); };
216
+ /** Asserting wrapper, for standalone runs and for sequential use in the suite. */
217
+ export var form_logic_js_execution_tests = function () { return __awaiter(void 0, void 0, void 0, function () {
218
+ var _a, _b;
219
+ return __generator(this, function (_c) {
220
+ switch (_c.label) {
221
+ case 0:
222
+ _a = assert_checks;
223
+ _b = ['F-0200 $JS in stored conditional logic'];
224
+ return [4 /*yield*/, form_logic_js_execution_checks()];
225
+ case 1:
226
+ _a.apply(void 0, _b.concat([_c.sent()]));
227
+ return [2 /*return*/];
228
+ }
229
+ });
230
+ }); };
231
+ // Allow running this test file independently
232
+ if (require.main === module) {
233
+ console.log("\uD83C\uDF10 Using API URL: ".concat(host));
234
+ form_logic_js_execution_tests()
235
+ .then(function () {
236
+ console.log("✅ F-0200 form logic JS execution test suite completed successfully");
237
+ process.exit(0);
238
+ })
239
+ .catch(function (error) {
240
+ console.error("❌ F-0200 form logic JS execution test suite failed:", error);
241
+ process.exit(1);
242
+ });
243
+ }
244
+ //# sourceMappingURL=F-0200-form-logic-js-execution.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"F-0200-form-logic-js-execution.test.js","sourceRoot":"","sources":["../../../../../src/tests/api_tests/security/F-0200-form-logic-js-execution.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import { Check } from "../../setup_isolated";
2
+ /**
3
+ * Regression test: `endusers.add_to_journey` must not enroll another tenant's enduser.
4
+ *
5
+ * `endusers.add_to_journey` inserted an `automated_actions` row for EACH caller-supplied `enduserId`
6
+ * with only mongoId-shape validation and no tenant-membership check. The preceding
7
+ * `DB.endusers.updateMany` is scoped but its result was discarded, and
8
+ * `DB.automated_actions.insertMany` is a direct write that bypasses the `ensureValidDependentKeys`
9
+ * foreign-key check the generic CRUD route gets. The resulting row is stamped
10
+ * `businessId: <caller's tenant>` while carrying `enduserId: <a victim in another tenant>`.
11
+ *
12
+ * The worker then dereferences that enduser with the raw driver and no `businessId`
13
+ * (worker/modules/automations.ts, the worker's unscoped raw-driver lookup family), so the victim's record loads
14
+ * under the attacker's automation: `pushFormsToPortal` copies the victim's PHI into the attacker's
15
+ * `form_responses` (readable by their staff) and `iterableCustomEvent` writes an attacker-authored
16
+ * note into the victim's chart. This plant is the reachability that made those three exploitable.
17
+ *
18
+ * There are TWO independent plant sites and a fix to only one is a half fix:
19
+ * - inline branch, taken when `automationStepId` is supplied (api.ts add_to_journey handler)
20
+ * - module branch, taken when it is not: `handle_automation_event`'s
21
+ * `validEnduserIds = conditionsQuery ? <scoped find> : event.enduserIds` passes caller ids
22
+ * straight through for a step with NO conditions.
23
+ * Both are covered below (A1/A2).
24
+ *
25
+ * Design notes:
26
+ * - Two provisioned tenants, so the victim is a real foreign enduser and we can also assert their
27
+ * record was not mutated. Both must come from provisioning: `add_to_journey` is
28
+ * `requiresSubscription: true`, and a `create_and_join` org is born with an expired subscription.
29
+ * - The steps use `setEnduserStatus` — no vendor, no Bedrock — and `startAt` is an hour out so a
30
+ * running worker cannot process the rows before we read them.
31
+ * - `throttle` is deliberately omitted: it is the only way add_to_journey's per-pair rate limit can
32
+ * throw.
33
+ * - Everything is observable through `automated_actions` reads, so the worker is never needed.
34
+ *
35
+ * RED (pre-fix): A1, A2 and B2 fail — the victim's id appears in the attacker tenant's
36
+ * automated_actions. GREEN (post-fix): the foreign id is silently dropped and the caller's own
37
+ * endusers still enroll normally.
38
+ */
39
+ export declare const add_to_journey_foreign_enduserid_checks: () => Promise<Check[]>;
40
+ /** Asserting wrapper, for standalone runs and for sequential use in the suite. */
41
+ export declare const add_to_journey_foreign_enduserid_tests: () => Promise<void>;
42
+ //# sourceMappingURL=add-to-journey-foreign-enduserid.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add-to-journey-foreign-enduserid.test.d.ts","sourceRoot":"","sources":["../../../../../src/tests/api_tests/security/add-to-journey-foreign-enduserid.test.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,EAEN,MAAM,sBAAsB,CAAA;AAS7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,eAAO,MAAM,uCAAuC,QAAa,QAAQ,KAAK,EAAE,CAuI/E,CAAA;AAED,kFAAkF;AAClF,eAAO,MAAM,sCAAsC,qBAElD,CAAA"}