palaryn 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (607) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +716 -0
  3. package/dist/sdk/typescript/src/client.d.ts +71 -0
  4. package/dist/sdk/typescript/src/client.d.ts.map +1 -0
  5. package/dist/sdk/typescript/src/client.js +176 -0
  6. package/dist/sdk/typescript/src/client.js.map +1 -0
  7. package/dist/sdk/typescript/src/errors.d.ts +50 -0
  8. package/dist/sdk/typescript/src/errors.d.ts.map +1 -0
  9. package/dist/sdk/typescript/src/errors.js +103 -0
  10. package/dist/sdk/typescript/src/errors.js.map +1 -0
  11. package/dist/sdk/typescript/src/index.d.ts +4 -0
  12. package/dist/sdk/typescript/src/index.d.ts.map +1 -0
  13. package/dist/sdk/typescript/src/index.js +15 -0
  14. package/dist/sdk/typescript/src/index.js.map +1 -0
  15. package/dist/sdk/typescript/src/types.d.ts +101 -0
  16. package/dist/sdk/typescript/src/types.d.ts.map +1 -0
  17. package/dist/sdk/typescript/src/types.js +6 -0
  18. package/dist/sdk/typescript/src/types.js.map +1 -0
  19. package/dist/src/admin/index.d.ts +2 -0
  20. package/dist/src/admin/index.d.ts.map +1 -0
  21. package/dist/src/admin/index.js +6 -0
  22. package/dist/src/admin/index.js.map +1 -0
  23. package/dist/src/admin/routes.d.ts +5 -0
  24. package/dist/src/admin/routes.d.ts.map +1 -0
  25. package/dist/src/admin/routes.js +471 -0
  26. package/dist/src/admin/routes.js.map +1 -0
  27. package/dist/src/admin/templates.d.ts +51 -0
  28. package/dist/src/admin/templates.d.ts.map +1 -0
  29. package/dist/src/admin/templates.js +500 -0
  30. package/dist/src/admin/templates.js.map +1 -0
  31. package/dist/src/anomaly/detector.d.ts +141 -0
  32. package/dist/src/anomaly/detector.d.ts.map +1 -0
  33. package/dist/src/anomaly/detector.js +554 -0
  34. package/dist/src/anomaly/detector.js.map +1 -0
  35. package/dist/src/anomaly/index.d.ts +2 -0
  36. package/dist/src/anomaly/index.d.ts.map +1 -0
  37. package/dist/src/anomaly/index.js +7 -0
  38. package/dist/src/anomaly/index.js.map +1 -0
  39. package/dist/src/approval/manager.d.ts +147 -0
  40. package/dist/src/approval/manager.d.ts.map +1 -0
  41. package/dist/src/approval/manager.js +511 -0
  42. package/dist/src/approval/manager.js.map +1 -0
  43. package/dist/src/approval/webhook.d.ts +36 -0
  44. package/dist/src/approval/webhook.d.ts.map +1 -0
  45. package/dist/src/approval/webhook.js +135 -0
  46. package/dist/src/approval/webhook.js.map +1 -0
  47. package/dist/src/audit/logger.d.ts +70 -0
  48. package/dist/src/audit/logger.d.ts.map +1 -0
  49. package/dist/src/audit/logger.js +440 -0
  50. package/dist/src/audit/logger.js.map +1 -0
  51. package/dist/src/auth/index.d.ts +6 -0
  52. package/dist/src/auth/index.d.ts.map +1 -0
  53. package/dist/src/auth/index.js +22 -0
  54. package/dist/src/auth/index.js.map +1 -0
  55. package/dist/src/auth/password.d.ts +3 -0
  56. package/dist/src/auth/password.d.ts.map +1 -0
  57. package/dist/src/auth/password.js +25 -0
  58. package/dist/src/auth/password.js.map +1 -0
  59. package/dist/src/auth/pkce.d.ts +13 -0
  60. package/dist/src/auth/pkce.d.ts.map +1 -0
  61. package/dist/src/auth/pkce.js +58 -0
  62. package/dist/src/auth/pkce.js.map +1 -0
  63. package/dist/src/auth/providers.d.ts +28 -0
  64. package/dist/src/auth/providers.d.ts.map +1 -0
  65. package/dist/src/auth/providers.js +198 -0
  66. package/dist/src/auth/providers.js.map +1 -0
  67. package/dist/src/auth/routes.d.ts +14 -0
  68. package/dist/src/auth/routes.d.ts.map +1 -0
  69. package/dist/src/auth/routes.js +431 -0
  70. package/dist/src/auth/routes.js.map +1 -0
  71. package/dist/src/auth/session.d.ts +24 -0
  72. package/dist/src/auth/session.d.ts.map +1 -0
  73. package/dist/src/auth/session.js +105 -0
  74. package/dist/src/auth/session.js.map +1 -0
  75. package/dist/src/billing/index.d.ts +7 -0
  76. package/dist/src/billing/index.d.ts.map +1 -0
  77. package/dist/src/billing/index.js +14 -0
  78. package/dist/src/billing/index.js.map +1 -0
  79. package/dist/src/billing/plan-enforcer.d.ts +44 -0
  80. package/dist/src/billing/plan-enforcer.d.ts.map +1 -0
  81. package/dist/src/billing/plan-enforcer.js +110 -0
  82. package/dist/src/billing/plan-enforcer.js.map +1 -0
  83. package/dist/src/billing/routes.d.ts +15 -0
  84. package/dist/src/billing/routes.d.ts.map +1 -0
  85. package/dist/src/billing/routes.js +193 -0
  86. package/dist/src/billing/routes.js.map +1 -0
  87. package/dist/src/billing/stripe-client.d.ts +14 -0
  88. package/dist/src/billing/stripe-client.d.ts.map +1 -0
  89. package/dist/src/billing/stripe-client.js +51 -0
  90. package/dist/src/billing/stripe-client.js.map +1 -0
  91. package/dist/src/billing/webhook-handler.d.ts +19 -0
  92. package/dist/src/billing/webhook-handler.d.ts.map +1 -0
  93. package/dist/src/billing/webhook-handler.js +169 -0
  94. package/dist/src/billing/webhook-handler.js.map +1 -0
  95. package/dist/src/billing/webhook-routes.d.ts +5 -0
  96. package/dist/src/billing/webhook-routes.d.ts.map +1 -0
  97. package/dist/src/billing/webhook-routes.js +30 -0
  98. package/dist/src/billing/webhook-routes.js.map +1 -0
  99. package/dist/src/budget/manager.d.ts +95 -0
  100. package/dist/src/budget/manager.d.ts.map +1 -0
  101. package/dist/src/budget/manager.js +547 -0
  102. package/dist/src/budget/manager.js.map +1 -0
  103. package/dist/src/budget/usage-extractor.d.ts +38 -0
  104. package/dist/src/budget/usage-extractor.d.ts.map +1 -0
  105. package/dist/src/budget/usage-extractor.js +165 -0
  106. package/dist/src/budget/usage-extractor.js.map +1 -0
  107. package/dist/src/cli.d.ts +3 -0
  108. package/dist/src/cli.d.ts.map +1 -0
  109. package/dist/src/cli.js +115 -0
  110. package/dist/src/cli.js.map +1 -0
  111. package/dist/src/config/defaults.d.ts +3 -0
  112. package/dist/src/config/defaults.d.ts.map +1 -0
  113. package/dist/src/config/defaults.js +243 -0
  114. package/dist/src/config/defaults.js.map +1 -0
  115. package/dist/src/config/validate.d.ts +15 -0
  116. package/dist/src/config/validate.d.ts.map +1 -0
  117. package/dist/src/config/validate.js +105 -0
  118. package/dist/src/config/validate.js.map +1 -0
  119. package/dist/src/dlp/composite-scanner.d.ts +47 -0
  120. package/dist/src/dlp/composite-scanner.d.ts.map +1 -0
  121. package/dist/src/dlp/composite-scanner.js +186 -0
  122. package/dist/src/dlp/composite-scanner.js.map +1 -0
  123. package/dist/src/dlp/index.d.ts +10 -0
  124. package/dist/src/dlp/index.d.ts.map +1 -0
  125. package/dist/src/dlp/index.js +26 -0
  126. package/dist/src/dlp/index.js.map +1 -0
  127. package/dist/src/dlp/interfaces.d.ts +33 -0
  128. package/dist/src/dlp/interfaces.d.ts.map +1 -0
  129. package/dist/src/dlp/interfaces.js +3 -0
  130. package/dist/src/dlp/interfaces.js.map +1 -0
  131. package/dist/src/dlp/patterns.d.ts +9 -0
  132. package/dist/src/dlp/patterns.d.ts.map +1 -0
  133. package/dist/src/dlp/patterns.js +25 -0
  134. package/dist/src/dlp/patterns.js.map +1 -0
  135. package/dist/src/dlp/prompt-injection-backend.d.ts +68 -0
  136. package/dist/src/dlp/prompt-injection-backend.d.ts.map +1 -0
  137. package/dist/src/dlp/prompt-injection-backend.js +148 -0
  138. package/dist/src/dlp/prompt-injection-backend.js.map +1 -0
  139. package/dist/src/dlp/prompt-injection-patterns.d.ts +32 -0
  140. package/dist/src/dlp/prompt-injection-patterns.d.ts.map +1 -0
  141. package/dist/src/dlp/prompt-injection-patterns.js +290 -0
  142. package/dist/src/dlp/prompt-injection-patterns.js.map +1 -0
  143. package/dist/src/dlp/regex-backend.d.ts +32 -0
  144. package/dist/src/dlp/regex-backend.d.ts.map +1 -0
  145. package/dist/src/dlp/regex-backend.js +153 -0
  146. package/dist/src/dlp/regex-backend.js.map +1 -0
  147. package/dist/src/dlp/scanner.d.ts +122 -0
  148. package/dist/src/dlp/scanner.d.ts.map +1 -0
  149. package/dist/src/dlp/scanner.js +444 -0
  150. package/dist/src/dlp/scanner.js.map +1 -0
  151. package/dist/src/dlp/text-normalizer.d.ts +41 -0
  152. package/dist/src/dlp/text-normalizer.d.ts.map +1 -0
  153. package/dist/src/dlp/text-normalizer.js +203 -0
  154. package/dist/src/dlp/text-normalizer.js.map +1 -0
  155. package/dist/src/dlp/trufflehog-backend.d.ts +64 -0
  156. package/dist/src/dlp/trufflehog-backend.d.ts.map +1 -0
  157. package/dist/src/dlp/trufflehog-backend.js +151 -0
  158. package/dist/src/dlp/trufflehog-backend.js.map +1 -0
  159. package/dist/src/executor/http-executor.d.ts +25 -0
  160. package/dist/src/executor/http-executor.d.ts.map +1 -0
  161. package/dist/src/executor/http-executor.js +333 -0
  162. package/dist/src/executor/http-executor.js.map +1 -0
  163. package/dist/src/executor/index.d.ts +6 -0
  164. package/dist/src/executor/index.d.ts.map +1 -0
  165. package/dist/src/executor/index.js +12 -0
  166. package/dist/src/executor/index.js.map +1 -0
  167. package/dist/src/executor/interfaces.d.ts +11 -0
  168. package/dist/src/executor/interfaces.d.ts.map +1 -0
  169. package/dist/src/executor/interfaces.js +3 -0
  170. package/dist/src/executor/interfaces.js.map +1 -0
  171. package/dist/src/executor/noop-executor.d.ts +13 -0
  172. package/dist/src/executor/noop-executor.d.ts.map +1 -0
  173. package/dist/src/executor/noop-executor.js +21 -0
  174. package/dist/src/executor/noop-executor.js.map +1 -0
  175. package/dist/src/executor/registry.d.ts +30 -0
  176. package/dist/src/executor/registry.d.ts.map +1 -0
  177. package/dist/src/executor/registry.js +62 -0
  178. package/dist/src/executor/registry.js.map +1 -0
  179. package/dist/src/executor/slack-executor.d.ts +24 -0
  180. package/dist/src/executor/slack-executor.d.ts.map +1 -0
  181. package/dist/src/executor/slack-executor.js +147 -0
  182. package/dist/src/executor/slack-executor.js.map +1 -0
  183. package/dist/src/index.d.ts +25 -0
  184. package/dist/src/index.d.ts.map +1 -0
  185. package/dist/src/index.js +74 -0
  186. package/dist/src/index.js.map +1 -0
  187. package/dist/src/mcp/auth-verifier.d.ts +23 -0
  188. package/dist/src/mcp/auth-verifier.d.ts.map +1 -0
  189. package/dist/src/mcp/auth-verifier.js +162 -0
  190. package/dist/src/mcp/auth-verifier.js.map +1 -0
  191. package/dist/src/mcp/bridge.d.ts +132 -0
  192. package/dist/src/mcp/bridge.d.ts.map +1 -0
  193. package/dist/src/mcp/bridge.js +734 -0
  194. package/dist/src/mcp/bridge.js.map +1 -0
  195. package/dist/src/mcp/http-transport.d.ts +32 -0
  196. package/dist/src/mcp/http-transport.d.ts.map +1 -0
  197. package/dist/src/mcp/http-transport.js +538 -0
  198. package/dist/src/mcp/http-transport.js.map +1 -0
  199. package/dist/src/mcp/index.d.ts +10 -0
  200. package/dist/src/mcp/index.d.ts.map +1 -0
  201. package/dist/src/mcp/index.js +17 -0
  202. package/dist/src/mcp/index.js.map +1 -0
  203. package/dist/src/mcp/oauth-pages.d.ts +23 -0
  204. package/dist/src/mcp/oauth-pages.d.ts.map +1 -0
  205. package/dist/src/mcp/oauth-pages.js +121 -0
  206. package/dist/src/mcp/oauth-pages.js.map +1 -0
  207. package/dist/src/mcp/oauth-postgres-stores.d.ts +55 -0
  208. package/dist/src/mcp/oauth-postgres-stores.d.ts.map +1 -0
  209. package/dist/src/mcp/oauth-postgres-stores.js +226 -0
  210. package/dist/src/mcp/oauth-postgres-stores.js.map +1 -0
  211. package/dist/src/mcp/oauth-provider.d.ts +95 -0
  212. package/dist/src/mcp/oauth-provider.d.ts.map +1 -0
  213. package/dist/src/mcp/oauth-provider.js +360 -0
  214. package/dist/src/mcp/oauth-provider.js.map +1 -0
  215. package/dist/src/mcp/oauth-stores.d.ts +62 -0
  216. package/dist/src/mcp/oauth-stores.d.ts.map +1 -0
  217. package/dist/src/mcp/oauth-stores.js +154 -0
  218. package/dist/src/mcp/oauth-stores.js.map +1 -0
  219. package/dist/src/mcp/server.d.ts +18 -0
  220. package/dist/src/mcp/server.d.ts.map +1 -0
  221. package/dist/src/mcp/server.js +51 -0
  222. package/dist/src/mcp/server.js.map +1 -0
  223. package/dist/src/metrics/collector.d.ts +106 -0
  224. package/dist/src/metrics/collector.d.ts.map +1 -0
  225. package/dist/src/metrics/collector.js +311 -0
  226. package/dist/src/metrics/collector.js.map +1 -0
  227. package/dist/src/metrics/index.d.ts +2 -0
  228. package/dist/src/metrics/index.d.ts.map +1 -0
  229. package/dist/src/metrics/index.js +6 -0
  230. package/dist/src/metrics/index.js.map +1 -0
  231. package/dist/src/middleware/auth.d.ts +77 -0
  232. package/dist/src/middleware/auth.d.ts.map +1 -0
  233. package/dist/src/middleware/auth.js +720 -0
  234. package/dist/src/middleware/auth.js.map +1 -0
  235. package/dist/src/middleware/session.d.ts +18 -0
  236. package/dist/src/middleware/session.d.ts.map +1 -0
  237. package/dist/src/middleware/session.js +67 -0
  238. package/dist/src/middleware/session.js.map +1 -0
  239. package/dist/src/middleware/validate.d.ts +3 -0
  240. package/dist/src/middleware/validate.d.ts.map +1 -0
  241. package/dist/src/middleware/validate.js +85 -0
  242. package/dist/src/middleware/validate.js.map +1 -0
  243. package/dist/src/policy/engine.d.ts +107 -0
  244. package/dist/src/policy/engine.d.ts.map +1 -0
  245. package/dist/src/policy/engine.js +646 -0
  246. package/dist/src/policy/engine.js.map +1 -0
  247. package/dist/src/policy/index.d.ts +3 -0
  248. package/dist/src/policy/index.d.ts.map +1 -0
  249. package/dist/src/policy/index.js +8 -0
  250. package/dist/src/policy/index.js.map +1 -0
  251. package/dist/src/policy/opa-engine.d.ts +176 -0
  252. package/dist/src/policy/opa-engine.d.ts.map +1 -0
  253. package/dist/src/policy/opa-engine.js +790 -0
  254. package/dist/src/policy/opa-engine.js.map +1 -0
  255. package/dist/src/proxy/forward-proxy.d.ts +30 -0
  256. package/dist/src/proxy/forward-proxy.d.ts.map +1 -0
  257. package/dist/src/proxy/forward-proxy.js +580 -0
  258. package/dist/src/proxy/forward-proxy.js.map +1 -0
  259. package/dist/src/proxy/index.d.ts +2 -0
  260. package/dist/src/proxy/index.d.ts.map +1 -0
  261. package/dist/src/proxy/index.js +8 -0
  262. package/dist/src/proxy/index.js.map +1 -0
  263. package/dist/src/ratelimit/limiter.d.ts +45 -0
  264. package/dist/src/ratelimit/limiter.d.ts.map +1 -0
  265. package/dist/src/ratelimit/limiter.js +158 -0
  266. package/dist/src/ratelimit/limiter.js.map +1 -0
  267. package/dist/src/replay/engine.d.ts +40 -0
  268. package/dist/src/replay/engine.d.ts.map +1 -0
  269. package/dist/src/replay/engine.js +106 -0
  270. package/dist/src/replay/engine.js.map +1 -0
  271. package/dist/src/replay/index.d.ts +2 -0
  272. package/dist/src/replay/index.d.ts.map +1 -0
  273. package/dist/src/replay/index.js +6 -0
  274. package/dist/src/replay/index.js.map +1 -0
  275. package/dist/src/saas/index.d.ts +2 -0
  276. package/dist/src/saas/index.d.ts.map +1 -0
  277. package/dist/src/saas/index.js +18 -0
  278. package/dist/src/saas/index.js.map +1 -0
  279. package/dist/src/saas/routes.d.ts +18 -0
  280. package/dist/src/saas/routes.d.ts.map +1 -0
  281. package/dist/src/saas/routes.js +1566 -0
  282. package/dist/src/saas/routes.js.map +1 -0
  283. package/dist/src/server/app.d.ts +44 -0
  284. package/dist/src/server/app.d.ts.map +1 -0
  285. package/dist/src/server/app.js +854 -0
  286. package/dist/src/server/app.js.map +1 -0
  287. package/dist/src/server/errors.d.ts +32 -0
  288. package/dist/src/server/errors.d.ts.map +1 -0
  289. package/dist/src/server/errors.js +39 -0
  290. package/dist/src/server/errors.js.map +1 -0
  291. package/dist/src/server/gateway.d.ts +165 -0
  292. package/dist/src/server/gateway.d.ts.map +1 -0
  293. package/dist/src/server/gateway.js +964 -0
  294. package/dist/src/server/gateway.js.map +1 -0
  295. package/dist/src/server/index.d.ts +2 -0
  296. package/dist/src/server/index.d.ts.map +1 -0
  297. package/dist/src/server/index.js +295 -0
  298. package/dist/src/server/index.js.map +1 -0
  299. package/dist/src/server/logger.d.ts +33 -0
  300. package/dist/src/server/logger.d.ts.map +1 -0
  301. package/dist/src/server/logger.js +230 -0
  302. package/dist/src/server/logger.js.map +1 -0
  303. package/dist/src/server/stream-proxy.d.ts +32 -0
  304. package/dist/src/server/stream-proxy.d.ts.map +1 -0
  305. package/dist/src/server/stream-proxy.js +184 -0
  306. package/dist/src/server/stream-proxy.js.map +1 -0
  307. package/dist/src/storage/file-persistence.d.ts +48 -0
  308. package/dist/src/storage/file-persistence.d.ts.map +1 -0
  309. package/dist/src/storage/file-persistence.js +280 -0
  310. package/dist/src/storage/file-persistence.js.map +1 -0
  311. package/dist/src/storage/index.d.ts +5 -0
  312. package/dist/src/storage/index.d.ts.map +1 -0
  313. package/dist/src/storage/index.js +21 -0
  314. package/dist/src/storage/index.js.map +1 -0
  315. package/dist/src/storage/interfaces.d.ts +237 -0
  316. package/dist/src/storage/interfaces.d.ts.map +1 -0
  317. package/dist/src/storage/interfaces.js +3 -0
  318. package/dist/src/storage/interfaces.js.map +1 -0
  319. package/dist/src/storage/memory.d.ts +162 -0
  320. package/dist/src/storage/memory.d.ts.map +1 -0
  321. package/dist/src/storage/memory.js +603 -0
  322. package/dist/src/storage/memory.js.map +1 -0
  323. package/dist/src/storage/postgres.d.ts +267 -0
  324. package/dist/src/storage/postgres.d.ts.map +1 -0
  325. package/dist/src/storage/postgres.js +1555 -0
  326. package/dist/src/storage/postgres.js.map +1 -0
  327. package/dist/src/storage/redis.d.ts +202 -0
  328. package/dist/src/storage/redis.d.ts.map +1 -0
  329. package/dist/src/storage/redis.js +629 -0
  330. package/dist/src/storage/redis.js.map +1 -0
  331. package/dist/src/tracing/index.d.ts +2 -0
  332. package/dist/src/tracing/index.d.ts.map +1 -0
  333. package/dist/src/tracing/index.js +6 -0
  334. package/dist/src/tracing/index.js.map +1 -0
  335. package/dist/src/tracing/provider.d.ts +43 -0
  336. package/dist/src/tracing/provider.d.ts.map +1 -0
  337. package/dist/src/tracing/provider.js +74 -0
  338. package/dist/src/tracing/provider.js.map +1 -0
  339. package/dist/src/trust/calculator.d.ts +54 -0
  340. package/dist/src/trust/calculator.d.ts.map +1 -0
  341. package/dist/src/trust/calculator.js +102 -0
  342. package/dist/src/trust/calculator.js.map +1 -0
  343. package/dist/src/trust/index.d.ts +2 -0
  344. package/dist/src/trust/index.d.ts.map +1 -0
  345. package/dist/src/trust/index.js +7 -0
  346. package/dist/src/trust/index.js.map +1 -0
  347. package/dist/src/types/budget.d.ts +30 -0
  348. package/dist/src/types/budget.d.ts.map +1 -0
  349. package/dist/src/types/budget.js +3 -0
  350. package/dist/src/types/budget.js.map +1 -0
  351. package/dist/src/types/config.d.ts +176 -0
  352. package/dist/src/types/config.d.ts.map +1 -0
  353. package/dist/src/types/config.js +3 -0
  354. package/dist/src/types/config.js.map +1 -0
  355. package/dist/src/types/events.d.ts +24 -0
  356. package/dist/src/types/events.d.ts.map +1 -0
  357. package/dist/src/types/events.js +3 -0
  358. package/dist/src/types/events.js.map +1 -0
  359. package/dist/src/types/index.d.ts +8 -0
  360. package/dist/src/types/index.d.ts.map +1 -0
  361. package/dist/src/types/index.js +24 -0
  362. package/dist/src/types/index.js.map +1 -0
  363. package/dist/src/types/policy.d.ts +60 -0
  364. package/dist/src/types/policy.d.ts.map +1 -0
  365. package/dist/src/types/policy.js +3 -0
  366. package/dist/src/types/policy.js.map +1 -0
  367. package/dist/src/types/stripe-config.d.ts +12 -0
  368. package/dist/src/types/stripe-config.d.ts.map +1 -0
  369. package/dist/src/types/stripe-config.js +3 -0
  370. package/dist/src/types/stripe-config.js.map +1 -0
  371. package/dist/src/types/subscription.d.ts +24 -0
  372. package/dist/src/types/subscription.d.ts.map +1 -0
  373. package/dist/src/types/subscription.js +38 -0
  374. package/dist/src/types/subscription.js.map +1 -0
  375. package/dist/src/types/tool-call.d.ts +42 -0
  376. package/dist/src/types/tool-call.d.ts.map +1 -0
  377. package/dist/src/types/tool-call.js +3 -0
  378. package/dist/src/types/tool-call.js.map +1 -0
  379. package/dist/src/types/tool-result.d.ts +58 -0
  380. package/dist/src/types/tool-result.d.ts.map +1 -0
  381. package/dist/src/types/tool-result.js +3 -0
  382. package/dist/src/types/tool-result.js.map +1 -0
  383. package/dist/src/types/user.d.ts +101 -0
  384. package/dist/src/types/user.d.ts.map +1 -0
  385. package/dist/src/types/user.js +6 -0
  386. package/dist/src/types/user.js.map +1 -0
  387. package/dist/tests/integration/api.test.d.ts +2 -0
  388. package/dist/tests/integration/api.test.d.ts.map +1 -0
  389. package/dist/tests/integration/api.test.js +1199 -0
  390. package/dist/tests/integration/api.test.js.map +1 -0
  391. package/dist/tests/integration/proxy.test.d.ts +2 -0
  392. package/dist/tests/integration/proxy.test.d.ts.map +1 -0
  393. package/dist/tests/integration/proxy.test.js +251 -0
  394. package/dist/tests/integration/proxy.test.js.map +1 -0
  395. package/dist/tests/integration/storage.test.d.ts +16 -0
  396. package/dist/tests/integration/storage.test.d.ts.map +1 -0
  397. package/dist/tests/integration/storage.test.js +826 -0
  398. package/dist/tests/integration/storage.test.js.map +1 -0
  399. package/dist/tests/unit/admin.test.d.ts +2 -0
  400. package/dist/tests/unit/admin.test.d.ts.map +1 -0
  401. package/dist/tests/unit/admin.test.js +698 -0
  402. package/dist/tests/unit/admin.test.js.map +1 -0
  403. package/dist/tests/unit/anomaly-detector.test.d.ts +2 -0
  404. package/dist/tests/unit/anomaly-detector.test.d.ts.map +1 -0
  405. package/dist/tests/unit/anomaly-detector.test.js +903 -0
  406. package/dist/tests/unit/anomaly-detector.test.js.map +1 -0
  407. package/dist/tests/unit/approval-manager.test.d.ts +2 -0
  408. package/dist/tests/unit/approval-manager.test.d.ts.map +1 -0
  409. package/dist/tests/unit/approval-manager.test.js +528 -0
  410. package/dist/tests/unit/approval-manager.test.js.map +1 -0
  411. package/dist/tests/unit/approval-webhook.test.d.ts +2 -0
  412. package/dist/tests/unit/approval-webhook.test.d.ts.map +1 -0
  413. package/dist/tests/unit/approval-webhook.test.js +355 -0
  414. package/dist/tests/unit/approval-webhook.test.js.map +1 -0
  415. package/dist/tests/unit/audit-logger.test.d.ts +2 -0
  416. package/dist/tests/unit/audit-logger.test.d.ts.map +1 -0
  417. package/dist/tests/unit/audit-logger.test.js +635 -0
  418. package/dist/tests/unit/audit-logger.test.js.map +1 -0
  419. package/dist/tests/unit/auth-routes.test.d.ts +2 -0
  420. package/dist/tests/unit/auth-routes.test.d.ts.map +1 -0
  421. package/dist/tests/unit/auth-routes.test.js +281 -0
  422. package/dist/tests/unit/auth-routes.test.js.map +1 -0
  423. package/dist/tests/unit/auth.test.d.ts +2 -0
  424. package/dist/tests/unit/auth.test.d.ts.map +1 -0
  425. package/dist/tests/unit/auth.test.js +1382 -0
  426. package/dist/tests/unit/auth.test.js.map +1 -0
  427. package/dist/tests/unit/billing.test.d.ts +2 -0
  428. package/dist/tests/unit/billing.test.d.ts.map +1 -0
  429. package/dist/tests/unit/billing.test.js +579 -0
  430. package/dist/tests/unit/billing.test.js.map +1 -0
  431. package/dist/tests/unit/budget-manager.test.d.ts +2 -0
  432. package/dist/tests/unit/budget-manager.test.d.ts.map +1 -0
  433. package/dist/tests/unit/budget-manager.test.js +778 -0
  434. package/dist/tests/unit/budget-manager.test.js.map +1 -0
  435. package/dist/tests/unit/budget-race.test.d.ts +2 -0
  436. package/dist/tests/unit/budget-race.test.d.ts.map +1 -0
  437. package/dist/tests/unit/budget-race.test.js +58 -0
  438. package/dist/tests/unit/budget-race.test.js.map +1 -0
  439. package/dist/tests/unit/cli.test.d.ts +2 -0
  440. package/dist/tests/unit/cli.test.d.ts.map +1 -0
  441. package/dist/tests/unit/cli.test.js +93 -0
  442. package/dist/tests/unit/cli.test.js.map +1 -0
  443. package/dist/tests/unit/concurrency.test.d.ts +2 -0
  444. package/dist/tests/unit/concurrency.test.d.ts.map +1 -0
  445. package/dist/tests/unit/concurrency.test.js +1270 -0
  446. package/dist/tests/unit/concurrency.test.js.map +1 -0
  447. package/dist/tests/unit/config-validate.test.d.ts +2 -0
  448. package/dist/tests/unit/config-validate.test.d.ts.map +1 -0
  449. package/dist/tests/unit/config-validate.test.js +230 -0
  450. package/dist/tests/unit/config-validate.test.js.map +1 -0
  451. package/dist/tests/unit/defaults.test.d.ts +2 -0
  452. package/dist/tests/unit/defaults.test.d.ts.map +1 -0
  453. package/dist/tests/unit/defaults.test.js +364 -0
  454. package/dist/tests/unit/defaults.test.js.map +1 -0
  455. package/dist/tests/unit/dlp-backends.test.d.ts +2 -0
  456. package/dist/tests/unit/dlp-backends.test.d.ts.map +1 -0
  457. package/dist/tests/unit/dlp-backends.test.js +563 -0
  458. package/dist/tests/unit/dlp-backends.test.js.map +1 -0
  459. package/dist/tests/unit/dlp-scanner.test.d.ts +2 -0
  460. package/dist/tests/unit/dlp-scanner.test.d.ts.map +1 -0
  461. package/dist/tests/unit/dlp-scanner.test.js +739 -0
  462. package/dist/tests/unit/dlp-scanner.test.js.map +1 -0
  463. package/dist/tests/unit/error-responses.test.d.ts +2 -0
  464. package/dist/tests/unit/error-responses.test.d.ts.map +1 -0
  465. package/dist/tests/unit/error-responses.test.js +101 -0
  466. package/dist/tests/unit/error-responses.test.js.map +1 -0
  467. package/dist/tests/unit/executor-registry.test.d.ts +2 -0
  468. package/dist/tests/unit/executor-registry.test.d.ts.map +1 -0
  469. package/dist/tests/unit/executor-registry.test.js +390 -0
  470. package/dist/tests/unit/executor-registry.test.js.map +1 -0
  471. package/dist/tests/unit/forward-proxy.test.d.ts +2 -0
  472. package/dist/tests/unit/forward-proxy.test.d.ts.map +1 -0
  473. package/dist/tests/unit/forward-proxy.test.js +621 -0
  474. package/dist/tests/unit/forward-proxy.test.js.map +1 -0
  475. package/dist/tests/unit/gateway-features.test.d.ts +2 -0
  476. package/dist/tests/unit/gateway-features.test.d.ts.map +1 -0
  477. package/dist/tests/unit/gateway-features.test.js +753 -0
  478. package/dist/tests/unit/gateway-features.test.js.map +1 -0
  479. package/dist/tests/unit/http-executor.test.d.ts +2 -0
  480. package/dist/tests/unit/http-executor.test.d.ts.map +1 -0
  481. package/dist/tests/unit/http-executor.test.js +310 -0
  482. package/dist/tests/unit/http-executor.test.js.map +1 -0
  483. package/dist/tests/unit/mcp-bridge.test.d.ts +2 -0
  484. package/dist/tests/unit/mcp-bridge.test.d.ts.map +1 -0
  485. package/dist/tests/unit/mcp-bridge.test.js +1136 -0
  486. package/dist/tests/unit/mcp-bridge.test.js.map +1 -0
  487. package/dist/tests/unit/mcp-http-transport.test.d.ts +2 -0
  488. package/dist/tests/unit/mcp-http-transport.test.d.ts.map +1 -0
  489. package/dist/tests/unit/mcp-http-transport.test.js +899 -0
  490. package/dist/tests/unit/mcp-http-transport.test.js.map +1 -0
  491. package/dist/tests/unit/mcp-oauth.test.d.ts +2 -0
  492. package/dist/tests/unit/mcp-oauth.test.d.ts.map +1 -0
  493. package/dist/tests/unit/mcp-oauth.test.js +759 -0
  494. package/dist/tests/unit/mcp-oauth.test.js.map +1 -0
  495. package/dist/tests/unit/mcp-server.test.d.ts +15 -0
  496. package/dist/tests/unit/mcp-server.test.d.ts.map +1 -0
  497. package/dist/tests/unit/mcp-server.test.js +158 -0
  498. package/dist/tests/unit/mcp-server.test.js.map +1 -0
  499. package/dist/tests/unit/metrics.test.d.ts +2 -0
  500. package/dist/tests/unit/metrics.test.d.ts.map +1 -0
  501. package/dist/tests/unit/metrics.test.js +208 -0
  502. package/dist/tests/unit/metrics.test.js.map +1 -0
  503. package/dist/tests/unit/oauth.test.d.ts +2 -0
  504. package/dist/tests/unit/oauth.test.d.ts.map +1 -0
  505. package/dist/tests/unit/oauth.test.js +281 -0
  506. package/dist/tests/unit/oauth.test.js.map +1 -0
  507. package/dist/tests/unit/opa-circuit-breaker.test.d.ts +2 -0
  508. package/dist/tests/unit/opa-circuit-breaker.test.d.ts.map +1 -0
  509. package/dist/tests/unit/opa-circuit-breaker.test.js +297 -0
  510. package/dist/tests/unit/opa-circuit-breaker.test.js.map +1 -0
  511. package/dist/tests/unit/opa-engine.test.d.ts +2 -0
  512. package/dist/tests/unit/opa-engine.test.d.ts.map +1 -0
  513. package/dist/tests/unit/opa-engine.test.js +1813 -0
  514. package/dist/tests/unit/opa-engine.test.js.map +1 -0
  515. package/dist/tests/unit/pipeline-timing.test.d.ts +2 -0
  516. package/dist/tests/unit/pipeline-timing.test.d.ts.map +1 -0
  517. package/dist/tests/unit/pipeline-timing.test.js +528 -0
  518. package/dist/tests/unit/pipeline-timing.test.js.map +1 -0
  519. package/dist/tests/unit/policy-engine.test.d.ts +2 -0
  520. package/dist/tests/unit/policy-engine.test.d.ts.map +1 -0
  521. package/dist/tests/unit/policy-engine.test.js +1345 -0
  522. package/dist/tests/unit/policy-engine.test.js.map +1 -0
  523. package/dist/tests/unit/policy-store.test.d.ts +2 -0
  524. package/dist/tests/unit/policy-store.test.d.ts.map +1 -0
  525. package/dist/tests/unit/policy-store.test.js +60 -0
  526. package/dist/tests/unit/policy-store.test.js.map +1 -0
  527. package/dist/tests/unit/postgres-storage.test.d.ts +2 -0
  528. package/dist/tests/unit/postgres-storage.test.d.ts.map +1 -0
  529. package/dist/tests/unit/postgres-storage.test.js +614 -0
  530. package/dist/tests/unit/postgres-storage.test.js.map +1 -0
  531. package/dist/tests/unit/prompt-injection-backend.test.d.ts +2 -0
  532. package/dist/tests/unit/prompt-injection-backend.test.d.ts.map +1 -0
  533. package/dist/tests/unit/prompt-injection-backend.test.js +621 -0
  534. package/dist/tests/unit/prompt-injection-backend.test.js.map +1 -0
  535. package/dist/tests/unit/proxy-hardening.test.d.ts +2 -0
  536. package/dist/tests/unit/proxy-hardening.test.d.ts.map +1 -0
  537. package/dist/tests/unit/proxy-hardening.test.js +166 -0
  538. package/dist/tests/unit/proxy-hardening.test.js.map +1 -0
  539. package/dist/tests/unit/rate-limiter.test.d.ts +2 -0
  540. package/dist/tests/unit/rate-limiter.test.d.ts.map +1 -0
  541. package/dist/tests/unit/rate-limiter.test.js +443 -0
  542. package/dist/tests/unit/rate-limiter.test.js.map +1 -0
  543. package/dist/tests/unit/redis-storage.test.d.ts +2 -0
  544. package/dist/tests/unit/redis-storage.test.d.ts.map +1 -0
  545. package/dist/tests/unit/redis-storage.test.js +766 -0
  546. package/dist/tests/unit/redis-storage.test.js.map +1 -0
  547. package/dist/tests/unit/replay-engine.test.d.ts +2 -0
  548. package/dist/tests/unit/replay-engine.test.d.ts.map +1 -0
  549. package/dist/tests/unit/replay-engine.test.js +371 -0
  550. package/dist/tests/unit/replay-engine.test.js.map +1 -0
  551. package/dist/tests/unit/saas-routes.test.d.ts +2 -0
  552. package/dist/tests/unit/saas-routes.test.d.ts.map +1 -0
  553. package/dist/tests/unit/saas-routes.test.js +1399 -0
  554. package/dist/tests/unit/saas-routes.test.js.map +1 -0
  555. package/dist/tests/unit/session.test.d.ts +2 -0
  556. package/dist/tests/unit/session.test.d.ts.map +1 -0
  557. package/dist/tests/unit/session.test.js +532 -0
  558. package/dist/tests/unit/session.test.js.map +1 -0
  559. package/dist/tests/unit/slack-executor.test.d.ts +2 -0
  560. package/dist/tests/unit/slack-executor.test.d.ts.map +1 -0
  561. package/dist/tests/unit/slack-executor.test.js +209 -0
  562. package/dist/tests/unit/slack-executor.test.js.map +1 -0
  563. package/dist/tests/unit/storage-hardening.test.d.ts +2 -0
  564. package/dist/tests/unit/storage-hardening.test.d.ts.map +1 -0
  565. package/dist/tests/unit/storage-hardening.test.js +165 -0
  566. package/dist/tests/unit/storage-hardening.test.js.map +1 -0
  567. package/dist/tests/unit/storage.test.d.ts +2 -0
  568. package/dist/tests/unit/storage.test.d.ts.map +1 -0
  569. package/dist/tests/unit/storage.test.js +698 -0
  570. package/dist/tests/unit/storage.test.js.map +1 -0
  571. package/dist/tests/unit/text-normalizer.test.d.ts +2 -0
  572. package/dist/tests/unit/text-normalizer.test.d.ts.map +1 -0
  573. package/dist/tests/unit/text-normalizer.test.js +229 -0
  574. package/dist/tests/unit/text-normalizer.test.js.map +1 -0
  575. package/dist/tests/unit/tracing.test.d.ts +2 -0
  576. package/dist/tests/unit/tracing.test.d.ts.map +1 -0
  577. package/dist/tests/unit/tracing.test.js +611 -0
  578. package/dist/tests/unit/tracing.test.js.map +1 -0
  579. package/dist/tests/unit/trust-calculator.test.d.ts +2 -0
  580. package/dist/tests/unit/trust-calculator.test.d.ts.map +1 -0
  581. package/dist/tests/unit/trust-calculator.test.js +497 -0
  582. package/dist/tests/unit/trust-calculator.test.js.map +1 -0
  583. package/dist/tests/unit/ts-sdk.test.d.ts +2 -0
  584. package/dist/tests/unit/ts-sdk.test.d.ts.map +1 -0
  585. package/dist/tests/unit/ts-sdk.test.js +421 -0
  586. package/dist/tests/unit/ts-sdk.test.js.map +1 -0
  587. package/dist/tests/unit/usage-extractor-llm.test.d.ts +2 -0
  588. package/dist/tests/unit/usage-extractor-llm.test.d.ts.map +1 -0
  589. package/dist/tests/unit/usage-extractor-llm.test.js +139 -0
  590. package/dist/tests/unit/usage-extractor-llm.test.js.map +1 -0
  591. package/dist/tests/unit/usage-extractor.test.d.ts +2 -0
  592. package/dist/tests/unit/usage-extractor.test.d.ts.map +1 -0
  593. package/dist/tests/unit/usage-extractor.test.js +271 -0
  594. package/dist/tests/unit/usage-extractor.test.js.map +1 -0
  595. package/dist/tests/unit/user-stores.test.d.ts +2 -0
  596. package/dist/tests/unit/user-stores.test.d.ts.map +1 -0
  597. package/dist/tests/unit/user-stores.test.js +687 -0
  598. package/dist/tests/unit/user-stores.test.js.map +1 -0
  599. package/dist/tests/unit/validate.test.d.ts +2 -0
  600. package/dist/tests/unit/validate.test.d.ts.map +1 -0
  601. package/dist/tests/unit/validate.test.js +545 -0
  602. package/dist/tests/unit/validate.test.js.map +1 -0
  603. package/package.json +86 -0
  604. package/policy-packs/README.md +42 -0
  605. package/policy-packs/default.yaml +46 -0
  606. package/policy-packs/dev_fast.yaml +54 -0
  607. package/policy-packs/prod_strict.yaml +83 -0
