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,1345 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const fs = __importStar(require("fs"));
37
+ const path = __importStar(require("path"));
38
+ const os = __importStar(require("os"));
39
+ const engine_1 = require("../../src/policy/engine");
40
+ // ---------------------------------------------------------------------------
41
+ // Helpers
42
+ // ---------------------------------------------------------------------------
43
+ /** Create a temporary directory that is cleaned up automatically. */
44
+ let tmpDir;
45
+ beforeAll(() => {
46
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'policy-engine-test-'));
47
+ });
48
+ afterAll(() => {
49
+ fs.rmSync(tmpDir, { recursive: true, force: true });
50
+ });
51
+ /** Write a YAML string to a temporary file and return its path. */
52
+ function writeTmpYaml(filename, content) {
53
+ const filePath = path.join(tmpDir, filename);
54
+ fs.writeFileSync(filePath, content, 'utf-8');
55
+ return filePath;
56
+ }
57
+ /** Build a minimal valid ToolCall for testing. */
58
+ function buildToolCall(overrides = {}) {
59
+ return {
60
+ tool_call_id: 'tc-001',
61
+ task_id: 'task-001',
62
+ workspace_id: 'ws-default',
63
+ actor: {
64
+ type: 'agent',
65
+ id: 'agent-1',
66
+ ...(overrides.actor || {}),
67
+ },
68
+ source: {
69
+ platform: 'langgraph',
70
+ ...(overrides.source || {}),
71
+ },
72
+ tool: {
73
+ name: 'http.request',
74
+ capability: 'read',
75
+ ...(overrides.tool || {}),
76
+ },
77
+ args: {
78
+ method: 'GET',
79
+ url: 'https://api.github.com/repos',
80
+ ...(overrides.args || {}),
81
+ },
82
+ context: overrides.context,
83
+ constraints: overrides.constraints,
84
+ timestamp: overrides.timestamp,
85
+ };
86
+ }
87
+ // ---------------------------------------------------------------------------
88
+ // A valid minimal policy pack YAML
89
+ // ---------------------------------------------------------------------------
90
+ const VALID_PACK_YAML = `
91
+ name: test_pack
92
+ version: "1.0.0"
93
+ description: "Test policy pack"
94
+
95
+ rules:
96
+ - name: "allow-reads"
97
+ description: "Allow read operations"
98
+ effect: ALLOW
99
+ priority: 10
100
+ conditions:
101
+ capabilities:
102
+ - "read"
103
+
104
+ - name: "deny-all"
105
+ description: "Default deny everything else"
106
+ effect: DENY
107
+ priority: 100
108
+ conditions: {}
109
+ `;
110
+ // ---------------------------------------------------------------------------
111
+ // Tests
112
+ // ---------------------------------------------------------------------------
113
+ describe('PolicyEngine', () => {
114
+ // -----------------------------------------------------------------------
115
+ // Loading & Parsing
116
+ // -----------------------------------------------------------------------
117
+ describe('loadPack (constructor)', () => {
118
+ it('should load a valid YAML policy pack from disk', () => {
119
+ const packPath = writeTmpYaml('valid.yaml', VALID_PACK_YAML);
120
+ const engine = new engine_1.PolicyEngine(packPath);
121
+ const pack = engine.getPack();
122
+ expect(pack.name).toBe('test_pack');
123
+ expect(pack.version).toBe('1.0.0');
124
+ expect(pack.rules).toHaveLength(2);
125
+ });
126
+ it('should throw when the policy pack file does not exist', () => {
127
+ const missingPath = path.join(tmpDir, 'nonexistent.yaml');
128
+ expect(() => new engine_1.PolicyEngine(missingPath)).toThrow(/Failed to read policy pack file/);
129
+ });
130
+ it('should throw on invalid YAML syntax', () => {
131
+ const badYaml = writeTmpYaml('bad-syntax.yaml', '{ invalid: yaml: [');
132
+ expect(() => new engine_1.PolicyEngine(badYaml)).toThrow(/Failed to parse YAML/);
133
+ });
134
+ it('should throw when the YAML file contains a non-object (e.g. a scalar)', () => {
135
+ const scalarYaml = writeTmpYaml('scalar.yaml', 'just a string');
136
+ expect(() => new engine_1.PolicyEngine(scalarYaml)).toThrow(/does not contain a valid YAML object/);
137
+ });
138
+ it('should throw when the YAML file is empty (null content)', () => {
139
+ const emptyYaml = writeTmpYaml('empty.yaml', '');
140
+ expect(() => new engine_1.PolicyEngine(emptyYaml)).toThrow(/does not contain a valid YAML object/);
141
+ });
142
+ it('should sort rules by priority (ascending) on load', () => {
143
+ const yaml = `
144
+ name: priority_test
145
+ version: "1.0.0"
146
+ rules:
147
+ - name: "low-priority"
148
+ effect: DENY
149
+ priority: 50
150
+ conditions: {}
151
+ - name: "high-priority"
152
+ effect: ALLOW
153
+ priority: 5
154
+ conditions: {}
155
+ - name: "medium-priority"
156
+ effect: DENY
157
+ priority: 25
158
+ conditions: {}
159
+ `;
160
+ const packPath = writeTmpYaml('priority.yaml', yaml);
161
+ const engine = new engine_1.PolicyEngine(packPath);
162
+ const pack = engine.getPack();
163
+ expect(pack.rules[0].name).toBe('high-priority');
164
+ expect(pack.rules[1].name).toBe('medium-priority');
165
+ expect(pack.rules[2].name).toBe('low-priority');
166
+ });
167
+ it('should place rules without explicit priority at the end', () => {
168
+ const yaml = `
169
+ name: no_priority_test
170
+ version: "1.0.0"
171
+ rules:
172
+ - name: "no-priority"
173
+ effect: DENY
174
+ conditions: {}
175
+ - name: "has-priority"
176
+ effect: ALLOW
177
+ priority: 10
178
+ conditions: {}
179
+ `;
180
+ const packPath = writeTmpYaml('no-priority.yaml', yaml);
181
+ const engine = new engine_1.PolicyEngine(packPath);
182
+ const pack = engine.getPack();
183
+ expect(pack.rules[0].name).toBe('has-priority');
184
+ expect(pack.rules[1].name).toBe('no-priority');
185
+ });
186
+ it('should load the real default.yaml policy pack successfully', () => {
187
+ const defaultPath = path.resolve(__dirname, '../../policy-packs/default.yaml');
188
+ const engine = new engine_1.PolicyEngine(defaultPath);
189
+ const pack = engine.getPack();
190
+ expect(pack.name).toBe('default');
191
+ expect(pack.rules.length).toBeGreaterThanOrEqual(4);
192
+ expect(pack.rules[0].name).toBe('Block metadata endpoints');
193
+ });
194
+ });
195
+ // -----------------------------------------------------------------------
196
+ // Validation
197
+ // -----------------------------------------------------------------------
198
+ describe('validate', () => {
199
+ it('should return valid for a correctly structured policy pack', () => {
200
+ const pack = {
201
+ name: 'valid_pack',
202
+ version: '1.0.0',
203
+ rules: [
204
+ {
205
+ name: 'rule1',
206
+ effect: 'ALLOW',
207
+ conditions: {},
208
+ },
209
+ ],
210
+ };
211
+ const result = engine_1.PolicyEngine.validate(pack);
212
+ expect(result.valid).toBe(true);
213
+ expect(result.errors).toHaveLength(0);
214
+ });
215
+ it('should return errors when name is missing', () => {
216
+ const pack = {
217
+ name: '',
218
+ version: '1.0.0',
219
+ rules: [{ name: 'r', effect: 'ALLOW', conditions: {} }],
220
+ };
221
+ const result = engine_1.PolicyEngine.validate(pack);
222
+ expect(result.valid).toBe(false);
223
+ expect(result.errors).toEqual(expect.arrayContaining([expect.stringMatching(/non-empty "name" string/)]));
224
+ });
225
+ it('should return errors when version is missing', () => {
226
+ const pack = {
227
+ name: 'test',
228
+ version: '',
229
+ rules: [{ name: 'r', effect: 'ALLOW', conditions: {} }],
230
+ };
231
+ const result = engine_1.PolicyEngine.validate(pack);
232
+ expect(result.valid).toBe(false);
233
+ expect(result.errors).toEqual(expect.arrayContaining([expect.stringMatching(/non-empty "version" string/)]));
234
+ });
235
+ it('should return errors when rules is not an array', () => {
236
+ const pack = {
237
+ name: 'test',
238
+ version: '1.0.0',
239
+ rules: 'not-an-array',
240
+ };
241
+ const result = engine_1.PolicyEngine.validate(pack);
242
+ expect(result.valid).toBe(false);
243
+ expect(result.errors).toEqual(expect.arrayContaining([expect.stringMatching(/"rules" array/)]));
244
+ });
245
+ it('should return errors for a rule with no name', () => {
246
+ const pack = {
247
+ name: 'test',
248
+ version: '1.0.0',
249
+ rules: [{ name: '', effect: 'ALLOW', conditions: {} }],
250
+ };
251
+ const result = engine_1.PolicyEngine.validate(pack);
252
+ expect(result.valid).toBe(false);
253
+ expect(result.errors).toEqual(expect.arrayContaining([expect.stringMatching(/Rule\[0\].*non-empty "name"/)]));
254
+ });
255
+ it('should return errors for a rule with missing effect', () => {
256
+ const pack = {
257
+ name: 'test',
258
+ version: '1.0.0',
259
+ rules: [{ name: 'r', effect: '', conditions: {} }],
260
+ };
261
+ const result = engine_1.PolicyEngine.validate(pack);
262
+ expect(result.valid).toBe(false);
263
+ expect(result.errors).toEqual(expect.arrayContaining([expect.stringMatching(/must have an "effect" string/)]));
264
+ });
265
+ it('should return errors for a rule with invalid effect value', () => {
266
+ const pack = {
267
+ name: 'test',
268
+ version: '1.0.0',
269
+ rules: [{ name: 'r', effect: 'EXECUTE', conditions: {} }],
270
+ };
271
+ const result = engine_1.PolicyEngine.validate(pack);
272
+ expect(result.valid).toBe(false);
273
+ expect(result.errors).toEqual(expect.arrayContaining([expect.stringMatching(/invalid effect "EXECUTE"/)]));
274
+ });
275
+ it('should return errors for a rule with missing conditions', () => {
276
+ const pack = {
277
+ name: 'test',
278
+ version: '1.0.0',
279
+ rules: [{ name: 'r', effect: 'ALLOW' }],
280
+ };
281
+ const result = engine_1.PolicyEngine.validate(pack);
282
+ expect(result.valid).toBe(false);
283
+ expect(result.errors).toEqual(expect.arrayContaining([expect.stringMatching(/must have a "conditions" object/)]));
284
+ });
285
+ it('should return errors when priority is not a finite number', () => {
286
+ const pack = {
287
+ name: 'test',
288
+ version: '1.0.0',
289
+ rules: [
290
+ {
291
+ name: 'r',
292
+ effect: 'ALLOW',
293
+ priority: Infinity,
294
+ conditions: {},
295
+ },
296
+ ],
297
+ };
298
+ const result = engine_1.PolicyEngine.validate(pack);
299
+ expect(result.valid).toBe(false);
300
+ expect(result.errors).toEqual(expect.arrayContaining([expect.stringMatching(/"priority" must be a finite number/)]));
301
+ });
302
+ it('should return errors for an invalid tool_match regex', () => {
303
+ const pack = {
304
+ name: 'test',
305
+ version: '1.0.0',
306
+ rules: [
307
+ {
308
+ name: 'r',
309
+ effect: 'ALLOW',
310
+ conditions: { tool_match: '[invalid(' },
311
+ },
312
+ ],
313
+ };
314
+ const result = engine_1.PolicyEngine.validate(pack);
315
+ expect(result.valid).toBe(false);
316
+ expect(result.errors).toEqual(expect.arrayContaining([expect.stringMatching(/conditions.tool_match.*not a valid regex/)]));
317
+ });
318
+ it('should return errors for an invalid label_match regex', () => {
319
+ const pack = {
320
+ name: 'test',
321
+ version: '1.0.0',
322
+ rules: [
323
+ {
324
+ name: 'r',
325
+ effect: 'ALLOW',
326
+ conditions: { label_match: '[bad regex(' },
327
+ },
328
+ ],
329
+ };
330
+ const result = engine_1.PolicyEngine.validate(pack);
331
+ expect(result.valid).toBe(false);
332
+ expect(result.errors).toEqual(expect.arrayContaining([expect.stringMatching(/conditions.label_match.*not a valid regex/)]));
333
+ });
334
+ it('should accept valid tool_match and label_match regex patterns', () => {
335
+ const pack = {
336
+ name: 'test',
337
+ version: '1.0.0',
338
+ rules: [
339
+ {
340
+ name: 'r',
341
+ effect: 'ALLOW',
342
+ conditions: {
343
+ tool_match: '^http\\.',
344
+ label_match: 'prod|staging',
345
+ },
346
+ },
347
+ ],
348
+ };
349
+ const result = engine_1.PolicyEngine.validate(pack);
350
+ expect(result.valid).toBe(true);
351
+ });
352
+ });
353
+ // -----------------------------------------------------------------------
354
+ // evaluate() -- Decision Types
355
+ // -----------------------------------------------------------------------
356
+ describe('evaluate - decisions', () => {
357
+ it('should return ALLOW decision when a matching ALLOW rule is found', () => {
358
+ const yaml = `
359
+ name: allow_test
360
+ version: "1.0.0"
361
+ rules:
362
+ - name: "allow-reads"
363
+ effect: ALLOW
364
+ priority: 1
365
+ conditions:
366
+ capabilities:
367
+ - "read"
368
+ `;
369
+ const packPath = writeTmpYaml('allow.yaml', yaml);
370
+ const engine = new engine_1.PolicyEngine(packPath);
371
+ const result = engine.evaluate(buildToolCall());
372
+ expect(result.decision).toBe('allow');
373
+ expect(result.rule_name).toBe('allow-reads');
374
+ });
375
+ it('should return DENY decision when a matching DENY rule is found', () => {
376
+ const yaml = `
377
+ name: deny_test
378
+ version: "1.0.0"
379
+ rules:
380
+ - name: "deny-writes"
381
+ effect: DENY
382
+ priority: 1
383
+ conditions:
384
+ capabilities:
385
+ - "write"
386
+ `;
387
+ const packPath = writeTmpYaml('deny.yaml', yaml);
388
+ const engine = new engine_1.PolicyEngine(packPath);
389
+ const tc = buildToolCall({ tool: { name: 'db.write', capability: 'write' } });
390
+ const result = engine.evaluate(tc);
391
+ expect(result.decision).toBe('deny');
392
+ expect(result.rule_name).toBe('deny-writes');
393
+ });
394
+ it('should return TRANSFORM decision', () => {
395
+ const yaml = `
396
+ name: transform_test
397
+ version: "1.0.0"
398
+ rules:
399
+ - name: "strip-auth"
400
+ description: "Strip authorization header"
401
+ effect: TRANSFORM
402
+ priority: 1
403
+ conditions:
404
+ tools:
405
+ - "http.request"
406
+ transformations:
407
+ - type: strip_header
408
+ target: "Authorization"
409
+ `;
410
+ const packPath = writeTmpYaml('transform.yaml', yaml);
411
+ const engine = new engine_1.PolicyEngine(packPath);
412
+ const result = engine.evaluate(buildToolCall());
413
+ expect(result.decision).toBe('transform');
414
+ expect(result.rule_name).toBe('strip-auth');
415
+ expect(result.transformations).toEqual([
416
+ { type: 'strip_header', target: 'Authorization' },
417
+ ]);
418
+ });
419
+ it('should return REQUIRE_APPROVAL decision with approval metadata', () => {
420
+ const yaml = `
421
+ name: approval_test
422
+ version: "1.0.0"
423
+ rules:
424
+ - name: "require-approval-writes"
425
+ description: "Writes need approval"
426
+ effect: REQUIRE_APPROVAL
427
+ priority: 1
428
+ conditions:
429
+ capabilities:
430
+ - "write"
431
+ approval:
432
+ scope: "team_lead"
433
+ ttl_seconds: 1800
434
+ reason: "Write operations require approval"
435
+ `;
436
+ const packPath = writeTmpYaml('approval.yaml', yaml);
437
+ const engine = new engine_1.PolicyEngine(packPath);
438
+ const tc = buildToolCall({ tool: { name: 'db.update', capability: 'write' } });
439
+ const result = engine.evaluate(tc);
440
+ expect(result.decision).toBe('require_approval');
441
+ expect(result.approval).toBeDefined();
442
+ expect(result.approval.scope).toBe('team_lead');
443
+ expect(result.approval.ttl_seconds).toBe(1800);
444
+ expect(result.approval.reason).toBe('Write operations require approval');
445
+ });
446
+ it('should apply default DENY when no rules match and no pack default_effect', () => {
447
+ const yaml = `
448
+ name: no_match_test
449
+ version: "1.0.0"
450
+ rules:
451
+ - name: "only-specific-tool"
452
+ effect: ALLOW
453
+ priority: 1
454
+ conditions:
455
+ tools:
456
+ - "very.specific.tool"
457
+ `;
458
+ const packPath = writeTmpYaml('no-match.yaml', yaml);
459
+ const engine = new engine_1.PolicyEngine(packPath, 'DENY');
460
+ const result = engine.evaluate(buildToolCall());
461
+ expect(result.decision).toBe('deny');
462
+ expect(result.rule_name).toBe('__default');
463
+ expect(result.reasons).toEqual(expect.arrayContaining([expect.stringMatching(/No matching rule found/)]));
464
+ });
465
+ it('should use pack-level default_effect when no rules match', () => {
466
+ const yaml = `
467
+ name: pack_default_test
468
+ version: "1.0.0"
469
+ default_effect: ALLOW
470
+ rules:
471
+ - name: "only-specific-tool"
472
+ effect: DENY
473
+ priority: 1
474
+ conditions:
475
+ tools:
476
+ - "very.specific.tool"
477
+ `;
478
+ const packPath = writeTmpYaml('pack-default.yaml', yaml);
479
+ const engine = new engine_1.PolicyEngine(packPath, 'DENY');
480
+ const result = engine.evaluate(buildToolCall());
481
+ // Pack-level default_effect takes precedence over constructor default
482
+ expect(result.decision).toBe('allow');
483
+ expect(result.rule_name).toBe('__default');
484
+ });
485
+ it('should include description in reasons when rule has a description', () => {
486
+ const yaml = `
487
+ name: desc_test
488
+ version: "1.0.0"
489
+ rules:
490
+ - name: "described-rule"
491
+ description: "This rule has a detailed description"
492
+ effect: ALLOW
493
+ priority: 1
494
+ conditions: {}
495
+ `;
496
+ const packPath = writeTmpYaml('desc.yaml', yaml);
497
+ const engine = new engine_1.PolicyEngine(packPath);
498
+ const result = engine.evaluate(buildToolCall());
499
+ expect(result.reasons).toContain('This rule has a detailed description');
500
+ expect(result.reasons).toContain('Matched rule "described-rule"');
501
+ });
502
+ });
503
+ // -----------------------------------------------------------------------
504
+ // evaluate() -- Condition Matching
505
+ // -----------------------------------------------------------------------
506
+ describe('evaluate - condition matching', () => {
507
+ it('should match on tool name (tools condition)', () => {
508
+ const yaml = `
509
+ name: tool_name_test
510
+ version: "1.0.0"
511
+ rules:
512
+ - name: "match-http"
513
+ effect: ALLOW
514
+ priority: 1
515
+ conditions:
516
+ tools:
517
+ - "http.request"
518
+ - name: "deny-all"
519
+ effect: DENY
520
+ priority: 100
521
+ conditions: {}
522
+ `;
523
+ const packPath = writeTmpYaml('tool-name.yaml', yaml);
524
+ const engine = new engine_1.PolicyEngine(packPath);
525
+ const httpCall = buildToolCall({ tool: { name: 'http.request', capability: 'read' } });
526
+ expect(engine.evaluate(httpCall).decision).toBe('allow');
527
+ const dbCall = buildToolCall({ tool: { name: 'db.query', capability: 'read' } });
528
+ expect(engine.evaluate(dbCall).decision).toBe('deny');
529
+ });
530
+ it('should match on tool_match regex', () => {
531
+ const yaml = `
532
+ name: tool_regex_test
533
+ version: "1.0.0"
534
+ rules:
535
+ - name: "match-http-wildcard"
536
+ effect: ALLOW
537
+ priority: 1
538
+ conditions:
539
+ tool_match: "^http\\\\."
540
+ - name: "deny-all"
541
+ effect: DENY
542
+ priority: 100
543
+ conditions: {}
544
+ `;
545
+ const packPath = writeTmpYaml('tool-regex.yaml', yaml);
546
+ const engine = new engine_1.PolicyEngine(packPath);
547
+ const httpCall = buildToolCall({ tool: { name: 'http.request', capability: 'read' } });
548
+ expect(engine.evaluate(httpCall).decision).toBe('allow');
549
+ const httpPostCall = buildToolCall({ tool: { name: 'http.post', capability: 'write' } });
550
+ expect(engine.evaluate(httpPostCall).decision).toBe('allow');
551
+ const dbCall = buildToolCall({ tool: { name: 'db.query', capability: 'read' } });
552
+ expect(engine.evaluate(dbCall).decision).toBe('deny');
553
+ });
554
+ it('should match on capability', () => {
555
+ const yaml = `
556
+ name: cap_test
557
+ version: "1.0.0"
558
+ rules:
559
+ - name: "allow-reads"
560
+ effect: ALLOW
561
+ priority: 1
562
+ conditions:
563
+ capabilities:
564
+ - "read"
565
+ - name: "deny-all"
566
+ effect: DENY
567
+ priority: 100
568
+ conditions: {}
569
+ `;
570
+ const packPath = writeTmpYaml('capability.yaml', yaml);
571
+ const engine = new engine_1.PolicyEngine(packPath);
572
+ const readCall = buildToolCall({ tool: { name: 'http.request', capability: 'read' } });
573
+ expect(engine.evaluate(readCall).decision).toBe('allow');
574
+ const writeCall = buildToolCall({ tool: { name: 'http.request', capability: 'write' } });
575
+ expect(engine.evaluate(writeCall).decision).toBe('deny');
576
+ });
577
+ it('should match on domain extracted from URL in args', () => {
578
+ const yaml = `
579
+ name: domain_test
580
+ version: "1.0.0"
581
+ rules:
582
+ - name: "allow-github"
583
+ effect: ALLOW
584
+ priority: 1
585
+ conditions:
586
+ domains:
587
+ - "api.github.com"
588
+ - name: "deny-all"
589
+ effect: DENY
590
+ priority: 100
591
+ conditions: {}
592
+ `;
593
+ const packPath = writeTmpYaml('domain-match.yaml', yaml);
594
+ const engine = new engine_1.PolicyEngine(packPath);
595
+ const githubCall = buildToolCall({ args: { url: 'https://api.github.com/repos', method: 'GET' } });
596
+ expect(engine.evaluate(githubCall).decision).toBe('allow');
597
+ const otherCall = buildToolCall({ args: { url: 'https://evil.com/data', method: 'GET' } });
598
+ expect(engine.evaluate(otherCall).decision).toBe('deny');
599
+ });
600
+ it('should match wildcard subdomain patterns (*.example.com)', () => {
601
+ const yaml = `
602
+ name: wildcard_domain_test
603
+ version: "1.0.0"
604
+ rules:
605
+ - name: "allow-aws"
606
+ effect: ALLOW
607
+ priority: 1
608
+ conditions:
609
+ domains:
610
+ - "*.amazonaws.com"
611
+ - name: "deny-all"
612
+ effect: DENY
613
+ priority: 100
614
+ conditions: {}
615
+ `;
616
+ const packPath = writeTmpYaml('wildcard-domain.yaml', yaml);
617
+ const engine = new engine_1.PolicyEngine(packPath);
618
+ const s3Call = buildToolCall({ args: { url: 'https://s3.amazonaws.com/bucket/key', method: 'GET' } });
619
+ expect(engine.evaluate(s3Call).decision).toBe('allow');
620
+ const deepSubCall = buildToolCall({ args: { url: 'https://us-east-1.s3.amazonaws.com/b', method: 'GET' } });
621
+ expect(engine.evaluate(deepSubCall).decision).toBe('allow');
622
+ const otherCall = buildToolCall({ args: { url: 'https://evil.com/data', method: 'GET' } });
623
+ expect(engine.evaluate(otherCall).decision).toBe('deny');
624
+ });
625
+ it('should fail domain match when tool call has no URL', () => {
626
+ const yaml = `
627
+ name: no_url_domain_test
628
+ version: "1.0.0"
629
+ rules:
630
+ - name: "require-domain"
631
+ effect: ALLOW
632
+ priority: 1
633
+ conditions:
634
+ domains:
635
+ - "api.github.com"
636
+ - name: "deny-all"
637
+ effect: DENY
638
+ priority: 100
639
+ conditions: {}
640
+ `;
641
+ const packPath = writeTmpYaml('no-url.yaml', yaml);
642
+ const engine = new engine_1.PolicyEngine(packPath);
643
+ // Must explicitly set url to undefined to override the default in buildToolCall
644
+ const noUrlCall = buildToolCall({ args: { method: 'GET', url: undefined } });
645
+ expect(engine.evaluate(noUrlCall).decision).toBe('deny');
646
+ });
647
+ it('should match on HTTP method (case-insensitive)', () => {
648
+ const yaml = `
649
+ name: method_test
650
+ version: "1.0.0"
651
+ rules:
652
+ - name: "allow-gets"
653
+ effect: ALLOW
654
+ priority: 1
655
+ conditions:
656
+ methods:
657
+ - "GET"
658
+ - "HEAD"
659
+ - name: "deny-all"
660
+ effect: DENY
661
+ priority: 100
662
+ conditions: {}
663
+ `;
664
+ const packPath = writeTmpYaml('method.yaml', yaml);
665
+ const engine = new engine_1.PolicyEngine(packPath);
666
+ const getCall = buildToolCall({ args: { method: 'get', url: 'https://api.github.com/repos' } });
667
+ expect(engine.evaluate(getCall).decision).toBe('allow');
668
+ const postCall = buildToolCall({ args: { method: 'POST', url: 'https://api.github.com/repos' } });
669
+ expect(engine.evaluate(postCall).decision).toBe('deny');
670
+ });
671
+ it('should fail method match when tool call has no method', () => {
672
+ const yaml = `
673
+ name: no_method_test
674
+ version: "1.0.0"
675
+ rules:
676
+ - name: "require-method"
677
+ effect: ALLOW
678
+ priority: 1
679
+ conditions:
680
+ methods:
681
+ - "GET"
682
+ - name: "deny-all"
683
+ effect: DENY
684
+ priority: 100
685
+ conditions: {}
686
+ `;
687
+ const packPath = writeTmpYaml('no-method.yaml', yaml);
688
+ const engine = new engine_1.PolicyEngine(packPath);
689
+ // Must explicitly set method to undefined to override the default in buildToolCall
690
+ const noMethodCall = buildToolCall({ args: { url: 'https://api.github.com/repos', method: undefined } });
691
+ expect(engine.evaluate(noMethodCall).decision).toBe('deny');
692
+ });
693
+ it('should match on actor type (actor_types condition)', () => {
694
+ const yaml = `
695
+ name: actor_type_test
696
+ version: "1.0.0"
697
+ rules:
698
+ - name: "allow-agents"
699
+ effect: ALLOW
700
+ priority: 1
701
+ conditions:
702
+ actor_types:
703
+ - "agent"
704
+ - name: "deny-all"
705
+ effect: DENY
706
+ priority: 100
707
+ conditions: {}
708
+ `;
709
+ const packPath = writeTmpYaml('actor-type.yaml', yaml);
710
+ const engine = new engine_1.PolicyEngine(packPath);
711
+ const agentCall = buildToolCall({ actor: { type: 'agent', id: 'a1' } });
712
+ expect(engine.evaluate(agentCall).decision).toBe('allow');
713
+ const userCall = buildToolCall({ actor: { type: 'user', id: 'u1' } });
714
+ expect(engine.evaluate(userCall).decision).toBe('deny');
715
+ });
716
+ it('should match on actor id (actors condition)', () => {
717
+ const yaml = `
718
+ name: actor_id_test
719
+ version: "1.0.0"
720
+ rules:
721
+ - name: "allow-specific-actor"
722
+ effect: ALLOW
723
+ priority: 1
724
+ conditions:
725
+ actors:
726
+ - "trusted-agent-1"
727
+ - name: "deny-all"
728
+ effect: DENY
729
+ priority: 100
730
+ conditions: {}
731
+ `;
732
+ const packPath = writeTmpYaml('actor-id.yaml', yaml);
733
+ const engine = new engine_1.PolicyEngine(packPath);
734
+ const trustedCall = buildToolCall({ actor: { type: 'agent', id: 'trusted-agent-1' } });
735
+ expect(engine.evaluate(trustedCall).decision).toBe('allow');
736
+ const untrustedCall = buildToolCall({ actor: { type: 'agent', id: 'unknown-agent' } });
737
+ expect(engine.evaluate(untrustedCall).decision).toBe('deny');
738
+ });
739
+ it('should match on labels (at least one label matches)', () => {
740
+ const yaml = `
741
+ name: label_test
742
+ version: "1.0.0"
743
+ rules:
744
+ - name: "allow-production"
745
+ effect: ALLOW
746
+ priority: 1
747
+ conditions:
748
+ labels:
749
+ - "production"
750
+ - "staging"
751
+ - name: "deny-all"
752
+ effect: DENY
753
+ priority: 100
754
+ conditions: {}
755
+ `;
756
+ const packPath = writeTmpYaml('labels.yaml', yaml);
757
+ const engine = new engine_1.PolicyEngine(packPath);
758
+ const prodCall = buildToolCall({ context: { labels: ['production', 'important'] } });
759
+ expect(engine.evaluate(prodCall).decision).toBe('allow');
760
+ const stagingCall = buildToolCall({ context: { labels: ['staging'] } });
761
+ expect(engine.evaluate(stagingCall).decision).toBe('allow');
762
+ const devCall = buildToolCall({ context: { labels: ['development'] } });
763
+ expect(engine.evaluate(devCall).decision).toBe('deny');
764
+ const noLabelsCall = buildToolCall();
765
+ expect(engine.evaluate(noLabelsCall).decision).toBe('deny');
766
+ });
767
+ it('should match on label_match regex', () => {
768
+ const yaml = `
769
+ name: label_regex_test
770
+ version: "1.0.0"
771
+ rules:
772
+ - name: "allow-env-labels"
773
+ effect: ALLOW
774
+ priority: 1
775
+ conditions:
776
+ label_match: "^(prod|staging)"
777
+ - name: "deny-all"
778
+ effect: DENY
779
+ priority: 100
780
+ conditions: {}
781
+ `;
782
+ const packPath = writeTmpYaml('label-regex.yaml', yaml);
783
+ const engine = new engine_1.PolicyEngine(packPath);
784
+ const prodCall = buildToolCall({ context: { labels: ['production'] } });
785
+ expect(engine.evaluate(prodCall).decision).toBe('allow');
786
+ const stagingCall = buildToolCall({ context: { labels: ['staging-test'] } });
787
+ expect(engine.evaluate(stagingCall).decision).toBe('allow');
788
+ const devCall = buildToolCall({ context: { labels: ['development'] } });
789
+ expect(engine.evaluate(devCall).decision).toBe('deny');
790
+ });
791
+ it('should match on platforms', () => {
792
+ const yaml = `
793
+ name: platform_test
794
+ version: "1.0.0"
795
+ rules:
796
+ - name: "allow-langgraph"
797
+ effect: ALLOW
798
+ priority: 1
799
+ conditions:
800
+ platforms:
801
+ - "langgraph"
802
+ - name: "deny-all"
803
+ effect: DENY
804
+ priority: 100
805
+ conditions: {}
806
+ `;
807
+ const packPath = writeTmpYaml('platforms.yaml', yaml);
808
+ const engine = new engine_1.PolicyEngine(packPath);
809
+ const lgCall = buildToolCall({ source: { platform: 'langgraph' } });
810
+ expect(engine.evaluate(lgCall).decision).toBe('allow');
811
+ const n8nCall = buildToolCall({ source: { platform: 'n8n' } });
812
+ expect(engine.evaluate(n8nCall).decision).toBe('deny');
813
+ });
814
+ it('should match on workspace_ids', () => {
815
+ const yaml = `
816
+ name: workspace_test
817
+ version: "1.0.0"
818
+ rules:
819
+ - name: "allow-specific-ws"
820
+ effect: ALLOW
821
+ priority: 1
822
+ conditions:
823
+ workspace_ids:
824
+ - "ws-alpha"
825
+ - "ws-beta"
826
+ - name: "deny-all"
827
+ effect: DENY
828
+ priority: 100
829
+ conditions: {}
830
+ `;
831
+ const packPath = writeTmpYaml('workspace.yaml', yaml);
832
+ const engine = new engine_1.PolicyEngine(packPath);
833
+ const alphaCall = buildToolCall();
834
+ alphaCall.workspace_id = 'ws-alpha';
835
+ expect(engine.evaluate(alphaCall).decision).toBe('allow');
836
+ const gammaCall = buildToolCall();
837
+ gammaCall.workspace_id = 'ws-gamma';
838
+ expect(engine.evaluate(gammaCall).decision).toBe('deny');
839
+ });
840
+ it('should apply AND logic for multiple conditions in a single rule', () => {
841
+ const yaml = `
842
+ name: and_logic_test
843
+ version: "1.0.0"
844
+ rules:
845
+ - name: "specific-combo"
846
+ effect: ALLOW
847
+ priority: 1
848
+ conditions:
849
+ tools:
850
+ - "http.request"
851
+ capabilities:
852
+ - "read"
853
+ methods:
854
+ - "GET"
855
+ - name: "deny-all"
856
+ effect: DENY
857
+ priority: 100
858
+ conditions: {}
859
+ `;
860
+ const packPath = writeTmpYaml('and-logic.yaml', yaml);
861
+ const engine = new engine_1.PolicyEngine(packPath);
862
+ // All three conditions match
863
+ const fullMatch = buildToolCall({
864
+ tool: { name: 'http.request', capability: 'read' },
865
+ args: { method: 'GET', url: 'https://example.com' },
866
+ });
867
+ expect(engine.evaluate(fullMatch).decision).toBe('allow');
868
+ // Tool matches, capability matches, but method doesn't
869
+ const partialMatch = buildToolCall({
870
+ tool: { name: 'http.request', capability: 'read' },
871
+ args: { method: 'POST', url: 'https://example.com' },
872
+ });
873
+ expect(engine.evaluate(partialMatch).decision).toBe('deny');
874
+ });
875
+ it('should match everything when conditions object is empty', () => {
876
+ const yaml = `
877
+ name: empty_conditions_test
878
+ version: "1.0.0"
879
+ rules:
880
+ - name: "catch-all"
881
+ effect: ALLOW
882
+ priority: 1
883
+ conditions: {}
884
+ `;
885
+ const packPath = writeTmpYaml('empty-conditions.yaml', yaml);
886
+ const engine = new engine_1.PolicyEngine(packPath);
887
+ const anyCall = buildToolCall({
888
+ tool: { name: 'anything', capability: 'admin' },
889
+ args: { method: 'DELETE', url: 'https://evil.com/destroy' },
890
+ });
891
+ expect(engine.evaluate(anyCall).decision).toBe('allow');
892
+ expect(engine.evaluate(anyCall).rule_name).toBe('catch-all');
893
+ });
894
+ });
895
+ // -----------------------------------------------------------------------
896
+ // evaluate() -- Priority Ordering
897
+ // -----------------------------------------------------------------------
898
+ describe('evaluate - priority ordering', () => {
899
+ it('should return the first matching rule by priority (lower = higher precedence)', () => {
900
+ const yaml = `
901
+ name: priority_order_test
902
+ version: "1.0.0"
903
+ rules:
904
+ - name: "low-priority-allow"
905
+ effect: ALLOW
906
+ priority: 50
907
+ conditions:
908
+ capabilities:
909
+ - "read"
910
+ - name: "high-priority-deny"
911
+ effect: DENY
912
+ priority: 5
913
+ conditions:
914
+ capabilities:
915
+ - "read"
916
+ `;
917
+ const packPath = writeTmpYaml('priority-order.yaml', yaml);
918
+ const engine = new engine_1.PolicyEngine(packPath);
919
+ const readCall = buildToolCall({ tool: { name: 'http.request', capability: 'read' } });
920
+ const result = engine.evaluate(readCall);
921
+ // The higher-precedence (lower number) rule should win
922
+ expect(result.decision).toBe('deny');
923
+ expect(result.rule_name).toBe('high-priority-deny');
924
+ });
925
+ it('should match more specific rules before catch-all when priorities enforce it', () => {
926
+ const yaml = `
927
+ name: specificity_test
928
+ version: "1.0.0"
929
+ rules:
930
+ - name: "specific-allow"
931
+ effect: ALLOW
932
+ priority: 10
933
+ conditions:
934
+ tools:
935
+ - "http.request"
936
+ methods:
937
+ - "GET"
938
+ - name: "broad-deny"
939
+ effect: DENY
940
+ priority: 20
941
+ conditions: {}
942
+ `;
943
+ const packPath = writeTmpYaml('specificity.yaml', yaml);
944
+ const engine = new engine_1.PolicyEngine(packPath);
945
+ const getCall = buildToolCall({
946
+ tool: { name: 'http.request', capability: 'read' },
947
+ args: { method: 'GET', url: 'https://example.com' },
948
+ });
949
+ expect(engine.evaluate(getCall).decision).toBe('allow');
950
+ const postCall = buildToolCall({
951
+ tool: { name: 'http.request', capability: 'write' },
952
+ args: { method: 'POST', url: 'https://example.com' },
953
+ });
954
+ expect(engine.evaluate(postCall).decision).toBe('deny');
955
+ });
956
+ });
957
+ // -----------------------------------------------------------------------
958
+ // evaluate() -- Domain Blocklist / Allowlist (Pack-level)
959
+ // -----------------------------------------------------------------------
960
+ describe('evaluate - pack-level domain lists', () => {
961
+ it('should DENY when domain is in pack-level domain_blocklist', () => {
962
+ const yaml = `
963
+ name: blocklist_test
964
+ version: "1.0.0"
965
+ domain_blocklist:
966
+ - "evil.com"
967
+ - "metadata.google.internal"
968
+ rules:
969
+ - name: "allow-all"
970
+ effect: ALLOW
971
+ priority: 1
972
+ conditions: {}
973
+ `;
974
+ const packPath = writeTmpYaml('blocklist.yaml', yaml);
975
+ const engine = new engine_1.PolicyEngine(packPath);
976
+ const evilCall = buildToolCall({ args: { url: 'https://evil.com/steal', method: 'GET' } });
977
+ const result = engine.evaluate(evilCall);
978
+ expect(result.decision).toBe('deny');
979
+ expect(result.rule_name).toBe('__pack_domain_blocklist');
980
+ expect(result.reasons[0]).toContain('evil.com');
981
+ });
982
+ it('should DENY when domain is not in pack-level domain_allowlist', () => {
983
+ const yaml = `
984
+ name: allowlist_test
985
+ version: "1.0.0"
986
+ domain_allowlist:
987
+ - "api.github.com"
988
+ - "api.slack.com"
989
+ rules:
990
+ - name: "allow-all"
991
+ effect: ALLOW
992
+ priority: 1
993
+ conditions: {}
994
+ `;
995
+ const packPath = writeTmpYaml('allowlist.yaml', yaml);
996
+ const engine = new engine_1.PolicyEngine(packPath);
997
+ // Allowed domain
998
+ const githubCall = buildToolCall({ args: { url: 'https://api.github.com/repos', method: 'GET' } });
999
+ expect(engine.evaluate(githubCall).decision).toBe('allow');
1000
+ // Not in allowlist
1001
+ const unknownCall = buildToolCall({ args: { url: 'https://unknown.io/api', method: 'GET' } });
1002
+ const result = engine.evaluate(unknownCall);
1003
+ expect(result.decision).toBe('deny');
1004
+ expect(result.rule_name).toBe('__pack_domain_allowlist');
1005
+ });
1006
+ it('should ALLOW tool calls without URL even when domain_allowlist is set', () => {
1007
+ const yaml = `
1008
+ name: no_url_allowlist_test
1009
+ version: "1.0.0"
1010
+ domain_allowlist:
1011
+ - "api.github.com"
1012
+ rules:
1013
+ - name: "allow-all"
1014
+ effect: ALLOW
1015
+ priority: 1
1016
+ conditions: {}
1017
+ `;
1018
+ const packPath = writeTmpYaml('no-url-allowlist.yaml', yaml);
1019
+ const engine = new engine_1.PolicyEngine(packPath);
1020
+ // No URL in args -- domain checks should be skipped
1021
+ const noUrlCall = buildToolCall({ args: { method: 'GET' } });
1022
+ expect(engine.evaluate(noUrlCall).decision).toBe('allow');
1023
+ });
1024
+ it('should support wildcard domains in pack-level blocklist', () => {
1025
+ const yaml = `
1026
+ name: wildcard_blocklist_test
1027
+ version: "1.0.0"
1028
+ domain_blocklist:
1029
+ - "*.evil.com"
1030
+ rules:
1031
+ - name: "allow-all"
1032
+ effect: ALLOW
1033
+ priority: 1
1034
+ conditions: {}
1035
+ `;
1036
+ const packPath = writeTmpYaml('wildcard-blocklist.yaml', yaml);
1037
+ const engine = new engine_1.PolicyEngine(packPath);
1038
+ const subdomainCall = buildToolCall({ args: { url: 'https://api.evil.com/steal', method: 'GET' } });
1039
+ expect(engine.evaluate(subdomainCall).decision).toBe('deny');
1040
+ // The root domain itself should also match (*.evil.com -> evil.com)
1041
+ const rootCall = buildToolCall({ args: { url: 'https://evil.com/steal', method: 'GET' } });
1042
+ expect(engine.evaluate(rootCall).decision).toBe('deny');
1043
+ });
1044
+ it('should check blocklist before allowlist', () => {
1045
+ const yaml = `
1046
+ name: blocklist_before_allowlist_test
1047
+ version: "1.0.0"
1048
+ domain_allowlist:
1049
+ - "*.example.com"
1050
+ domain_blocklist:
1051
+ - "bad.example.com"
1052
+ rules:
1053
+ - name: "allow-all"
1054
+ effect: ALLOW
1055
+ priority: 1
1056
+ conditions: {}
1057
+ `;
1058
+ const packPath = writeTmpYaml('blocklist-first.yaml', yaml);
1059
+ const engine = new engine_1.PolicyEngine(packPath);
1060
+ // Domain is in both allowlist (via wildcard) and blocklist -- blocklist wins
1061
+ const badCall = buildToolCall({ args: { url: 'https://bad.example.com/data', method: 'GET' } });
1062
+ expect(engine.evaluate(badCall).decision).toBe('deny');
1063
+ expect(engine.evaluate(badCall).rule_name).toBe('__pack_domain_blocklist');
1064
+ // Good subdomain is fine
1065
+ const goodCall = buildToolCall({ args: { url: 'https://good.example.com/data', method: 'GET' } });
1066
+ expect(engine.evaluate(goodCall).decision).toBe('allow');
1067
+ });
1068
+ });
1069
+ // -----------------------------------------------------------------------
1070
+ // evaluate() -- Rule-level domain_blocklist
1071
+ // -----------------------------------------------------------------------
1072
+ describe('evaluate - rule-level domain_blocklist', () => {
1073
+ it('should not match rule when call domain is in rule-level domain_blocklist', () => {
1074
+ const yaml = `
1075
+ name: rule_blocklist_test
1076
+ version: "1.0.0"
1077
+ rules:
1078
+ - name: "allow-gets-except-bad"
1079
+ effect: ALLOW
1080
+ priority: 1
1081
+ conditions:
1082
+ methods:
1083
+ - "GET"
1084
+ domain_blocklist:
1085
+ - "bad.example.com"
1086
+ - name: "deny-all"
1087
+ effect: DENY
1088
+ priority: 100
1089
+ conditions: {}
1090
+ `;
1091
+ const packPath = writeTmpYaml('rule-blocklist.yaml', yaml);
1092
+ const engine = new engine_1.PolicyEngine(packPath);
1093
+ const badCall = buildToolCall({ args: { url: 'https://bad.example.com/data', method: 'GET' } });
1094
+ expect(engine.evaluate(badCall).decision).toBe('deny');
1095
+ expect(engine.evaluate(badCall).rule_name).toBe('deny-all');
1096
+ const goodCall = buildToolCall({ args: { url: 'https://good.example.com/data', method: 'GET' } });
1097
+ expect(engine.evaluate(goodCall).decision).toBe('allow');
1098
+ });
1099
+ });
1100
+ // -----------------------------------------------------------------------
1101
+ // reload()
1102
+ // -----------------------------------------------------------------------
1103
+ describe('reload', () => {
1104
+ it('should reload policy pack from disk reflecting file changes', () => {
1105
+ const yaml1 = `
1106
+ name: reload_v1
1107
+ version: "1.0.0"
1108
+ rules:
1109
+ - name: "deny-all"
1110
+ effect: DENY
1111
+ priority: 1
1112
+ conditions: {}
1113
+ `;
1114
+ const packPath = writeTmpYaml('reload.yaml', yaml1);
1115
+ const engine = new engine_1.PolicyEngine(packPath);
1116
+ expect(engine.getPack().name).toBe('reload_v1');
1117
+ expect(engine.evaluate(buildToolCall()).decision).toBe('deny');
1118
+ // Overwrite the file with a new pack
1119
+ const yaml2 = `
1120
+ name: reload_v2
1121
+ version: "2.0.0"
1122
+ rules:
1123
+ - name: "allow-all"
1124
+ effect: ALLOW
1125
+ priority: 1
1126
+ conditions: {}
1127
+ `;
1128
+ fs.writeFileSync(packPath, yaml2, 'utf-8');
1129
+ engine.reload();
1130
+ expect(engine.getPack().name).toBe('reload_v2');
1131
+ expect(engine.evaluate(buildToolCall()).decision).toBe('allow');
1132
+ });
1133
+ it('should throw if the reloaded file is invalid', () => {
1134
+ const validYaml = `
1135
+ name: reload_valid
1136
+ version: "1.0.0"
1137
+ rules:
1138
+ - name: "allow-all"
1139
+ effect: ALLOW
1140
+ priority: 1
1141
+ conditions: {}
1142
+ `;
1143
+ const packPath = writeTmpYaml('reload-invalid.yaml', validYaml);
1144
+ const engine = new engine_1.PolicyEngine(packPath);
1145
+ expect(engine.getPack().name).toBe('reload_valid');
1146
+ // Overwrite with invalid YAML
1147
+ fs.writeFileSync(packPath, 'just a scalar string', 'utf-8');
1148
+ expect(() => engine.reload()).toThrow(/does not contain a valid YAML object/);
1149
+ // The original pack should still be loaded (reload failed, old pack retained)
1150
+ // Actually, per the implementation, reload() throws, so the internal state
1151
+ // stays as-is because the assignment only happens after loadPack succeeds.
1152
+ expect(engine.getPack().name).toBe('reload_valid');
1153
+ });
1154
+ });
1155
+ // -----------------------------------------------------------------------
1156
+ // fromPack() -- Static Factory
1157
+ // -----------------------------------------------------------------------
1158
+ describe('fromPack', () => {
1159
+ it('should create a working engine from an in-memory PolicyPack', () => {
1160
+ const pack = {
1161
+ name: 'in_memory_pack',
1162
+ version: '1.0.0',
1163
+ rules: [
1164
+ {
1165
+ name: 'allow-reads',
1166
+ effect: 'ALLOW',
1167
+ priority: 10,
1168
+ conditions: { capabilities: ['read'] },
1169
+ },
1170
+ {
1171
+ name: 'deny-all',
1172
+ effect: 'DENY',
1173
+ priority: 100,
1174
+ conditions: {},
1175
+ },
1176
+ ],
1177
+ };
1178
+ const engine = engine_1.PolicyEngine.fromPack(pack);
1179
+ const loaded = engine.getPack();
1180
+ expect(loaded.name).toBe('in_memory_pack');
1181
+ expect(loaded.version).toBe('1.0.0');
1182
+ expect(loaded.rules).toHaveLength(2);
1183
+ });
1184
+ it('should throw on an invalid pack', () => {
1185
+ const badPack = {
1186
+ name: '',
1187
+ version: '1.0.0',
1188
+ rules: [{ name: 'r', effect: 'ALLOW', conditions: {} }],
1189
+ };
1190
+ expect(() => engine_1.PolicyEngine.fromPack(badPack)).toThrow(/Invalid policy pack/);
1191
+ });
1192
+ it('should evaluate rules correctly', () => {
1193
+ const pack = {
1194
+ name: 'eval_test',
1195
+ version: '1.0.0',
1196
+ rules: [
1197
+ {
1198
+ name: 'allow-reads',
1199
+ effect: 'ALLOW',
1200
+ priority: 10,
1201
+ conditions: { capabilities: ['read'] },
1202
+ },
1203
+ {
1204
+ name: 'deny-all',
1205
+ effect: 'DENY',
1206
+ priority: 100,
1207
+ conditions: {},
1208
+ },
1209
+ ],
1210
+ };
1211
+ const engine = engine_1.PolicyEngine.fromPack(pack);
1212
+ const readCall = buildToolCall({ tool: { name: 'http.request', capability: 'read' } });
1213
+ expect(engine.evaluate(readCall).decision).toBe('allow');
1214
+ const writeCall = buildToolCall({ tool: { name: 'http.request', capability: 'write' } });
1215
+ expect(engine.evaluate(writeCall).decision).toBe('deny');
1216
+ });
1217
+ it('should sort rules by priority', () => {
1218
+ const pack = {
1219
+ name: 'sort_test',
1220
+ version: '1.0.0',
1221
+ rules: [
1222
+ { name: 'low-priority', effect: 'DENY', priority: 50, conditions: {} },
1223
+ { name: 'high-priority', effect: 'ALLOW', priority: 5, conditions: {} },
1224
+ { name: 'medium-priority', effect: 'DENY', priority: 25, conditions: {} },
1225
+ ],
1226
+ };
1227
+ const engine = engine_1.PolicyEngine.fromPack(pack);
1228
+ const loaded = engine.getPack();
1229
+ expect(loaded.rules[0].name).toBe('high-priority');
1230
+ expect(loaded.rules[1].name).toBe('medium-priority');
1231
+ expect(loaded.rules[2].name).toBe('low-priority');
1232
+ });
1233
+ it('should not mutate the original pack', () => {
1234
+ const pack = {
1235
+ name: 'no_mutate_test',
1236
+ version: '1.0.0',
1237
+ rules: [
1238
+ { name: 'z-rule', effect: 'DENY', priority: 50, conditions: {} },
1239
+ { name: 'a-rule', effect: 'ALLOW', priority: 5, conditions: {} },
1240
+ ],
1241
+ };
1242
+ const originalFirstRule = pack.rules[0].name;
1243
+ engine_1.PolicyEngine.fromPack(pack);
1244
+ // Original pack should be unchanged
1245
+ expect(pack.rules[0].name).toBe(originalFirstRule);
1246
+ });
1247
+ it('should respect the defaultEffect parameter', () => {
1248
+ const pack = {
1249
+ name: 'default_effect_test',
1250
+ version: '1.0.0',
1251
+ rules: [
1252
+ {
1253
+ name: 'only-specific',
1254
+ effect: 'DENY',
1255
+ priority: 1,
1256
+ conditions: { tools: ['very.specific.tool'] },
1257
+ },
1258
+ ],
1259
+ };
1260
+ const engine = engine_1.PolicyEngine.fromPack(pack, 'ALLOW');
1261
+ const result = engine.evaluate(buildToolCall());
1262
+ expect(result.decision).toBe('allow');
1263
+ expect(result.rule_name).toBe('__default');
1264
+ });
1265
+ it('should support domain_allowlist and domain_blocklist', () => {
1266
+ const pack = {
1267
+ name: 'domain_list_test',
1268
+ version: '1.0.0',
1269
+ domain_blocklist: ['evil.com'],
1270
+ domain_allowlist: ['api.github.com'],
1271
+ rules: [
1272
+ { name: 'allow-all', effect: 'ALLOW', priority: 1, conditions: {} },
1273
+ ],
1274
+ };
1275
+ const engine = engine_1.PolicyEngine.fromPack(pack);
1276
+ const evilCall = buildToolCall({ args: { url: 'https://evil.com/steal', method: 'GET' } });
1277
+ expect(engine.evaluate(evilCall).decision).toBe('deny');
1278
+ const githubCall = buildToolCall({ args: { url: 'https://api.github.com/repos', method: 'GET' } });
1279
+ expect(engine.evaluate(githubCall).decision).toBe('allow');
1280
+ const unknownCall = buildToolCall({ args: { url: 'https://unknown.io/api', method: 'GET' } });
1281
+ expect(engine.evaluate(unknownCall).decision).toBe('deny');
1282
+ });
1283
+ });
1284
+ // -----------------------------------------------------------------------
1285
+ // getPack()
1286
+ // -----------------------------------------------------------------------
1287
+ describe('getPack', () => {
1288
+ it('should return the currently loaded policy pack', () => {
1289
+ const packPath = writeTmpYaml('getpack.yaml', VALID_PACK_YAML);
1290
+ const engine = new engine_1.PolicyEngine(packPath);
1291
+ const pack = engine.getPack();
1292
+ expect(pack).toBeDefined();
1293
+ expect(pack.name).toBe('test_pack');
1294
+ expect(pack.version).toBe('1.0.0');
1295
+ });
1296
+ });
1297
+ // -----------------------------------------------------------------------
1298
+ // SSRF Bypass Tests (IPv6 variants)
1299
+ // -----------------------------------------------------------------------
1300
+ describe('SSRF protection - IPv6 bypass vectors', () => {
1301
+ // Use a policy with a domain_allowlist to activate SSRF protection
1302
+ const ssrfPack = {
1303
+ name: 'ssrf_test',
1304
+ version: '1.0.0',
1305
+ domain_allowlist: ['api.github.com', 'api.slack.com'],
1306
+ rules: [
1307
+ {
1308
+ name: 'allow-all',
1309
+ effect: 'ALLOW',
1310
+ priority: 1,
1311
+ conditions: {},
1312
+ },
1313
+ ],
1314
+ };
1315
+ it('should deny IPv6 unspecified address (::)', () => {
1316
+ const engine = engine_1.PolicyEngine.fromPack(ssrfPack);
1317
+ const tc = buildToolCall({ args: { url: 'http://[::]/secret', method: 'GET' } });
1318
+ const result = engine.evaluate(tc);
1319
+ expect(result.decision).toBe('deny');
1320
+ expect(result.rule_name).toBe('__ssrf_protection');
1321
+ });
1322
+ it('should deny IPv6 documentation range (2001:db8::1)', () => {
1323
+ const engine = engine_1.PolicyEngine.fromPack(ssrfPack);
1324
+ const tc = buildToolCall({ args: { url: 'http://[2001:db8::1]/data', method: 'GET' } });
1325
+ const result = engine.evaluate(tc);
1326
+ expect(result.decision).toBe('deny');
1327
+ expect(result.rule_name).toBe('__ssrf_protection');
1328
+ });
1329
+ it('should deny IPv4-mapped IPv6 loopback (::ffff:127.0.0.1)', () => {
1330
+ const engine = engine_1.PolicyEngine.fromPack(ssrfPack);
1331
+ const tc = buildToolCall({ args: { url: 'http://[::ffff:127.0.0.1]/internal', method: 'GET' } });
1332
+ const result = engine.evaluate(tc);
1333
+ expect(result.decision).toBe('deny');
1334
+ expect(result.rule_name).toBe('__ssrf_protection');
1335
+ });
1336
+ it('should deny IPv4-mapped IPv6 private address (::ffff:10.0.0.1)', () => {
1337
+ const engine = engine_1.PolicyEngine.fromPack(ssrfPack);
1338
+ const tc = buildToolCall({ args: { url: 'http://[::ffff:10.0.0.1]/internal', method: 'GET' } });
1339
+ const result = engine.evaluate(tc);
1340
+ expect(result.decision).toBe('deny');
1341
+ expect(result.rule_name).toBe('__ssrf_protection');
1342
+ });
1343
+ });
1344
+ });
1345
+ //# sourceMappingURL=policy-engine.test.js.map