@@ -0,0 +1,1199 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ const supertest_1 = __importDefault(require("supertest"));
40
+ const http = __importStar(require("http"));
41
+ const app_1 = require("../../src/server/app");
42
+ // ---------------------------------------------------------------------------
43
+ // Local test HTTP server for the executor to call
44
+ // ---------------------------------------------------------------------------
45
+ let testServer;
46
+ let testServerPort;
47
+ beforeAll((done) => {
48
+ testServer = http.createServer((req, res) => {
49
+ if (req.url === '/api/data' && req.method === 'GET') {
50
+ res.writeHead(200, { 'Content-Type': 'application/json' });
51
+ res.end(JSON.stringify({ data: 'test response', items: [1, 2, 3] }));
52
+ }
53
+ else if (req.url === '/api/create' && req.method === 'POST') {
54
+ let body = '';
55
+ req.on('data', (chunk) => (body += chunk));
56
+ req.on('end', () => {
57
+ res.writeHead(201, { 'Content-Type': 'application/json' });
58
+ res.end(JSON.stringify({ created: true, received: JSON.parse(body) }));
59
+ });
60
+ }
61
+ else if (req.url === '/api/secret-echo') {
62
+ res.writeHead(200, { 'Content-Type': 'application/json' });
63
+ res.end(JSON.stringify({ token: 'Bearer sk-secret-token-12345678' }));
64
+ }
65
+ else {
66
+ res.writeHead(404, { 'Content-Type': 'application/json' });
67
+ res.end(JSON.stringify({ error: 'not found' }));
68
+ }
69
+ });
70
+ testServer.listen(0, '127.0.0.1', () => {
71
+ testServerPort = testServer.address().port;
72
+ done();
73
+ });
74
+ });
75
+ afterAll((done) => {
76
+ testServer.close(done);
77
+ });
78
+ // ---------------------------------------------------------------------------
79
+ // Test config factory
80
+ // ---------------------------------------------------------------------------
81
+ function createTestConfig(overrides) {
82
+ return {
83
+ port: 0,
84
+ host: '127.0.0.1',
85
+ auth: {
86
+ enabled: true,
87
+ api_keys: {
88
+ 'test-key': { workspace_id: 'ws_test', description: 'Test key', roles: ['admin'] },
89
+ 'approver-key': { workspace_id: 'ws_test', description: 'Approver key', roles: ['admin'] },
90
+ },
91
+ },
92
+ policy: {
93
+ pack_path: './policy-packs/dev_fast.yaml',
94
+ default_effect: 'DENY',
95
+ hot_reload: false,
96
+ },
97
+ dlp: {
98
+ enabled: true,
99
+ scan_args: true,
100
+ scan_output: true,
101
+ secrets_detection: true,
102
+ pii_detection: true,
103
+ default_redaction_method: 'mask',
104
+ },
105
+ budget: {
106
+ task_budget_usd: 2.0,
107
+ max_steps_per_task: 50,
108
+ max_retries_per_call: 3,
109
+ max_wall_clock_ms: 300000,
110
+ },
111
+ audit: {
112
+ enabled: true,
113
+ log_dir: '',
114
+ console_output: false,
115
+ retention_days: 30,
116
+ },
117
+ executor: {
118
+ http: {
119
+ timeout_ms: 5000,
120
+ max_retries: 1,
121
+ backoff_base_ms: 100,
122
+ },
123
+ cache: {
124
+ enabled: true,
125
+ ttl_ms: 60000,
126
+ },
127
+ },
128
+ approval: {
129
+ enabled: true,
130
+ token_secret: 'test-integration-secret',
131
+ default_ttl_seconds: 3600,
132
+ },
133
+ rate_limit: {
134
+ enabled: true,
135
+ actor_max_per_window: 100,
136
+ workspace_max_per_window: 500,
137
+ window_ms: 60000,
138
+ },
139
+ ...overrides,
140
+ };
141
+ }
142
+ // ---------------------------------------------------------------------------
143
+ // Helper: build a valid ToolCall payload
144
+ // ---------------------------------------------------------------------------
145
+ function buildToolCall(overrides) {
146
+ return {
147
+ tool_call_id: `tc-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
148
+ task_id: 'task-integ-001',
149
+ actor: { type: 'agent', id: 'test-agent', display: 'Test Agent' },
150
+ source: { platform: 'integration-test' },
151
+ tool: { name: 'http.request', capability: 'read' },
152
+ args: {
153
+ method: 'GET',
154
+ url: `http://127.0.0.1:${testServerPort}/api/data`,
155
+ },
156
+ ...overrides,
157
+ };
158
+ }
159
+ // ---------------------------------------------------------------------------
160
+ // Helper: extract CSRF token from HTML response
161
+ // ---------------------------------------------------------------------------
162
+ function extractCSRFToken(html) {
163
+ const match = html.match(/name="_csrf"\s+value="([^"]+)"/);
164
+ return match ? match[1] : '';
165
+ }
166
+ // ===========================================================================
167
+ // Integration Tests
168
+ // ===========================================================================
169
+ describe('Gateway API Integration', () => {
170
+ let app;
171
+ let gateway;
172
+ let healthChecks;
173
+ beforeEach(() => {
174
+ const result = (0, app_1.createApp)(createTestConfig());
175
+ app = result.app;
176
+ gateway = result.gateway;
177
+ healthChecks = result.healthChecks;
178
+ // Disable SSRF protection for tests using localhost
179
+ gateway.getHttpExecutor().ssrfProtectionEnabled = false;
180
+ });
181
+ afterEach(() => {
182
+ gateway.shutdown();
183
+ });
184
+ // -------------------------------------------------------------------------
185
+ // Health Check
186
+ // -------------------------------------------------------------------------
187
+ describe('GET /health', () => {
188
+ it('should return 200 with status ok', async () => {
189
+ const res = await (0, supertest_1.default)(app).get('/health');
190
+ expect(res.status).toBe(200);
191
+ expect(res.body.status).toBe('ok');
192
+ });
193
+ });
194
+ // -------------------------------------------------------------------------
195
+ // Authentication
196
+ // -------------------------------------------------------------------------
197
+ describe('Authentication', () => {
198
+ it('should return 401 without API key', async () => {
199
+ const res = await (0, supertest_1.default)(app)
200
+ .post('/v1/tool/execute')
201
+ .send(buildToolCall());
202
+ expect(res.status).toBe(401);
203
+ expect(res.body.error).toContain('Missing API key');
204
+ });
205
+ it('should return 403 with invalid API key', async () => {
206
+ const res = await (0, supertest_1.default)(app)
207
+ .post('/v1/tool/execute')
208
+ .set('X-API-Key', 'bad-key')
209
+ .send(buildToolCall());
210
+ expect(res.status).toBe(403);
211
+ expect(res.body.error).toContain('Invalid API key');
212
+ });
213
+ it('should accept valid API key via X-API-Key header', async () => {
214
+ const res = await (0, supertest_1.default)(app)
215
+ .post('/v1/tool/execute')
216
+ .set('X-API-Key', 'test-key')
217
+ .send(buildToolCall());
218
+ expect(res.status).not.toBe(401);
219
+ expect(res.status).not.toBe(403);
220
+ });
221
+ it('should accept valid API key via Bearer token', async () => {
222
+ const res = await (0, supertest_1.default)(app)
223
+ .post('/v1/tool/execute')
224
+ .set('Authorization', 'Bearer test-key')
225
+ .send(buildToolCall());
226
+ expect(res.status).not.toBe(401);
227
+ expect(res.status).not.toBe(403);
228
+ });
229
+ });
230
+ // -------------------------------------------------------------------------
231
+ // POST /v1/tool/execute
232
+ // -------------------------------------------------------------------------
233
+ describe('POST /v1/tool/execute', () => {
234
+ it('should return 400 when tool_call_id is missing', async () => {
235
+ const { tool_call_id, ...body } = buildToolCall();
236
+ const res = await (0, supertest_1.default)(app)
237
+ .post('/v1/tool/execute')
238
+ .set('X-API-Key', 'test-key')
239
+ .send(body);
240
+ expect(res.status).toBe(400);
241
+ expect(res.body.details).toContain('tool_call_id is required');
242
+ });
243
+ it('should return 400 when actor is missing', async () => {
244
+ const { actor, ...body } = buildToolCall();
245
+ const res = await (0, supertest_1.default)(app)
246
+ .post('/v1/tool/execute')
247
+ .set('X-API-Key', 'test-key')
248
+ .send(body);
249
+ expect(res.status).toBe(400);
250
+ });
251
+ it('should return 400 when tool is missing', async () => {
252
+ const { tool, ...body } = buildToolCall();
253
+ const res = await (0, supertest_1.default)(app)
254
+ .post('/v1/tool/execute')
255
+ .set('X-API-Key', 'test-key')
256
+ .send(body);
257
+ expect(res.status).toBe(400);
258
+ });
259
+ it('should execute a read request and return status ok', async () => {
260
+ const res = await (0, supertest_1.default)(app)
261
+ .post('/v1/tool/execute')
262
+ .set('X-API-Key', 'test-key')
263
+ .send(buildToolCall());
264
+ expect(res.status).toBe(200);
265
+ expect(res.body.status).toBe('ok');
266
+ expect(res.body.policy.decision).toBe('allow');
267
+ });
268
+ it('should include all ToolResult fields in response', async () => {
269
+ const res = await (0, supertest_1.default)(app)
270
+ .post('/v1/tool/execute')
271
+ .set('X-API-Key', 'test-key')
272
+ .send(buildToolCall());
273
+ expect(res.body).toHaveProperty('tool_call_id');
274
+ expect(res.body).toHaveProperty('task_id');
275
+ expect(res.body).toHaveProperty('status');
276
+ expect(res.body).toHaveProperty('policy');
277
+ expect(res.body).toHaveProperty('dlp');
278
+ expect(res.body).toHaveProperty('budget');
279
+ expect(res.body).toHaveProperty('timing');
280
+ expect(res.body.policy).toHaveProperty('decision');
281
+ expect(res.body.policy).toHaveProperty('reasons');
282
+ expect(res.body.dlp).toHaveProperty('detected');
283
+ expect(res.body.dlp).toHaveProperty('severity');
284
+ expect(res.body.budget).toHaveProperty('estimated_cost_usd');
285
+ expect(res.body.budget).toHaveProperty('spent_cost_usd_task');
286
+ expect(res.body.budget).toHaveProperty('remaining_cost_usd_task');
287
+ expect(res.body.timing).toHaveProperty('started_at');
288
+ expect(res.body.timing).toHaveProperty('duration_ms');
289
+ });
290
+ it('should return output with http_status and body', async () => {
291
+ const res = await (0, supertest_1.default)(app)
292
+ .post('/v1/tool/execute')
293
+ .set('X-API-Key', 'test-key')
294
+ .send(buildToolCall());
295
+ expect(res.body.output).toBeDefined();
296
+ expect(res.body.output.http_status).toBe(200);
297
+ expect(res.body.output.body).toEqual({ data: 'test response', items: [1, 2, 3] });
298
+ });
299
+ it('should allow write operations with dev_fast policy', async () => {
300
+ const res = await (0, supertest_1.default)(app)
301
+ .post('/v1/tool/execute')
302
+ .set('X-API-Key', 'test-key')
303
+ .send(buildToolCall({
304
+ tool: { name: 'http.request', capability: 'write' },
305
+ args: {
306
+ method: 'POST',
307
+ url: `http://127.0.0.1:${testServerPort}/api/create`,
308
+ body: { key: 'value' },
309
+ },
310
+ }));
311
+ expect(res.status).toBe(200);
312
+ expect(res.body.status).toBe('ok');
313
+ expect(res.body.policy.decision).toBe('allow');
314
+ });
315
+ it('should require approval for delete operations with dev_fast policy', async () => {
316
+ const res = await (0, supertest_1.default)(app)
317
+ .post('/v1/tool/execute')
318
+ .set('X-API-Key', 'test-key')
319
+ .send(buildToolCall({
320
+ tool: { name: 'http.request', capability: 'delete' },
321
+ args: {
322
+ method: 'DELETE',
323
+ url: `http://127.0.0.1:${testServerPort}/api/data`,
324
+ },
325
+ }));
326
+ expect(res.status).toBe(202);
327
+ expect(res.body.status).toBe('needs_approval');
328
+ expect(res.body.approval).toBeDefined();
329
+ expect(res.body.approval.token).toBeDefined();
330
+ expect(res.body.approval.approval_id).toBeDefined();
331
+ });
332
+ it('should track budget across multiple calls in the same task', async () => {
333
+ const taskId = 'task-budget-test';
334
+ const res1 = await (0, supertest_1.default)(app)
335
+ .post('/v1/tool/execute')
336
+ .set('X-API-Key', 'test-key')
337
+ .send(buildToolCall({ task_id: taskId }));
338
+ const res2 = await (0, supertest_1.default)(app)
339
+ .post('/v1/tool/execute')
340
+ .set('X-API-Key', 'test-key')
341
+ .send(buildToolCall({ task_id: taskId }));
342
+ expect(res2.body.budget.spent_cost_usd_task).toBeGreaterThan(0);
343
+ expect(res2.body.budget.remaining_cost_usd_task).toBeLessThan(res1.body.budget.remaining_cost_usd_task);
344
+ });
345
+ });
346
+ // -------------------------------------------------------------------------
347
+ // DLP Detection in Pipeline
348
+ // -------------------------------------------------------------------------
349
+ describe('DLP Detection', () => {
350
+ it('should detect secrets in tool call args (Bearer token in header)', async () => {
351
+ const res = await (0, supertest_1.default)(app)
352
+ .post('/v1/tool/execute')
353
+ .set('X-API-Key', 'test-key')
354
+ .send(buildToolCall({
355
+ args: {
356
+ method: 'GET',
357
+ url: `http://127.0.0.1:${testServerPort}/api/data`,
358
+ headers: {
359
+ Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U',
360
+ },
361
+ },
362
+ }));
363
+ expect(res.body.dlp.detected.length).toBeGreaterThan(0);
364
+ expect(res.body.dlp.severity).not.toBe('low');
365
+ });
366
+ });
367
+ // -------------------------------------------------------------------------
368
+ // POST /v1/tool/approve
369
+ // -------------------------------------------------------------------------
370
+ describe('POST /v1/tool/approve', () => {
371
+ it('should return 400 without approval_token', async () => {
372
+ const res = await (0, supertest_1.default)(app)
373
+ .post('/v1/tool/approve')
374
+ .set('X-API-Key', 'test-key')
375
+ .send({});
376
+ expect(res.status).toBe(400);
377
+ expect(res.body.error).toContain('approval_token is required');
378
+ });
379
+ it('should approve a pending action with valid token', async () => {
380
+ // First trigger an approval
381
+ const execRes = await (0, supertest_1.default)(app)
382
+ .post('/v1/tool/execute')
383
+ .set('X-API-Key', 'test-key')
384
+ .send(buildToolCall({
385
+ tool: { name: 'http.request', capability: 'delete' },
386
+ args: { method: 'DELETE', url: `http://127.0.0.1:${testServerPort}/api/data` },
387
+ }));
388
+ expect(execRes.body.status).toBe('needs_approval');
389
+ const token = execRes.body.approval.token;
390
+ // Now approve it (using a different key to avoid self-approval rejection)
391
+ const approveRes = await (0, supertest_1.default)(app)
392
+ .post('/v1/tool/approve')
393
+ .set('X-API-Key', 'approver-key')
394
+ .send({
395
+ approval_token: token,
396
+ approved: true,
397
+ approver_id: 'admin-001',
398
+ });
399
+ expect(approveRes.status).toBe(200);
400
+ expect(approveRes.body.status).toBe('ok');
401
+ expect(approveRes.body.success).toBe(true);
402
+ });
403
+ it('should deny a pending action', async () => {
404
+ const execRes = await (0, supertest_1.default)(app)
405
+ .post('/v1/tool/execute')
406
+ .set('X-API-Key', 'test-key')
407
+ .send(buildToolCall({
408
+ tool: { name: 'http.request', capability: 'admin' },
409
+ args: { method: 'DELETE', url: `http://127.0.0.1:${testServerPort}/api/data` },
410
+ }));
411
+ const token = execRes.body.approval.token;
412
+ const denyRes = await (0, supertest_1.default)(app)
413
+ .post('/v1/tool/approve')
414
+ .set('X-API-Key', 'test-key')
415
+ .send({
416
+ approval_token: token,
417
+ approved: false,
418
+ approver_id: 'admin-001',
419
+ reason: 'Not authorized',
420
+ });
421
+ expect(denyRes.status).toBe(200);
422
+ });
423
+ });
424
+ // -------------------------------------------------------------------------
425
+ // GET /v1/tasks/:task_id/trace
426
+ // -------------------------------------------------------------------------
427
+ describe('GET /v1/tasks/:task_id/trace', () => {
428
+ it('should return events for a task after execution', async () => {
429
+ const taskId = 'task-trace-test';
430
+ await (0, supertest_1.default)(app)
431
+ .post('/v1/tool/execute')
432
+ .set('X-API-Key', 'test-key')
433
+ .send(buildToolCall({ task_id: taskId }));
434
+ const res = await (0, supertest_1.default)(app)
435
+ .get(`/v1/tasks/${taskId}/trace`)
436
+ .set('X-API-Key', 'test-key');
437
+ expect(res.status).toBe(200);
438
+ expect(res.body.task_id).toBe(taskId);
439
+ expect(res.body.events).toBeInstanceOf(Array);
440
+ expect(res.body.events.length).toBeGreaterThan(0);
441
+ // Should include at least TOOL_CALL_RECEIVED and POLICY_DECIDED
442
+ const eventTypes = res.body.events.map((e) => e.event_type);
443
+ expect(eventTypes).toContain('TOOL_CALL_RECEIVED');
444
+ expect(eventTypes).toContain('POLICY_DECIDED');
445
+ });
446
+ it('should return empty events for unknown task', async () => {
447
+ const res = await (0, supertest_1.default)(app)
448
+ .get('/v1/tasks/nonexistent-task/trace')
449
+ .set('X-API-Key', 'test-key');
450
+ expect(res.status).toBe(200);
451
+ expect(res.body.events).toEqual([]);
452
+ });
453
+ });
454
+ // -------------------------------------------------------------------------
455
+ // GET /v1/policies/current
456
+ // -------------------------------------------------------------------------
457
+ describe('GET /v1/policies/current', () => {
458
+ it('should return the current policy pack', async () => {
459
+ const res = await (0, supertest_1.default)(app)
460
+ .get('/v1/policies/current')
461
+ .set('X-API-Key', 'test-key');
462
+ expect(res.status).toBe(200);
463
+ expect(res.body.name).toBe('dev_fast');
464
+ expect(res.body.version).toBe('1.0.0');
465
+ expect(res.body.rules).toBeInstanceOf(Array);
466
+ expect(res.body.rules.length).toBeGreaterThan(0);
467
+ });
468
+ });
469
+ // -------------------------------------------------------------------------
470
+ // POST /v1/policies/validate
471
+ // -------------------------------------------------------------------------
472
+ describe('POST /v1/policies/validate', () => {
473
+ it('should validate a valid policy pack', async () => {
474
+ const res = await (0, supertest_1.default)(app)
475
+ .post('/v1/policies/validate')
476
+ .set('X-API-Key', 'test-key')
477
+ .send({
478
+ name: 'test-pack',
479
+ version: '1.0.0',
480
+ rules: [
481
+ { name: 'allow-all', effect: 'ALLOW', conditions: {} },
482
+ ],
483
+ });
484
+ expect(res.status).toBe(200);
485
+ expect(res.body.valid).toBe(true);
486
+ expect(res.body.errors).toHaveLength(0);
487
+ });
488
+ it('should reject an invalid policy pack', async () => {
489
+ const res = await (0, supertest_1.default)(app)
490
+ .post('/v1/policies/validate')
491
+ .set('X-API-Key', 'test-key')
492
+ .send({
493
+ name: '',
494
+ rules: 'not-an-array',
495
+ });
496
+ expect(res.status).toBe(200);
497
+ expect(res.body.valid).toBe(false);
498
+ expect(res.body.errors.length).toBeGreaterThan(0);
499
+ });
500
+ });
501
+ // -------------------------------------------------------------------------
502
+ // GET /v1/approvals/pending
503
+ // -------------------------------------------------------------------------
504
+ describe('GET /v1/approvals/pending', () => {
505
+ it('should list pending approvals after triggering them', async () => {
506
+ // Trigger an approval
507
+ await (0, supertest_1.default)(app)
508
+ .post('/v1/tool/execute')
509
+ .set('X-API-Key', 'test-key')
510
+ .send(buildToolCall({
511
+ tool: { name: 'http.request', capability: 'delete' },
512
+ args: { method: 'DELETE', url: `http://127.0.0.1:${testServerPort}/api/data` },
513
+ }));
514
+ const res = await (0, supertest_1.default)(app)
515
+ .get('/v1/approvals/pending')
516
+ .set('X-API-Key', 'test-key');
517
+ expect(res.status).toBe(200);
518
+ expect(res.body.approvals).toBeInstanceOf(Array);
519
+ expect(res.body.approvals.length).toBeGreaterThan(0);
520
+ expect(res.body.approvals[0]).toHaveProperty('approval_id');
521
+ expect(res.body.approvals[0]).toHaveProperty('tool_name');
522
+ expect(res.body.approvals[0].status).toBe('pending');
523
+ });
524
+ it('should filter by workspace_id', async () => {
525
+ const res = await (0, supertest_1.default)(app)
526
+ .get('/v1/approvals/pending?workspace_id=nonexistent')
527
+ .set('X-API-Key', 'test-key');
528
+ expect(res.status).toBe(200);
529
+ expect(res.body.approvals).toEqual([]);
530
+ });
531
+ });
532
+ // -------------------------------------------------------------------------
533
+ // Full Pipeline
534
+ // -------------------------------------------------------------------------
535
+ describe('Full Pipeline', () => {
536
+ it('should process a complete read request end-to-end', async () => {
537
+ const taskId = `task-e2e-${Date.now()}`;
538
+ const res = await (0, supertest_1.default)(app)
539
+ .post('/v1/tool/execute')
540
+ .set('X-API-Key', 'test-key')
541
+ .send(buildToolCall({ task_id: taskId }));
542
+ // Verify response structure
543
+ expect(res.status).toBe(200);
544
+ expect(res.body.status).toBe('ok');
545
+ // Policy
546
+ expect(res.body.policy.decision).toBe('allow');
547
+ // Budget
548
+ expect(res.body.budget.estimated_cost_usd).toBeGreaterThan(0);
549
+ expect(res.body.budget.remaining_cost_usd_task).toBeGreaterThan(0);
550
+ // Timing
551
+ expect(res.body.timing.started_at).toBeDefined();
552
+ expect(res.body.timing.duration_ms).toBeGreaterThanOrEqual(0);
553
+ // Output
554
+ expect(res.body.output).toBeDefined();
555
+ expect(res.body.output.http_status).toBe(200);
556
+ // Verify audit trace was created
557
+ const traceRes = await (0, supertest_1.default)(app)
558
+ .get(`/v1/tasks/${taskId}/trace`)
559
+ .set('X-API-Key', 'test-key');
560
+ expect(traceRes.body.events.length).toBeGreaterThanOrEqual(4);
561
+ const types = traceRes.body.events.map((e) => e.event_type);
562
+ expect(types).toContain('TOOL_CALL_RECEIVED');
563
+ expect(types).toContain('POLICY_DECIDED');
564
+ expect(types).toContain('TOOL_EXECUTED');
565
+ expect(types).toContain('TOOL_RESULT_RETURNED');
566
+ });
567
+ it('should handle a write + approve + verify trace flow', async () => {
568
+ const taskId = `task-approve-flow-${Date.now()}`;
569
+ // Step 1: Execute a delete operation (requires approval)
570
+ const execRes = await (0, supertest_1.default)(app)
571
+ .post('/v1/tool/execute')
572
+ .set('X-API-Key', 'test-key')
573
+ .send(buildToolCall({
574
+ task_id: taskId,
575
+ tool: { name: 'http.request', capability: 'delete' },
576
+ args: { method: 'DELETE', url: `http://127.0.0.1:${testServerPort}/api/data` },
577
+ }));
578
+ expect(execRes.body.status).toBe('needs_approval');
579
+ // Step 2: Approve (using a different key to avoid self-approval rejection)
580
+ const token = execRes.body.approval.token;
581
+ const approveRes = await (0, supertest_1.default)(app)
582
+ .post('/v1/tool/approve')
583
+ .set('X-API-Key', 'approver-key')
584
+ .send({ approval_token: token, approved: true, approver_id: 'admin' });
585
+ expect(approveRes.body.success).toBe(true);
586
+ // Step 3: Verify trace includes approval events
587
+ const traceRes = await (0, supertest_1.default)(app)
588
+ .get(`/v1/tasks/${taskId}/trace`)
589
+ .set('X-API-Key', 'test-key');
590
+ const types = traceRes.body.events.map((e) => e.event_type);
591
+ expect(types).toContain('TOOL_CALL_RECEIVED');
592
+ expect(types).toContain('POLICY_DECIDED');
593
+ expect(types).toContain('APPROVAL_REQUESTED');
594
+ });
595
+ });
596
+ // -------------------------------------------------------------------------
597
+ // Auth disabled mode
598
+ // -------------------------------------------------------------------------
599
+ describe('Auth disabled mode', () => {
600
+ it('should work without API key when auth is disabled', async () => {
601
+ const config = createTestConfig({
602
+ auth: { enabled: false, api_keys: {} },
603
+ });
604
+ const { app: noAuthApp, gateway: noAuthGateway } = (0, app_1.createApp)(config);
605
+ noAuthGateway.getHttpExecutor().ssrfProtectionEnabled = false;
606
+ const res = await (0, supertest_1.default)(noAuthApp)
607
+ .post('/v1/tool/execute')
608
+ .send(buildToolCall());
609
+ expect(res.status).toBe(200);
610
+ expect(res.body.status).toBe('ok');
611
+ noAuthGateway.shutdown();
612
+ });
613
+ });
614
+ // -------------------------------------------------------------------------
615
+ // RBAC Enforcement through HTTP
616
+ // -------------------------------------------------------------------------
617
+ describe('RBAC enforcement through HTTP', () => {
618
+ let rbacApp;
619
+ let rbacGateway;
620
+ beforeEach(() => {
621
+ const config = createTestConfig({
622
+ auth: {
623
+ enabled: true,
624
+ api_keys: {
625
+ 'admin-key': {
626
+ workspace_id: 'ws_test',
627
+ description: 'Admin key',
628
+ roles: ['admin'],
629
+ },
630
+ 'readonly-key': {
631
+ workspace_id: 'ws_test',
632
+ description: 'Readonly key',
633
+ roles: ['readonly'],
634
+ },
635
+ 'agent-key': {
636
+ workspace_id: 'ws_test',
637
+ description: 'Agent key',
638
+ roles: ['agent'],
639
+ },
640
+ },
641
+ rbac: {
642
+ enabled: true,
643
+ roles: {
644
+ admin: {
645
+ permissions: ['admin:full'],
646
+ },
647
+ readonly: {
648
+ permissions: ['policy:read', 'trace:read'],
649
+ },
650
+ agent: {
651
+ permissions: ['tool:execute', 'trace:read'],
652
+ },
653
+ },
654
+ default_role: 'readonly',
655
+ },
656
+ },
657
+ });
658
+ const result = (0, app_1.createApp)(config);
659
+ rbacApp = result.app;
660
+ rbacGateway = result.gateway;
661
+ rbacGateway.getHttpExecutor().ssrfProtectionEnabled = false;
662
+ });
663
+ afterEach(() => {
664
+ rbacGateway.shutdown();
665
+ });
666
+ it('should allow admin key to read policies', async () => {
667
+ const res = await (0, supertest_1.default)(rbacApp)
668
+ .get('/v1/policies/current')
669
+ .set('X-API-Key', 'admin-key');
670
+ expect(res.status).toBe(200);
671
+ expect(res.body.name).toBe('dev_fast');
672
+ });
673
+ it('should allow admin key to execute tools', async () => {
674
+ const res = await (0, supertest_1.default)(rbacApp)
675
+ .post('/v1/tool/execute')
676
+ .set('X-API-Key', 'admin-key')
677
+ .send(buildToolCall());
678
+ expect(res.status).toBe(200);
679
+ expect(res.body.status).toBe('ok');
680
+ });
681
+ it('should allow admin key to validate policies', async () => {
682
+ const res = await (0, supertest_1.default)(rbacApp)
683
+ .post('/v1/policies/validate')
684
+ .set('X-API-Key', 'admin-key')
685
+ .send({
686
+ name: 'test-pack',
687
+ version: '1.0.0',
688
+ rules: [{ name: 'allow-all', effect: 'ALLOW', conditions: {} }],
689
+ });
690
+ expect(res.status).toBe(200);
691
+ expect(res.body.valid).toBe(true);
692
+ });
693
+ it('should deny readonly key from executing tools', async () => {
694
+ const res = await (0, supertest_1.default)(rbacApp)
695
+ .post('/v1/tool/execute')
696
+ .set('X-API-Key', 'readonly-key')
697
+ .send(buildToolCall());
698
+ expect(res.status).toBe(403);
699
+ expect(res.body.error).toContain('Insufficient permissions');
700
+ expect(res.body.error).toContain('tool:execute');
701
+ });
702
+ it('should allow readonly key to read policies', async () => {
703
+ const res = await (0, supertest_1.default)(rbacApp)
704
+ .get('/v1/policies/current')
705
+ .set('X-API-Key', 'readonly-key');
706
+ expect(res.status).toBe(200);
707
+ expect(res.body.name).toBe('dev_fast');
708
+ });
709
+ it('should deny readonly key from managing approvals', async () => {
710
+ const res = await (0, supertest_1.default)(rbacApp)
711
+ .get('/v1/approvals/pending')
712
+ .set('X-API-Key', 'readonly-key');
713
+ expect(res.status).toBe(403);
714
+ expect(res.body.error).toContain('Insufficient permissions');
715
+ });
716
+ it('should allow agent key to execute tools', async () => {
717
+ const res = await (0, supertest_1.default)(rbacApp)
718
+ .post('/v1/tool/execute')
719
+ .set('X-API-Key', 'agent-key')
720
+ .send(buildToolCall());
721
+ expect(res.status).toBe(200);
722
+ expect(res.body.status).toBe('ok');
723
+ });
724
+ it('should deny agent key from writing policies', async () => {
725
+ const res = await (0, supertest_1.default)(rbacApp)
726
+ .post('/v1/policies/validate')
727
+ .set('X-API-Key', 'agent-key')
728
+ .send({
729
+ name: 'test-pack',
730
+ version: '1.0.0',
731
+ rules: [{ name: 'allow-all', effect: 'ALLOW', conditions: {} }],
732
+ });
733
+ expect(res.status).toBe(403);
734
+ expect(res.body.error).toContain('Insufficient permissions');
735
+ expect(res.body.error).toContain('policy:write');
736
+ });
737
+ it('should allow agent key to read traces', async () => {
738
+ const res = await (0, supertest_1.default)(rbacApp)
739
+ .get('/v1/tasks/some-task/trace')
740
+ .set('X-API-Key', 'agent-key');
741
+ expect(res.status).toBe(200);
742
+ expect(res.body.task_id).toBe('some-task');
743
+ });
744
+ });
745
+ // -------------------------------------------------------------------------
746
+ // OPA Fallback Chain
747
+ // -------------------------------------------------------------------------
748
+ describe('OPA fallback chain', () => {
749
+ let opaApp;
750
+ let opaGateway;
751
+ beforeEach(() => {
752
+ const config = createTestConfig({
753
+ policy: {
754
+ pack_path: './policy-packs/dev_fast.yaml',
755
+ default_effect: 'DENY',
756
+ hot_reload: false,
757
+ opa: {
758
+ enabled: true,
759
+ server_url: 'http://127.0.0.1:19999', // unreachable port
760
+ policy_path: 'v1/data/palaryn/policy',
761
+ timeout_ms: 500,
762
+ fallback_decision: 'deny',
763
+ },
764
+ },
765
+ });
766
+ const result = (0, app_1.createApp)(config);
767
+ opaApp = result.app;
768
+ opaGateway = result.gateway;
769
+ opaGateway.getHttpExecutor().ssrfProtectionEnabled = false;
770
+ });
771
+ afterEach(() => {
772
+ opaGateway.shutdown();
773
+ });
774
+ it('should fall back to YAML policy when OPA is unreachable and allow read operations', async () => {
775
+ const res = await (0, supertest_1.default)(opaApp)
776
+ .post('/v1/tool/execute')
777
+ .set('X-API-Key', 'test-key')
778
+ .send(buildToolCall());
779
+ // dev_fast.yaml allows read operations
780
+ expect(res.status).toBe(200);
781
+ expect(res.body.status).toBe('ok');
782
+ expect(res.body.policy.decision).toBe('allow');
783
+ });
784
+ it('should fall back to YAML policy when OPA is unreachable and allow write operations', async () => {
785
+ const res = await (0, supertest_1.default)(opaApp)
786
+ .post('/v1/tool/execute')
787
+ .set('X-API-Key', 'test-key')
788
+ .send(buildToolCall({
789
+ tool: { name: 'http.request', capability: 'write' },
790
+ args: {
791
+ method: 'POST',
792
+ url: `http://127.0.0.1:${testServerPort}/api/create`,
793
+ body: { key: 'value' },
794
+ },
795
+ }));
796
+ // dev_fast.yaml allows write operations
797
+ expect(res.status).toBe(200);
798
+ expect(res.body.status).toBe('ok');
799
+ expect(res.body.policy.decision).toBe('allow');
800
+ });
801
+ it('should fall back to YAML policy when OPA is unreachable and require approval for delete', async () => {
802
+ const res = await (0, supertest_1.default)(opaApp)
803
+ .post('/v1/tool/execute')
804
+ .set('X-API-Key', 'test-key')
805
+ .send(buildToolCall({
806
+ tool: { name: 'http.request', capability: 'delete' },
807
+ args: { method: 'DELETE', url: `http://127.0.0.1:${testServerPort}/api/data` },
808
+ }));
809
+ // dev_fast.yaml requires approval for delete operations
810
+ expect(res.status).toBe(202);
811
+ expect(res.body.status).toBe('needs_approval');
812
+ expect(res.body.approval).toBeDefined();
813
+ expect(res.body.approval.token).toBeDefined();
814
+ });
815
+ });
816
+ // -------------------------------------------------------------------------
817
+ // Anomaly Blocking
818
+ // -------------------------------------------------------------------------
819
+ describe('Anomaly blocking', () => {
820
+ let anomalyApp;
821
+ let anomalyGateway;
822
+ beforeEach(() => {
823
+ const config = createTestConfig({
824
+ anomaly: {
825
+ enabled: true,
826
+ action: 'block',
827
+ window_ms: 60000,
828
+ z_score_threshold: 3,
829
+ min_samples: 5,
830
+ },
831
+ });
832
+ const result = (0, app_1.createApp)(config);
833
+ anomalyApp = result.app;
834
+ anomalyGateway = result.gateway;
835
+ anomalyGateway.getHttpExecutor().ssrfProtectionEnabled = false;
836
+ });
837
+ afterEach(() => {
838
+ anomalyGateway.shutdown();
839
+ });
840
+ it('should block when capability escalation to admin is detected', async () => {
841
+ const actorId = `anomaly-actor-${Date.now()}`;
842
+ // First request with 'read' capability - establishes baseline capability
843
+ const res1 = await (0, supertest_1.default)(anomalyApp)
844
+ .post('/v1/tool/execute')
845
+ .set('X-API-Key', 'test-key')
846
+ .send(buildToolCall({
847
+ actor: { type: 'agent', id: actorId, display: 'Anomaly Test Agent' },
848
+ tool: { name: 'http.request', capability: 'read' },
849
+ }));
850
+ expect(res1.status).toBe(200);
851
+ expect(res1.body.status).toBe('ok');
852
+ // Second request with 'admin' capability - triggers capability escalation
853
+ // with high severity (read -> admin is a jump from level 0 to level 3)
854
+ const res2 = await (0, supertest_1.default)(anomalyApp)
855
+ .post('/v1/tool/execute')
856
+ .set('X-API-Key', 'test-key')
857
+ .send(buildToolCall({
858
+ actor: { type: 'agent', id: actorId, display: 'Anomaly Test Agent' },
859
+ tool: { name: 'http.request', capability: 'admin' },
860
+ args: { method: 'GET', url: `http://127.0.0.1:${testServerPort}/api/data` },
861
+ }));
862
+ // Anomaly detector should block before policy eval due to high severity
863
+ expect(res2.status).toBe(403);
864
+ expect(res2.body.status).toBe('blocked');
865
+ expect(res2.body.policy.rule_id).toBe('anomaly_detection');
866
+ expect(res2.body.policy.reasons).toEqual(expect.arrayContaining([expect.stringContaining('capability_escalation')]));
867
+ });
868
+ it('should not block when action is log instead of block', async () => {
869
+ // Create a separate app with action: 'log'
870
+ const logConfig = createTestConfig({
871
+ anomaly: {
872
+ enabled: true,
873
+ action: 'log',
874
+ window_ms: 60000,
875
+ z_score_threshold: 3,
876
+ min_samples: 5,
877
+ },
878
+ });
879
+ const { app: logApp, gateway: logGateway } = (0, app_1.createApp)(logConfig);
880
+ logGateway.getHttpExecutor().ssrfProtectionEnabled = false;
881
+ const actorId = `log-actor-${Date.now()}`;
882
+ // First request establishes baseline
883
+ await (0, supertest_1.default)(logApp)
884
+ .post('/v1/tool/execute')
885
+ .set('X-API-Key', 'test-key')
886
+ .send(buildToolCall({
887
+ actor: { type: 'agent', id: actorId, display: 'Log Test Agent' },
888
+ tool: { name: 'http.request', capability: 'read' },
889
+ }));
890
+ // Second request with escalation - should NOT block since action is 'log'
891
+ // (admin capability still requires approval per dev_fast.yaml policy)
892
+ const res2 = await (0, supertest_1.default)(logApp)
893
+ .post('/v1/tool/execute')
894
+ .set('X-API-Key', 'test-key')
895
+ .send(buildToolCall({
896
+ actor: { type: 'agent', id: actorId, display: 'Log Test Agent' },
897
+ tool: { name: 'http.request', capability: 'admin' },
898
+ args: { method: 'GET', url: `http://127.0.0.1:${testServerPort}/api/data` },
899
+ }));
900
+ // Should reach policy engine (not blocked by anomaly), but policy requires approval for admin
901
+ expect(res2.status).toBe(202);
902
+ expect(res2.body.status).toBe('needs_approval');
903
+ logGateway.shutdown();
904
+ });
905
+ it('should allow first request from an actor without anomaly blocking', async () => {
906
+ const actorId = `fresh-actor-${Date.now()}`;
907
+ const res = await (0, supertest_1.default)(anomalyApp)
908
+ .post('/v1/tool/execute')
909
+ .set('X-API-Key', 'test-key')
910
+ .send(buildToolCall({
911
+ actor: { type: 'agent', id: actorId, display: 'Fresh Agent' },
912
+ tool: { name: 'http.request', capability: 'read' },
913
+ }));
914
+ // First request should not trigger any anomaly
915
+ expect(res.status).toBe(200);
916
+ expect(res.body.status).toBe('ok');
917
+ });
918
+ });
919
+ // -------------------------------------------------------------------------
920
+ // Admin Form Submissions
921
+ // -------------------------------------------------------------------------
922
+ describe('Admin form submissions', () => {
923
+ it('should return HTML for GET /admin', async () => {
924
+ const res = await (0, supertest_1.default)(app).get('/admin').set('X-API-Key', 'test-key');
925
+ expect(res.status).toBe(200);
926
+ expect(res.headers['content-type']).toMatch(/text\/html/);
927
+ expect(res.text).toContain('Palaryn');
928
+ });
929
+ it('should return HTML for GET /admin/approvals', async () => {
930
+ const res = await (0, supertest_1.default)(app).get('/admin/approvals').set('X-API-Key', 'test-key');
931
+ expect(res.status).toBe(200);
932
+ expect(res.headers['content-type']).toMatch(/text\/html/);
933
+ });
934
+ it('should redirect on POST /admin/approvals/:id/approve for valid approval', async () => {
935
+ // First create a pending approval via the API
936
+ const execRes = await (0, supertest_1.default)(app)
937
+ .post('/v1/tool/execute')
938
+ .set('X-API-Key', 'test-key')
939
+ .send(buildToolCall({
940
+ tool: { name: 'http.request', capability: 'delete' },
941
+ args: { method: 'DELETE', url: `http://127.0.0.1:${testServerPort}/api/data` },
942
+ }));
943
+ expect(execRes.body.status).toBe('needs_approval');
944
+ const approvalId = execRes.body.approval.approval_id;
945
+ // GET the approvals page to obtain a CSRF token
946
+ const getRes = await (0, supertest_1.default)(app).get('/admin/approvals').set('X-API-Key', 'test-key');
947
+ const csrfToken = extractCSRFToken(getRes.text);
948
+ // POST to approve via admin form
949
+ const res = await (0, supertest_1.default)(app)
950
+ .post(`/admin/approvals/${approvalId}/approve`)
951
+ .set('X-API-Key', 'test-key')
952
+ .type('form')
953
+ .send({ _csrf: csrfToken });
954
+ expect(res.status).toBe(302);
955
+ expect(res.headers['location']).toBe('/admin/approvals');
956
+ });
957
+ it('should redirect on POST /admin/approvals/:id/deny for valid approval', async () => {
958
+ // Create a pending approval
959
+ const execRes = await (0, supertest_1.default)(app)
960
+ .post('/v1/tool/execute')
961
+ .set('X-API-Key', 'test-key')
962
+ .send(buildToolCall({
963
+ tool: { name: 'http.request', capability: 'delete' },
964
+ args: { method: 'DELETE', url: `http://127.0.0.1:${testServerPort}/api/data` },
965
+ }));
966
+ expect(execRes.body.status).toBe('needs_approval');
967
+ const approvalId = execRes.body.approval.approval_id;
968
+ // GET the approvals page to obtain a CSRF token
969
+ const getRes = await (0, supertest_1.default)(app).get('/admin/approvals').set('X-API-Key', 'test-key');
970
+ const csrfToken = extractCSRFToken(getRes.text);
971
+ const res = await (0, supertest_1.default)(app)
972
+ .post(`/admin/approvals/${approvalId}/deny`)
973
+ .set('X-API-Key', 'test-key')
974
+ .type('form')
975
+ .send({ _csrf: csrfToken });
976
+ expect(res.status).toBe(302);
977
+ expect(res.headers['location']).toBe('/admin/approvals');
978
+ });
979
+ it('should redirect on POST /admin/approvals/:id/approve for nonexistent approval', async () => {
980
+ // Create a real approval so the page renders forms with CSRF tokens
981
+ await (0, supertest_1.default)(app)
982
+ .post('/v1/tool/execute')
983
+ .set('X-API-Key', 'test-key')
984
+ .send(buildToolCall({
985
+ tool: { name: 'http.request', capability: 'delete' },
986
+ args: { method: 'DELETE', url: `http://127.0.0.1:${testServerPort}/api/data` },
987
+ }));
988
+ const getRes = await (0, supertest_1.default)(app).get('/admin/approvals').set('X-API-Key', 'test-key');
989
+ const csrfToken = extractCSRFToken(getRes.text);
990
+ const res = await (0, supertest_1.default)(app)
991
+ .post('/admin/approvals/nonexistent-id/approve')
992
+ .set('X-API-Key', 'test-key')
993
+ .type('form')
994
+ .send({ _csrf: csrfToken });
995
+ // Still redirects even if approval not found (graceful handling)
996
+ expect(res.status).toBe(302);
997
+ expect(res.headers['location']).toBe('/admin/approvals');
998
+ });
999
+ it('should return validation result on POST /admin/policies/validate with valid policy', async () => {
1000
+ const getRes = await (0, supertest_1.default)(app).get('/admin/policies').set('X-API-Key', 'test-key');
1001
+ const csrfToken = extractCSRFToken(getRes.text);
1002
+ const validPolicy = JSON.stringify({
1003
+ name: 'test-pack',
1004
+ version: '1.0.0',
1005
+ rules: [{ name: 'allow-all', effect: 'ALLOW', conditions: {} }],
1006
+ });
1007
+ const res = await (0, supertest_1.default)(app)
1008
+ .post('/admin/policies/validate')
1009
+ .set('X-API-Key', 'test-key')
1010
+ .type('form')
1011
+ .send({ _csrf: csrfToken, policy_json: validPolicy });
1012
+ expect(res.status).toBe(200);
1013
+ expect(res.headers['content-type']).toMatch(/text\/html/);
1014
+ // The response HTML should indicate the policy is valid
1015
+ expect(res.text).toMatch(/valid/i);
1016
+ });
1017
+ it('should return validation errors on POST /admin/policies/validate with invalid JSON', async () => {
1018
+ const getRes = await (0, supertest_1.default)(app).get('/admin/policies').set('X-API-Key', 'test-key');
1019
+ const csrfToken = extractCSRFToken(getRes.text);
1020
+ const res = await (0, supertest_1.default)(app)
1021
+ .post('/admin/policies/validate')
1022
+ .set('X-API-Key', 'test-key')
1023
+ .type('form')
1024
+ .send({ _csrf: csrfToken, policy_json: 'not valid json{{{' });
1025
+ expect(res.status).toBe(200);
1026
+ expect(res.headers['content-type']).toMatch(/text\/html/);
1027
+ // The response should contain an error indication
1028
+ expect(res.text).toMatch(/error|invalid/i);
1029
+ });
1030
+ it('should create an API key on POST /admin/api-keys and redirect', async () => {
1031
+ const getRes = await (0, supertest_1.default)(app).get('/admin/api-keys').set('X-API-Key', 'test-key');
1032
+ const csrfToken = extractCSRFToken(getRes.text);
1033
+ const res = await (0, supertest_1.default)(app)
1034
+ .post('/admin/api-keys')
1035
+ .set('X-API-Key', 'test-key')
1036
+ .type('form')
1037
+ .send({ _csrf: csrfToken, workspace_id: 'ws_new', description: 'Integration test key' });
1038
+ expect(res.status).toBe(302);
1039
+ expect(res.headers['location']).toMatch(/^\/admin\/api-keys\?created=key-/);
1040
+ });
1041
+ it('should list API keys on GET /admin/api-keys including the default test key', async () => {
1042
+ const res = await (0, supertest_1.default)(app).get('/admin/api-keys').set('X-API-Key', 'test-key');
1043
+ expect(res.status).toBe(200);
1044
+ expect(res.headers['content-type']).toMatch(/text\/html/);
1045
+ expect(res.text).toContain('test-key');
1046
+ });
1047
+ it('should return HTML for GET /admin/policies', async () => {
1048
+ const res = await (0, supertest_1.default)(app).get('/admin/policies').set('X-API-Key', 'test-key');
1049
+ expect(res.status).toBe(200);
1050
+ expect(res.headers['content-type']).toMatch(/text\/html/);
1051
+ expect(res.text).toContain('dev_fast');
1052
+ });
1053
+ it('should return HTML for GET /admin/budgets', async () => {
1054
+ const res = await (0, supertest_1.default)(app).get('/admin/budgets').set('X-API-Key', 'test-key');
1055
+ expect(res.status).toBe(200);
1056
+ expect(res.headers['content-type']).toMatch(/text\/html/);
1057
+ });
1058
+ it('should return HTML for GET /admin/traces', async () => {
1059
+ const res = await (0, supertest_1.default)(app).get('/admin/traces').set('X-API-Key', 'test-key');
1060
+ expect(res.status).toBe(200);
1061
+ expect(res.headers['content-type']).toMatch(/text\/html/);
1062
+ });
1063
+ });
1064
+ // -------------------------------------------------------------------------
1065
+ // Public Endpoint Rate Limiting
1066
+ // -------------------------------------------------------------------------
1067
+ describe('Public endpoint rate limiting', () => {
1068
+ let rateLimitedApp;
1069
+ let rateLimitedGateway;
1070
+ beforeEach(() => {
1071
+ const config = createTestConfig({
1072
+ public_rate_limit: {
1073
+ max_per_window: 3,
1074
+ window_ms: 60000,
1075
+ },
1076
+ });
1077
+ const result = (0, app_1.createApp)(config);
1078
+ rateLimitedApp = result.app;
1079
+ rateLimitedGateway = result.gateway;
1080
+ rateLimitedGateway.getHttpExecutor().ssrfProtectionEnabled = false;
1081
+ });
1082
+ afterEach(() => {
1083
+ rateLimitedGateway.shutdown();
1084
+ });
1085
+ it('should return 429 on /health after exceeding the public rate limit', async () => {
1086
+ // Send requests up to the limit (3)
1087
+ for (let i = 0; i < 3; i++) {
1088
+ const res = await (0, supertest_1.default)(rateLimitedApp).get('/health');
1089
+ expect(res.status).toBe(200);
1090
+ }
1091
+ // The 4th request should be rate limited
1092
+ const res = await (0, supertest_1.default)(rateLimitedApp).get('/health');
1093
+ expect(res.status).toBe(429);
1094
+ expect(res.body.error).toBe('Too many requests');
1095
+ expect(res.body.error_code).toBe('RATE_LIMIT_EXCEEDED');
1096
+ expect(res.body.details.retry_after_ms).toBeDefined();
1097
+ expect(res.body.details.retry_after_ms).toBeGreaterThan(0);
1098
+ });
1099
+ it('should return 429 on /metrics after exceeding the public rate limit', async () => {
1100
+ // Send requests up to the limit (3)
1101
+ for (let i = 0; i < 3; i++) {
1102
+ const res = await (0, supertest_1.default)(rateLimitedApp).get('/metrics');
1103
+ expect(res.status).toBe(200);
1104
+ }
1105
+ // The 4th request should be rate limited
1106
+ const res = await (0, supertest_1.default)(rateLimitedApp).get('/metrics');
1107
+ expect(res.status).toBe(429);
1108
+ expect(res.body.error).toBe('Too many requests');
1109
+ expect(res.body.details.retry_after_ms).toBeDefined();
1110
+ });
1111
+ it('should share the rate limit window across /health and /metrics for the same IP', async () => {
1112
+ // Use 2 requests on /health
1113
+ await (0, supertest_1.default)(rateLimitedApp).get('/health');
1114
+ await (0, supertest_1.default)(rateLimitedApp).get('/health');
1115
+ // Use 1 request on /metrics (3rd total)
1116
+ const metricsRes = await (0, supertest_1.default)(rateLimitedApp).get('/metrics');
1117
+ expect(metricsRes.status).toBe(200);
1118
+ // 4th request on either endpoint should be blocked
1119
+ const blockedHealth = await (0, supertest_1.default)(rateLimitedApp).get('/health');
1120
+ expect(blockedHealth.status).toBe(429);
1121
+ const blockedMetrics = await (0, supertest_1.default)(rateLimitedApp).get('/metrics');
1122
+ expect(blockedMetrics.status).toBe(429);
1123
+ });
1124
+ });
1125
+ // -------------------------------------------------------------------------
1126
+ // S3: Error messages never leak internal details
1127
+ // -------------------------------------------------------------------------
1128
+ describe('S3: Error message redaction', () => {
1129
+ it('should return generic error on tool-execute failure, not raw error message', async () => {
1130
+ // Send a tool call targeting a host that will fail (connection refused)
1131
+ const res = await (0, supertest_1.default)(app)
1132
+ .post('/v1/tool/execute')
1133
+ .set('X-API-Key', 'test-key')
1134
+ .send(buildToolCall({
1135
+ tool_call_id: `tc-s3-${Date.now()}`,
1136
+ args: { method: 'GET', url: 'http://127.0.0.1:1/will-fail' },
1137
+ }));
1138
+ // The response should not contain raw error messages like "ECONNREFUSED"
1139
+ // or stack traces. The gateway returns a ToolResult with a sanitized error field.
1140
+ const body = JSON.stringify(res.body);
1141
+ expect(body).not.toContain('ECONNREFUSED');
1142
+ expect(body).not.toContain('stack');
1143
+ expect(body).not.toContain('node_modules');
1144
+ // The error field should be a generic message, not the raw error
1145
+ expect(res.body.error).toBe('Tool execution failed');
1146
+ });
1147
+ it('should return generic error on approve endpoint failure', async () => {
1148
+ // Send an invalid approval token
1149
+ const res = await (0, supertest_1.default)(app)
1150
+ .post('/v1/tool/approve')
1151
+ .set('X-API-Key', 'test-key')
1152
+ .send({ token: 'invalid-jwt-token', approved: true, approver_id: 'admin' });
1153
+ // Should not leak JWT verification internals
1154
+ const body = JSON.stringify(res.body);
1155
+ expect(body).not.toContain('JsonWebTokenError');
1156
+ expect(body).not.toContain('jwt malformed');
1157
+ expect(body).not.toContain('stack');
1158
+ });
1159
+ });
1160
+ // -------------------------------------------------------------------------
1161
+ // A4: Health check returns 503 when unhealthy
1162
+ // -------------------------------------------------------------------------
1163
+ describe('A4: Health check HTTP status', () => {
1164
+ it('should return 200 when all health checks are ok', async () => {
1165
+ const res = await (0, supertest_1.default)(app).get('/health');
1166
+ expect(res.status).toBe(200);
1167
+ expect(res.body.status).toBe('ok');
1168
+ });
1169
+ it('should return 503 when a health check reports unhealthy', async () => {
1170
+ // Inject an unhealthy check into the shared healthChecks array
1171
+ healthChecks.push({
1172
+ name: 'test-db',
1173
+ check: async () => ({ status: 'unhealthy', message: 'connection refused' }),
1174
+ });
1175
+ const res = await (0, supertest_1.default)(app).get('/health');
1176
+ expect(res.status).toBe(503);
1177
+ expect(res.body.status).toBe('unhealthy');
1178
+ });
1179
+ it('should return 200 when health checks are degraded but not unhealthy', async () => {
1180
+ healthChecks.push({
1181
+ name: 'test-cache',
1182
+ check: async () => ({ status: 'degraded', message: 'slow response' }),
1183
+ });
1184
+ const res = await (0, supertest_1.default)(app).get('/health');
1185
+ expect(res.status).toBe(200);
1186
+ expect(res.body.status).toBe('degraded');
1187
+ });
1188
+ it('should return 503 when a health check throws an exception', async () => {
1189
+ healthChecks.push({
1190
+ name: 'test-failing',
1191
+ check: async () => { throw new Error('connection timeout'); },
1192
+ });
1193
+ const res = await (0, supertest_1.default)(app).get('/health');
1194
+ expect(res.status).toBe(503);
1195
+ expect(res.body.status).toBe('unhealthy');
1196
+ });
1197
+ });
1198
+ });
1199
+ //# sourceMappingURL=api.test.js.map