@actsecurity/iam-lens 0.1.156

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 (383) hide show
  1. package/LICENSE.txt +661 -0
  2. package/README.md +203 -0
  3. package/dist/cjs/cli.d.ts +3 -0
  4. package/dist/cjs/cli.d.ts.map +1 -0
  5. package/dist/cjs/cli.js +206 -0
  6. package/dist/cjs/cli.js.map +1 -0
  7. package/dist/cjs/collect/client.d.ts +550 -0
  8. package/dist/cjs/collect/client.d.ts.map +1 -0
  9. package/dist/cjs/collect/client.js +1119 -0
  10. package/dist/cjs/collect/client.js.map +1 -0
  11. package/dist/cjs/collect/collect.d.ts +44 -0
  12. package/dist/cjs/collect/collect.d.ts.map +1 -0
  13. package/dist/cjs/collect/collect.js +55 -0
  14. package/dist/cjs/collect/collect.js.map +1 -0
  15. package/dist/cjs/collect/inMemoryClient.d.ts +6 -0
  16. package/dist/cjs/collect/inMemoryClient.d.ts.map +1 -0
  17. package/dist/cjs/collect/inMemoryClient.js +11 -0
  18. package/dist/cjs/collect/inMemoryClient.js.map +1 -0
  19. package/dist/cjs/index.d.ts +13 -0
  20. package/dist/cjs/index.d.ts.map +1 -0
  21. package/dist/cjs/index.js +28 -0
  22. package/dist/cjs/index.js.map +1 -0
  23. package/dist/cjs/package.json +3 -0
  24. package/dist/cjs/principalCan/permission.d.ts +125 -0
  25. package/dist/cjs/principalCan/permission.d.ts.map +1 -0
  26. package/dist/cjs/principalCan/permission.js +1229 -0
  27. package/dist/cjs/principalCan/permission.js.map +1 -0
  28. package/dist/cjs/principalCan/permissionSet.d.ts +140 -0
  29. package/dist/cjs/principalCan/permissionSet.d.ts.map +1 -0
  30. package/dist/cjs/principalCan/permissionSet.js +437 -0
  31. package/dist/cjs/principalCan/permissionSet.js.map +1 -0
  32. package/dist/cjs/principalCan/principalCan.d.ts +26 -0
  33. package/dist/cjs/principalCan/principalCan.d.ts.map +1 -0
  34. package/dist/cjs/principalCan/principalCan.js +195 -0
  35. package/dist/cjs/principalCan/principalCan.js.map +1 -0
  36. package/dist/cjs/principalCan/principalConstraints.d.ts +147 -0
  37. package/dist/cjs/principalCan/principalConstraints.d.ts.map +1 -0
  38. package/dist/cjs/principalCan/principalConstraints.js +472 -0
  39. package/dist/cjs/principalCan/principalConstraints.js.map +1 -0
  40. package/dist/cjs/principalCan/resources/actions.d.ts +9 -0
  41. package/dist/cjs/principalCan/resources/actions.d.ts.map +1 -0
  42. package/dist/cjs/principalCan/resources/actions.js +27 -0
  43. package/dist/cjs/principalCan/resources/actions.js.map +1 -0
  44. package/dist/cjs/principalCan/resources/resourceTypes/iamRoles.d.ts +31 -0
  45. package/dist/cjs/principalCan/resources/resourceTypes/iamRoles.d.ts.map +1 -0
  46. package/dist/cjs/principalCan/resources/resourceTypes/iamRoles.js +114 -0
  47. package/dist/cjs/principalCan/resources/resourceTypes/iamRoles.js.map +1 -0
  48. package/dist/cjs/principalCan/resources/resourceTypes/kmsKeys.d.ts +30 -0
  49. package/dist/cjs/principalCan/resources/resourceTypes/kmsKeys.d.ts.map +1 -0
  50. package/dist/cjs/principalCan/resources/resourceTypes/kmsKeys.js +110 -0
  51. package/dist/cjs/principalCan/resources/resourceTypes/kmsKeys.js.map +1 -0
  52. package/dist/cjs/principalCan/resources/resourceTypes/s3Buckets.d.ts +18 -0
  53. package/dist/cjs/principalCan/resources/resourceTypes/s3Buckets.d.ts.map +1 -0
  54. package/dist/cjs/principalCan/resources/resourceTypes/s3Buckets.js +123 -0
  55. package/dist/cjs/principalCan/resources/resourceTypes/s3Buckets.js.map +1 -0
  56. package/dist/cjs/principalCan/resources/statements.d.ts +27 -0
  57. package/dist/cjs/principalCan/resources/statements.d.ts.map +1 -0
  58. package/dist/cjs/principalCan/resources/statements.js +119 -0
  59. package/dist/cjs/principalCan/resources/statements.js.map +1 -0
  60. package/dist/cjs/principalIndex/makePrincipalIndex.d.ts +8 -0
  61. package/dist/cjs/principalIndex/makePrincipalIndex.d.ts.map +1 -0
  62. package/dist/cjs/principalIndex/makePrincipalIndex.js +105 -0
  63. package/dist/cjs/principalIndex/makePrincipalIndex.js.map +1 -0
  64. package/dist/cjs/principals.d.ts +54 -0
  65. package/dist/cjs/principals.d.ts.map +1 -0
  66. package/dist/cjs/principals.js +143 -0
  67. package/dist/cjs/principals.js.map +1 -0
  68. package/dist/cjs/resources.d.ts +32 -0
  69. package/dist/cjs/resources.d.ts.map +1 -0
  70. package/dist/cjs/resources.js +71 -0
  71. package/dist/cjs/resources.js.map +1 -0
  72. package/dist/cjs/simulate/contextKeys.d.ts +68 -0
  73. package/dist/cjs/simulate/contextKeys.d.ts.map +1 -0
  74. package/dist/cjs/simulate/contextKeys.js +293 -0
  75. package/dist/cjs/simulate/contextKeys.js.map +1 -0
  76. package/dist/cjs/simulate/simulate.d.ts +72 -0
  77. package/dist/cjs/simulate/simulate.d.ts.map +1 -0
  78. package/dist/cjs/simulate/simulate.js +296 -0
  79. package/dist/cjs/simulate/simulate.js.map +1 -0
  80. package/dist/cjs/test-datasets/testClient.d.ts +17 -0
  81. package/dist/cjs/test-datasets/testClient.d.ts.map +1 -0
  82. package/dist/cjs/test-datasets/testClient.js +58 -0
  83. package/dist/cjs/test-datasets/testClient.js.map +1 -0
  84. package/dist/cjs/utils/arn.d.ts +22 -0
  85. package/dist/cjs/utils/arn.d.ts.map +1 -0
  86. package/dist/cjs/utils/arn.js +49 -0
  87. package/dist/cjs/utils/arn.js.map +1 -0
  88. package/dist/cjs/utils/bitset.d.ts +45 -0
  89. package/dist/cjs/utils/bitset.d.ts.map +1 -0
  90. package/dist/cjs/utils/bitset.js +149 -0
  91. package/dist/cjs/utils/bitset.js.map +1 -0
  92. package/dist/cjs/utils/dynamicImport.d.ts +31 -0
  93. package/dist/cjs/utils/dynamicImport.d.ts.map +1 -0
  94. package/dist/cjs/utils/dynamicImport.js +64 -0
  95. package/dist/cjs/utils/dynamicImport.js.map +1 -0
  96. package/dist/cjs/utils/packageVersion.d.ts +5 -0
  97. package/dist/cjs/utils/packageVersion.d.ts.map +1 -0
  98. package/dist/cjs/utils/packageVersion.js +25 -0
  99. package/dist/cjs/utils/packageVersion.js.map +1 -0
  100. package/dist/cjs/utils/readPackageFile.d.ts +3 -0
  101. package/dist/cjs/utils/readPackageFile.d.ts.map +1 -0
  102. package/dist/cjs/utils/readPackageFile.js +16 -0
  103. package/dist/cjs/utils/readPackageFile.js.map +1 -0
  104. package/dist/cjs/utils/s3Abac.d.ts +3 -0
  105. package/dist/cjs/utils/s3Abac.d.ts.map +1 -0
  106. package/dist/cjs/utils/s3Abac.js +5 -0
  107. package/dist/cjs/utils/s3Abac.js.map +1 -0
  108. package/dist/cjs/utils/stringOrFileArgument.d.ts +9 -0
  109. package/dist/cjs/utils/stringOrFileArgument.d.ts.map +1 -0
  110. package/dist/cjs/utils/stringOrFileArgument.js +25 -0
  111. package/dist/cjs/utils/stringOrFileArgument.js.map +1 -0
  112. package/dist/cjs/utils/sts.d.ts +2 -0
  113. package/dist/cjs/utils/sts.d.ts.map +1 -0
  114. package/dist/cjs/utils/sts.js +9 -0
  115. package/dist/cjs/utils/sts.js.map +1 -0
  116. package/dist/cjs/utils/testUtils.d.ts +56 -0
  117. package/dist/cjs/utils/testUtils.d.ts.map +1 -0
  118. package/dist/cjs/utils/testUtils.js +95 -0
  119. package/dist/cjs/utils/testUtils.js.map +1 -0
  120. package/dist/cjs/utils/workerScript.d.ts +8 -0
  121. package/dist/cjs/utils/workerScript.d.ts.map +1 -0
  122. package/dist/cjs/utils/workerScript.js +23 -0
  123. package/dist/cjs/utils/workerScript.js.map +1 -0
  124. package/dist/cjs/whoCan/WhoCanMainThreadWorker.d.ts +73 -0
  125. package/dist/cjs/whoCan/WhoCanMainThreadWorker.d.ts.map +1 -0
  126. package/dist/cjs/whoCan/WhoCanMainThreadWorker.js +80 -0
  127. package/dist/cjs/whoCan/WhoCanMainThreadWorker.js.map +1 -0
  128. package/dist/cjs/whoCan/WhoCanProcessor.d.ts +400 -0
  129. package/dist/cjs/whoCan/WhoCanProcessor.d.ts.map +1 -0
  130. package/dist/cjs/whoCan/WhoCanProcessor.js +1088 -0
  131. package/dist/cjs/whoCan/WhoCanProcessor.js.map +1 -0
  132. package/dist/cjs/whoCan/WhoCanWorker.d.ts +87 -0
  133. package/dist/cjs/whoCan/WhoCanWorker.d.ts.map +1 -0
  134. package/dist/cjs/whoCan/WhoCanWorker.js +201 -0
  135. package/dist/cjs/whoCan/WhoCanWorker.js.map +1 -0
  136. package/dist/cjs/whoCan/WhoCanWorkerThreadWorker.d.ts +2 -0
  137. package/dist/cjs/whoCan/WhoCanWorkerThreadWorker.d.ts.map +1 -0
  138. package/dist/cjs/whoCan/WhoCanWorkerThreadWorker.js +189 -0
  139. package/dist/cjs/whoCan/WhoCanWorkerThreadWorker.js.map +1 -0
  140. package/dist/cjs/whoCan/principalArnFilter.d.ts +84 -0
  141. package/dist/cjs/whoCan/principalArnFilter.d.ts.map +1 -0
  142. package/dist/cjs/whoCan/principalArnFilter.js +258 -0
  143. package/dist/cjs/whoCan/principalArnFilter.js.map +1 -0
  144. package/dist/cjs/whoCan/principalScope.d.ts +35 -0
  145. package/dist/cjs/whoCan/principalScope.d.ts.map +1 -0
  146. package/dist/cjs/whoCan/principalScope.js +104 -0
  147. package/dist/cjs/whoCan/principalScope.js.map +1 -0
  148. package/dist/cjs/whoCan/requestAnalysis.d.ts +73 -0
  149. package/dist/cjs/whoCan/requestAnalysis.d.ts.map +1 -0
  150. package/dist/cjs/whoCan/requestAnalysis.js +87 -0
  151. package/dist/cjs/whoCan/requestAnalysis.js.map +1 -0
  152. package/dist/cjs/whoCan/untrustingActions.d.ts +7 -0
  153. package/dist/cjs/whoCan/untrustingActions.d.ts.map +1 -0
  154. package/dist/cjs/whoCan/untrustingActions.js +30 -0
  155. package/dist/cjs/whoCan/untrustingActions.js.map +1 -0
  156. package/dist/cjs/whoCan/whoCan.d.ts +295 -0
  157. package/dist/cjs/whoCan/whoCan.d.ts.map +1 -0
  158. package/dist/cjs/whoCan/whoCan.js +574 -0
  159. package/dist/cjs/whoCan/whoCan.js.map +1 -0
  160. package/dist/cjs/whoCan/workerBootstrapPlugin.d.ts +17 -0
  161. package/dist/cjs/whoCan/workerBootstrapPlugin.d.ts.map +1 -0
  162. package/dist/cjs/whoCan/workerBootstrapPlugin.js +3 -0
  163. package/dist/cjs/whoCan/workerBootstrapPlugin.js.map +1 -0
  164. package/dist/cjs/workers/ArrayStreamingWorkQueue.d.ts +15 -0
  165. package/dist/cjs/workers/ArrayStreamingWorkQueue.d.ts.map +1 -0
  166. package/dist/cjs/workers/ArrayStreamingWorkQueue.js +35 -0
  167. package/dist/cjs/workers/ArrayStreamingWorkQueue.js.map +1 -0
  168. package/dist/cjs/workers/JobRunner.d.ts +47 -0
  169. package/dist/cjs/workers/JobRunner.d.ts.map +1 -0
  170. package/dist/cjs/workers/JobRunner.js +101 -0
  171. package/dist/cjs/workers/JobRunner.js.map +1 -0
  172. package/dist/cjs/workers/RingQueue.d.ts +14 -0
  173. package/dist/cjs/workers/RingQueue.d.ts.map +1 -0
  174. package/dist/cjs/workers/RingQueue.js +43 -0
  175. package/dist/cjs/workers/RingQueue.js.map +1 -0
  176. package/dist/cjs/workers/SharedArrayBufferMainCache.d.ts +10 -0
  177. package/dist/cjs/workers/SharedArrayBufferMainCache.d.ts.map +1 -0
  178. package/dist/cjs/workers/SharedArrayBufferMainCache.js +37 -0
  179. package/dist/cjs/workers/SharedArrayBufferMainCache.js.map +1 -0
  180. package/dist/cjs/workers/SharedArrayBufferWorkerCache.d.ts +10 -0
  181. package/dist/cjs/workers/SharedArrayBufferWorkerCache.d.ts.map +1 -0
  182. package/dist/cjs/workers/SharedArrayBufferWorkerCache.js +49 -0
  183. package/dist/cjs/workers/SharedArrayBufferWorkerCache.js.map +1 -0
  184. package/dist/cjs/workers/StreamingWorkQueue.d.ts +18 -0
  185. package/dist/cjs/workers/StreamingWorkQueue.d.ts.map +1 -0
  186. package/dist/cjs/workers/StreamingWorkQueue.js +67 -0
  187. package/dist/cjs/workers/StreamingWorkQueue.js.map +1 -0
  188. package/dist/cjs/workers/buffers.d.ts +4 -0
  189. package/dist/cjs/workers/buffers.d.ts.map +1 -0
  190. package/dist/cjs/workers/buffers.js +26 -0
  191. package/dist/cjs/workers/buffers.js.map +1 -0
  192. package/dist/esm/cli.d.ts +3 -0
  193. package/dist/esm/cli.d.ts.map +1 -0
  194. package/dist/esm/cli.js +204 -0
  195. package/dist/esm/cli.js.map +1 -0
  196. package/dist/esm/collect/client.d.ts +550 -0
  197. package/dist/esm/collect/client.d.ts.map +1 -0
  198. package/dist/esm/collect/client.js +1114 -0
  199. package/dist/esm/collect/client.js.map +1 -0
  200. package/dist/esm/collect/collect.d.ts +44 -0
  201. package/dist/esm/collect/collect.d.ts.map +1 -0
  202. package/dist/esm/collect/collect.js +51 -0
  203. package/dist/esm/collect/collect.js.map +1 -0
  204. package/dist/esm/collect/inMemoryClient.d.ts +6 -0
  205. package/dist/esm/collect/inMemoryClient.d.ts.map +1 -0
  206. package/dist/esm/collect/inMemoryClient.js +8 -0
  207. package/dist/esm/collect/inMemoryClient.js.map +1 -0
  208. package/dist/esm/index.d.ts +13 -0
  209. package/dist/esm/index.d.ts.map +1 -0
  210. package/dist/esm/index.js +11 -0
  211. package/dist/esm/index.js.map +1 -0
  212. package/dist/esm/package.json +3 -0
  213. package/dist/esm/principalCan/permission.d.ts +125 -0
  214. package/dist/esm/principalCan/permission.d.ts.map +1 -0
  215. package/dist/esm/principalCan/permission.js +1212 -0
  216. package/dist/esm/principalCan/permission.js.map +1 -0
  217. package/dist/esm/principalCan/permissionSet.d.ts +140 -0
  218. package/dist/esm/principalCan/permissionSet.d.ts.map +1 -0
  219. package/dist/esm/principalCan/permissionSet.js +429 -0
  220. package/dist/esm/principalCan/permissionSet.js.map +1 -0
  221. package/dist/esm/principalCan/principalCan.d.ts +26 -0
  222. package/dist/esm/principalCan/principalCan.d.ts.map +1 -0
  223. package/dist/esm/principalCan/principalCan.js +193 -0
  224. package/dist/esm/principalCan/principalCan.js.map +1 -0
  225. package/dist/esm/principalCan/principalConstraints.d.ts +147 -0
  226. package/dist/esm/principalCan/principalConstraints.d.ts.map +1 -0
  227. package/dist/esm/principalCan/principalConstraints.js +456 -0
  228. package/dist/esm/principalCan/principalConstraints.js.map +1 -0
  229. package/dist/esm/principalCan/resources/actions.d.ts +9 -0
  230. package/dist/esm/principalCan/resources/actions.d.ts.map +1 -0
  231. package/dist/esm/principalCan/resources/actions.js +24 -0
  232. package/dist/esm/principalCan/resources/actions.js.map +1 -0
  233. package/dist/esm/principalCan/resources/resourceTypes/iamRoles.d.ts +31 -0
  234. package/dist/esm/principalCan/resources/resourceTypes/iamRoles.d.ts.map +1 -0
  235. package/dist/esm/principalCan/resources/resourceTypes/iamRoles.js +110 -0
  236. package/dist/esm/principalCan/resources/resourceTypes/iamRoles.js.map +1 -0
  237. package/dist/esm/principalCan/resources/resourceTypes/kmsKeys.d.ts +30 -0
  238. package/dist/esm/principalCan/resources/resourceTypes/kmsKeys.d.ts.map +1 -0
  239. package/dist/esm/principalCan/resources/resourceTypes/kmsKeys.js +106 -0
  240. package/dist/esm/principalCan/resources/resourceTypes/kmsKeys.js.map +1 -0
  241. package/dist/esm/principalCan/resources/resourceTypes/s3Buckets.d.ts +18 -0
  242. package/dist/esm/principalCan/resources/resourceTypes/s3Buckets.d.ts.map +1 -0
  243. package/dist/esm/principalCan/resources/resourceTypes/s3Buckets.js +120 -0
  244. package/dist/esm/principalCan/resources/resourceTypes/s3Buckets.js.map +1 -0
  245. package/dist/esm/principalCan/resources/statements.d.ts +27 -0
  246. package/dist/esm/principalCan/resources/statements.d.ts.map +1 -0
  247. package/dist/esm/principalCan/resources/statements.js +117 -0
  248. package/dist/esm/principalCan/resources/statements.js.map +1 -0
  249. package/dist/esm/principalIndex/makePrincipalIndex.d.ts +8 -0
  250. package/dist/esm/principalIndex/makePrincipalIndex.d.ts.map +1 -0
  251. package/dist/esm/principalIndex/makePrincipalIndex.js +104 -0
  252. package/dist/esm/principalIndex/makePrincipalIndex.js.map +1 -0
  253. package/dist/esm/principals.d.ts +54 -0
  254. package/dist/esm/principals.d.ts.map +1 -0
  255. package/dist/esm/principals.js +133 -0
  256. package/dist/esm/principals.js.map +1 -0
  257. package/dist/esm/resources.d.ts +32 -0
  258. package/dist/esm/resources.d.ts.map +1 -0
  259. package/dist/esm/resources.js +69 -0
  260. package/dist/esm/resources.js.map +1 -0
  261. package/dist/esm/simulate/contextKeys.d.ts +68 -0
  262. package/dist/esm/simulate/contextKeys.d.ts.map +1 -0
  263. package/dist/esm/simulate/contextKeys.js +287 -0
  264. package/dist/esm/simulate/contextKeys.js.map +1 -0
  265. package/dist/esm/simulate/simulate.d.ts +72 -0
  266. package/dist/esm/simulate/simulate.d.ts.map +1 -0
  267. package/dist/esm/simulate/simulate.js +295 -0
  268. package/dist/esm/simulate/simulate.js.map +1 -0
  269. package/dist/esm/test-datasets/testClient.d.ts +17 -0
  270. package/dist/esm/test-datasets/testClient.d.ts.map +1 -0
  271. package/dist/esm/test-datasets/testClient.js +56 -0
  272. package/dist/esm/test-datasets/testClient.js.map +1 -0
  273. package/dist/esm/utils/arn.d.ts +22 -0
  274. package/dist/esm/utils/arn.d.ts.map +1 -0
  275. package/dist/esm/utils/arn.js +43 -0
  276. package/dist/esm/utils/arn.js.map +1 -0
  277. package/dist/esm/utils/bitset.d.ts +45 -0
  278. package/dist/esm/utils/bitset.d.ts.map +1 -0
  279. package/dist/esm/utils/bitset.js +141 -0
  280. package/dist/esm/utils/bitset.js.map +1 -0
  281. package/dist/esm/utils/dynamicImport.d.ts +31 -0
  282. package/dist/esm/utils/dynamicImport.d.ts.map +1 -0
  283. package/dist/esm/utils/dynamicImport.js +60 -0
  284. package/dist/esm/utils/dynamicImport.js.map +1 -0
  285. package/dist/esm/utils/packageVersion.d.ts +5 -0
  286. package/dist/esm/utils/packageVersion.d.ts.map +1 -0
  287. package/dist/esm/utils/packageVersion.js +22 -0
  288. package/dist/esm/utils/packageVersion.js.map +1 -0
  289. package/dist/esm/utils/readPackageFile.d.ts +3 -0
  290. package/dist/esm/utils/readPackageFile.d.ts.map +1 -0
  291. package/dist/esm/utils/readPackageFile.js +15 -0
  292. package/dist/esm/utils/readPackageFile.js.map +1 -0
  293. package/dist/esm/utils/readPackageFileEsm.d.ts.map +1 -0
  294. package/dist/esm/utils/s3Abac.d.ts +3 -0
  295. package/dist/esm/utils/s3Abac.d.ts.map +1 -0
  296. package/dist/esm/utils/s3Abac.js +2 -0
  297. package/dist/esm/utils/s3Abac.js.map +1 -0
  298. package/dist/esm/utils/stringOrFileArgument.d.ts +9 -0
  299. package/dist/esm/utils/stringOrFileArgument.d.ts.map +1 -0
  300. package/dist/esm/utils/stringOrFileArgument.js +22 -0
  301. package/dist/esm/utils/stringOrFileArgument.js.map +1 -0
  302. package/dist/esm/utils/sts.d.ts +2 -0
  303. package/dist/esm/utils/sts.d.ts.map +1 -0
  304. package/dist/esm/utils/sts.js +6 -0
  305. package/dist/esm/utils/sts.js.map +1 -0
  306. package/dist/esm/utils/testUtils.d.ts +56 -0
  307. package/dist/esm/utils/testUtils.d.ts.map +1 -0
  308. package/dist/esm/utils/testUtils.js +90 -0
  309. package/dist/esm/utils/testUtils.js.map +1 -0
  310. package/dist/esm/utils/workerScript.d.ts +8 -0
  311. package/dist/esm/utils/workerScript.d.ts.map +1 -0
  312. package/dist/esm/utils/workerScript.js +18 -0
  313. package/dist/esm/utils/workerScript.js.map +1 -0
  314. package/dist/esm/utils/workerScriptEsm.d.ts.map +1 -0
  315. package/dist/esm/whoCan/WhoCanMainThreadWorker.d.ts +73 -0
  316. package/dist/esm/whoCan/WhoCanMainThreadWorker.d.ts.map +1 -0
  317. package/dist/esm/whoCan/WhoCanMainThreadWorker.js +81 -0
  318. package/dist/esm/whoCan/WhoCanMainThreadWorker.js.map +1 -0
  319. package/dist/esm/whoCan/WhoCanProcessor.d.ts +400 -0
  320. package/dist/esm/whoCan/WhoCanProcessor.d.ts.map +1 -0
  321. package/dist/esm/whoCan/WhoCanProcessor.js +1080 -0
  322. package/dist/esm/whoCan/WhoCanProcessor.js.map +1 -0
  323. package/dist/esm/whoCan/WhoCanWorker.d.ts +87 -0
  324. package/dist/esm/whoCan/WhoCanWorker.d.ts.map +1 -0
  325. package/dist/esm/whoCan/WhoCanWorker.js +198 -0
  326. package/dist/esm/whoCan/WhoCanWorker.js.map +1 -0
  327. package/dist/esm/whoCan/WhoCanWorkerThreadWorker.d.ts +2 -0
  328. package/dist/esm/whoCan/WhoCanWorkerThreadWorker.d.ts.map +1 -0
  329. package/dist/esm/whoCan/WhoCanWorkerThreadWorker.js +193 -0
  330. package/dist/esm/whoCan/WhoCanWorkerThreadWorker.js.map +1 -0
  331. package/dist/esm/whoCan/principalArnFilter.d.ts +84 -0
  332. package/dist/esm/whoCan/principalArnFilter.d.ts.map +1 -0
  333. package/dist/esm/whoCan/principalArnFilter.js +253 -0
  334. package/dist/esm/whoCan/principalArnFilter.js.map +1 -0
  335. package/dist/esm/whoCan/principalScope.d.ts +35 -0
  336. package/dist/esm/whoCan/principalScope.d.ts.map +1 -0
  337. package/dist/esm/whoCan/principalScope.js +102 -0
  338. package/dist/esm/whoCan/principalScope.js.map +1 -0
  339. package/dist/esm/whoCan/requestAnalysis.d.ts +73 -0
  340. package/dist/esm/whoCan/requestAnalysis.d.ts.map +1 -0
  341. package/dist/esm/whoCan/requestAnalysis.js +83 -0
  342. package/dist/esm/whoCan/requestAnalysis.js.map +1 -0
  343. package/dist/esm/whoCan/untrustingActions.d.ts +7 -0
  344. package/dist/esm/whoCan/untrustingActions.d.ts.map +1 -0
  345. package/dist/esm/whoCan/untrustingActions.js +27 -0
  346. package/dist/esm/whoCan/untrustingActions.js.map +1 -0
  347. package/dist/esm/whoCan/whoCan.d.ts +295 -0
  348. package/dist/esm/whoCan/whoCan.d.ts.map +1 -0
  349. package/dist/esm/whoCan/whoCan.js +571 -0
  350. package/dist/esm/whoCan/whoCan.js.map +1 -0
  351. package/dist/esm/whoCan/workerBootstrapPlugin.d.ts +17 -0
  352. package/dist/esm/whoCan/workerBootstrapPlugin.d.ts.map +1 -0
  353. package/dist/esm/whoCan/workerBootstrapPlugin.js +2 -0
  354. package/dist/esm/whoCan/workerBootstrapPlugin.js.map +1 -0
  355. package/dist/esm/workers/ArrayStreamingWorkQueue.d.ts +15 -0
  356. package/dist/esm/workers/ArrayStreamingWorkQueue.d.ts.map +1 -0
  357. package/dist/esm/workers/ArrayStreamingWorkQueue.js +31 -0
  358. package/dist/esm/workers/ArrayStreamingWorkQueue.js.map +1 -0
  359. package/dist/esm/workers/JobRunner.d.ts +47 -0
  360. package/dist/esm/workers/JobRunner.d.ts.map +1 -0
  361. package/dist/esm/workers/JobRunner.js +94 -0
  362. package/dist/esm/workers/JobRunner.js.map +1 -0
  363. package/dist/esm/workers/RingQueue.d.ts +14 -0
  364. package/dist/esm/workers/RingQueue.d.ts.map +1 -0
  365. package/dist/esm/workers/RingQueue.js +36 -0
  366. package/dist/esm/workers/RingQueue.js.map +1 -0
  367. package/dist/esm/workers/SharedArrayBufferMainCache.d.ts +10 -0
  368. package/dist/esm/workers/SharedArrayBufferMainCache.d.ts.map +1 -0
  369. package/dist/esm/workers/SharedArrayBufferMainCache.js +34 -0
  370. package/dist/esm/workers/SharedArrayBufferMainCache.js.map +1 -0
  371. package/dist/esm/workers/SharedArrayBufferWorkerCache.d.ts +10 -0
  372. package/dist/esm/workers/SharedArrayBufferWorkerCache.d.ts.map +1 -0
  373. package/dist/esm/workers/SharedArrayBufferWorkerCache.js +46 -0
  374. package/dist/esm/workers/SharedArrayBufferWorkerCache.js.map +1 -0
  375. package/dist/esm/workers/StreamingWorkQueue.d.ts +18 -0
  376. package/dist/esm/workers/StreamingWorkQueue.d.ts.map +1 -0
  377. package/dist/esm/workers/StreamingWorkQueue.js +61 -0
  378. package/dist/esm/workers/StreamingWorkQueue.js.map +1 -0
  379. package/dist/esm/workers/buffers.d.ts +4 -0
  380. package/dist/esm/workers/buffers.d.ts.map +1 -0
  381. package/dist/esm/workers/buffers.js +21 -0
  382. package/dist/esm/workers/buffers.js.map +1 -0
  383. package/package.json +132 -0
@@ -0,0 +1,1212 @@
1
+ import { intersectPrincipalConstraints, normalizePrincipalConstraint, principalArgsForConstraint, principalConstraintsOverlap, principalIncludes, subtractPrincipalConstraint, unionPrincipalConstraints } from './principalConstraints.js';
2
+ /**
3
+ * Convert an AWS wildcard ARN pattern (e.g. "arn:aws:s3:::bucket/*") into a RegExp.
4
+ */
5
+ function wildcardToRegex(pattern) {
6
+ const parts = pattern.split('*').map((s) => s.replace(/[-/\\^$+?.()|[\]{}]/g, '\\$&'));
7
+ return new RegExp('^' + parts.join('.*') + '$');
8
+ }
9
+ /**
10
+ * An immutable representation of a single permission for a specific action.
11
+ *
12
+ * This will eventually have methods like "merge with another permission",
13
+ * "check if overlaps with another permission", "subtract a deny permission",
14
+ * etc and those will all return a new Permission instance.
15
+ */
16
+ export class Permission {
17
+ constructor(effect, service, action, resource, notResource, conditions, principal = undefined, notPrincipal = undefined) {
18
+ this.effect = effect;
19
+ this.service = service;
20
+ this.action = action;
21
+ this.resource = resource;
22
+ this.notResource = notResource;
23
+ this.conditions = conditions;
24
+ this.principal = principal;
25
+ this.notPrincipal = notPrincipal;
26
+ if (resource !== undefined && notResource !== undefined) {
27
+ throw new Error('Permission must have a resource or notResource, not both.');
28
+ }
29
+ else if (resource === undefined && notResource === undefined) {
30
+ throw new Error('Permission must have a resource or notResource, one must be defined.');
31
+ }
32
+ normalizePrincipalConstraint(principal, notPrincipal);
33
+ }
34
+ /**
35
+ * Return this permission's principal constraint normalized for set algebra.
36
+ *
37
+ * @returns The normalized principal constraint.
38
+ */
39
+ principalConstraint() {
40
+ return normalizePrincipalConstraint(this.principal, this.notPrincipal);
41
+ }
42
+ /**
43
+ * Returns true if this Permission completely includes the other Permission.
44
+ * Only supports merging of "Allow" permissions (same effect, service, action).
45
+ */
46
+ includes(other) {
47
+ // 1. Effects, service, and action must match
48
+ if (this.effect !== other.effect ||
49
+ this.service !== other.service ||
50
+ this.action !== other.action) {
51
+ return false;
52
+ }
53
+ if (!principalIncludes(this.principalConstraint(), other.principalConstraint())) {
54
+ return false;
55
+ }
56
+ // 2. Conditions: every condition in this must be implied by the other permission’s conditions
57
+ // That is, for each operator and context key in this.conditions, other.conditions must have it,
58
+ // and the values must satisfy inclusion logic per operator.
59
+ const condsA = normalizeConditionKeys(this.conditions || {});
60
+ const condsB = normalizeConditionKeys(other.conditions || {});
61
+ for (const op of Object.keys(condsA)) {
62
+ if (!(op in condsB))
63
+ return false;
64
+ const keysA = Object.keys(condsA[op]);
65
+ const keysB = Object.keys(condsB[op]);
66
+ // Every key in A must appear in B
67
+ for (const key of keysA) {
68
+ if (!keysB.includes(key))
69
+ return false;
70
+ const valsA = condsA[op][key];
71
+ const valsB = condsB[op][key];
72
+ const baseOp = conditionBaseOperator(op);
73
+ switch (baseOp) {
74
+ case 'stringequals':
75
+ case 'stringlike':
76
+ case 'arnequals':
77
+ case 'arnlike':
78
+ // other must be at least as restrictive: B_vals ⊆ A_vals
79
+ if (!valsB.every((v) => valsA.includes(v)))
80
+ return false;
81
+ break;
82
+ case 'stringnotequals':
83
+ case 'stringnotlike':
84
+ case 'arnnotequals':
85
+ case 'arnnotlike':
86
+ // other must exclude at least what A excludes: A_vals ⊆ B_vals
87
+ if (!valsA.every((v) => valsB.includes(v)))
88
+ return false;
89
+ break;
90
+ case 'numericlessthan':
91
+ case 'numericlessthanequals':
92
+ // other boundary <= this boundary
93
+ const numA = Number(valsA[0]);
94
+ const numB = Number(valsB[0]);
95
+ if (isNaN(numA) || isNaN(numB))
96
+ return false;
97
+ if (numB > numA)
98
+ return false;
99
+ break;
100
+ case 'numericgreaterthan':
101
+ case 'numericgreaterthanequals':
102
+ // other boundary >= this boundary
103
+ const ngA = Number(valsA[0]);
104
+ const ngB = Number(valsB[0]);
105
+ if (isNaN(ngA) || isNaN(ngB))
106
+ return false;
107
+ if (ngB < ngA)
108
+ return false;
109
+ break;
110
+ case 'bool':
111
+ // other must have the same boolean value
112
+ if (valsA[0] !== valsB[0])
113
+ return false;
114
+ break;
115
+ case 'ipaddress':
116
+ case 'notipaddress':
117
+ // every CIDR in B must be contained in some CIDR in A
118
+ for (const cidrB of valsB) {
119
+ if (!valsA.some((cidrA) => cidrA === cidrB)) {
120
+ return false;
121
+ }
122
+ }
123
+ break;
124
+ case 'datelessthan':
125
+ case 'datelessthanequals':
126
+ // other date <= this date lexically (ISO)
127
+ const dA = valsA[0];
128
+ const dB = valsB[0];
129
+ if (dB > dA)
130
+ return false;
131
+ break;
132
+ case 'dategreaterthan':
133
+ case 'dategreaterthanequals':
134
+ // other date >= this date
135
+ const dgA = valsA[0];
136
+ const dgB = valsB[0];
137
+ if (dgB < dgA)
138
+ return false;
139
+ break;
140
+ default:
141
+ return false;
142
+ }
143
+ }
144
+ }
145
+ // 3. Resources / NotResources
146
+ const thisResource = this.resource;
147
+ const thisNotResource = this.notResource;
148
+ const otherResource = other.resource;
149
+ const otherNotResource = other.notResource;
150
+ // 3a. If both have resource[]
151
+ if (thisResource !== undefined && otherResource !== undefined) {
152
+ return otherResource.every((r2) => thisResource.some((r1) => wildcardToRegex(r1).test(r2)));
153
+ }
154
+ // 3b. Both have notResource[]
155
+ if (thisNotResource !== undefined && otherNotResource !== undefined) {
156
+ return thisNotResource.every((n1) => otherNotResource.some((n2) => wildcardToRegex(n1).test(n2)));
157
+ }
158
+ // 3c. A.resource & B.notResource -> B allows almost all, A allows only R1 -> true iff every N2 is matched by some R1
159
+ if (thisResource !== undefined && otherNotResource !== undefined) {
160
+ return otherNotResource.every((n2) => thisResource.some((r1) => wildcardToRegex(r1).test(n2)));
161
+ }
162
+ // 3d. A.notResource & B.resource -> every r2 ∉ N1
163
+ if (thisNotResource !== undefined && otherResource !== undefined) {
164
+ return otherResource.every((r2) => !thisNotResource.some((n1) => wildcardToRegex(n1).test(r2)));
165
+ }
166
+ return false;
167
+ }
168
+ /**
169
+ * Returns the union of this Permission with another.
170
+ * If one includes the other, return the including Permission.
171
+ * Otherwise, attempt to merge conditions and resource/notResource.
172
+ * If merge yields a single Permission, return it; else return both.
173
+ */
174
+ union(other) {
175
+ // 1. Ensure same effect, service, and action
176
+ if (this.effect !== other.effect ||
177
+ this.service !== other.service ||
178
+ this.action !== other.action) {
179
+ return [this, other];
180
+ }
181
+ // 2. If one includes the other, return the including one
182
+ if (this.includes(other)) {
183
+ return [this];
184
+ }
185
+ if (other.includes(this)) {
186
+ return [other];
187
+ }
188
+ // 3. Attempt to combine conditions
189
+ const condsA = this.conditions || {};
190
+ const condsB = other.conditions || {};
191
+ const mergedConds = unionConditions(condsA, condsB);
192
+ if (mergedConds === null) {
193
+ return [this, other];
194
+ }
195
+ const principalUnions = unionPrincipalConstraints(this.principalConstraint(), other.principalConstraint());
196
+ if (principalUnions.length !== 1) {
197
+ return [this, other];
198
+ }
199
+ const principalArgs = principalArgsForConstraint(principalUnions[0]);
200
+ // 4. Combine resource/notResource (constructor enforces exclusivity)
201
+ const thisResource = this.resource;
202
+ const thisNotResource = this.notResource;
203
+ const otherResource = other.resource;
204
+ const otherNotResource = other.notResource;
205
+ const eff = this.effect;
206
+ const svc = this.service;
207
+ const act = this.action;
208
+ const conds = Object.keys(mergedConds).length > 0 ? mergedConds : undefined;
209
+ // Both have resource[]
210
+ if (thisResource !== undefined && otherResource !== undefined) {
211
+ const union = Array.from(new Set([...thisResource, ...otherResource]));
212
+ return [
213
+ new Permission(eff, svc, act, union, undefined, conds, principalArgs.principal, principalArgs.notPrincipal)
214
+ ];
215
+ }
216
+ // Both have notResource[]
217
+ if (thisNotResource !== undefined && otherNotResource !== undefined) {
218
+ // Intersection of both notResource arrays
219
+ const intersection = thisNotResource.filter((n) => otherNotResource.includes(n));
220
+ return [
221
+ new Permission(eff, svc, act, undefined, intersection, conds, principalArgs.principal, principalArgs.notPrincipal)
222
+ ];
223
+ }
224
+ // One has resource, other has notResource
225
+ if (thisResource !== undefined && otherNotResource !== undefined) {
226
+ return [
227
+ new Permission(eff, svc, act, thisResource, undefined, conds, principalArgs.principal, principalArgs.notPrincipal),
228
+ new Permission(eff, svc, act, undefined, otherNotResource, conds, principalArgs.principal, principalArgs.notPrincipal)
229
+ ];
230
+ }
231
+ if (otherResource !== undefined && thisNotResource !== undefined) {
232
+ return [
233
+ new Permission(eff, svc, act, otherResource, undefined, conds, principalArgs.principal, principalArgs.notPrincipal),
234
+ new Permission(eff, svc, act, undefined, thisNotResource, conds, principalArgs.principal, principalArgs.notPrincipal)
235
+ ];
236
+ }
237
+ // Otherwise cannot combine, return both
238
+ return [this, other];
239
+ }
240
+ /**
241
+ * Returns all representable intersections of this Permission with another.
242
+ *
243
+ * @param other The other Permission to intersect with.
244
+ * @returns New Permissions representing the overlap, or an empty array if there is no intersection.
245
+ */
246
+ intersections(other) {
247
+ const principalIntersections = intersectPrincipalConstraints(this.principalConstraint(), other.principalConstraint());
248
+ const results = [];
249
+ for (const principalIntersection of principalIntersections) {
250
+ const principalArgs = principalArgsForConstraint(principalIntersection);
251
+ const permissionWithIntersectionPrincipal = new Permission(this.effect, this.service, this.action, this.resource, this.notResource, this.conditions, principalArgs.principal, principalArgs.notPrincipal);
252
+ const intersection = permissionWithIntersectionPrincipal.intersection(other);
253
+ if (intersection) {
254
+ results.push(intersection);
255
+ }
256
+ }
257
+ return results;
258
+ }
259
+ /**
260
+ * Returns one intersection of this Permission with another.
261
+ *
262
+ * Prefer {@link intersections} when correctness depends on all possible overlaps. This
263
+ * compatibility method returns at most one Permission and may omit additional representable
264
+ * intersections if future principal algebra produces multiple principal residuals.
265
+ *
266
+ * @param other The other Permission to intersect with.
267
+ * @returns A new Permission representing one intersection of other and this, or undefined if there is no intersection.
268
+ */
269
+ intersection(other) {
270
+ // 1. Must match effect, service, and action
271
+ if (this.effect !== other.effect ||
272
+ this.service !== other.service ||
273
+ this.action !== other.action) {
274
+ // No overlap at all—return a "zero-resource" permission
275
+ return undefined;
276
+ }
277
+ const principalIntersections = intersectPrincipalConstraints(this.principalConstraint(), other.principalConstraint());
278
+ if (principalIntersections.length === 0) {
279
+ return undefined;
280
+ }
281
+ const principalArgs = principalArgsForConstraint(principalIntersections[0]);
282
+ if (this.resource != undefined && other.resource != undefined) {
283
+ // 2. If one includes the other, return the narrower one unless both are NotResource
284
+ if (this.includes(other)) {
285
+ return other;
286
+ }
287
+ if (other.includes(this)) {
288
+ return this;
289
+ }
290
+ }
291
+ // 3. Attempt to intersect/merge conditions
292
+ const a = normalizeConditionKeys(this.conditions || {});
293
+ const b = normalizeConditionKeys(other.conditions || {});
294
+ const allOps = Array.from(new Set([...Object.keys(a), ...Object.keys(b)]));
295
+ const intersectedConds = {};
296
+ for (const op of allOps) {
297
+ const condA = a[op] || {};
298
+ const condB = b[op] || {};
299
+ const allKeys = Array.from(new Set([...Object.keys(condA), ...Object.keys(condB)]));
300
+ intersectedConds[op] = {};
301
+ for (const key of allKeys) {
302
+ const valsA = condA[key] || [];
303
+ const valsB = condB[key] || [];
304
+ // If key appears in both, intersect or combine based on operator
305
+ if (key in condA && key in condB) {
306
+ switch (conditionBaseOperator(op)) {
307
+ case 'stringequals':
308
+ case 'stringlike':
309
+ case 'arnequals':
310
+ case 'arnlike': {
311
+ // Intersection of string lists
312
+ const common = valsA.filter((v) => valsB.includes(v));
313
+ if (common.length === 0) {
314
+ return undefined;
315
+ }
316
+ intersectedConds[op][key] = common;
317
+ break;
318
+ }
319
+ case 'stringnotequals':
320
+ case 'stringnotlike':
321
+ case 'arnnotequals':
322
+ case 'arnnotlike': {
323
+ // Union of exclusions
324
+ intersectedConds[op][key] = Array.from(new Set([...valsA, ...valsB]));
325
+ break;
326
+ }
327
+ case 'numericlessthan':
328
+ case 'numericlessthanequals': {
329
+ const numA = Number(valsA[0]);
330
+ const numB = Number(valsB[0]);
331
+ if (isNaN(numA) || isNaN(numB)) {
332
+ return undefined;
333
+ }
334
+ const boundary = Math.min(numA, numB);
335
+ intersectedConds[op][key] = [String(boundary)];
336
+ break;
337
+ }
338
+ case 'numericgreaterthan':
339
+ case 'numericgreaterthanequals': {
340
+ const ngA = Number(valsA[0]);
341
+ const ngB = Number(valsB[0]);
342
+ if (isNaN(ngA) || isNaN(ngB)) {
343
+ return undefined;
344
+ }
345
+ const boundary = Math.max(ngA, ngB);
346
+ intersectedConds[op][key] = [String(boundary)];
347
+ break;
348
+ }
349
+ case 'bool': {
350
+ if (valsA[0] !== valsB[0]) {
351
+ return undefined;
352
+ }
353
+ intersectedConds[op][key] = [valsA[0]];
354
+ break;
355
+ }
356
+ case 'ipaddress':
357
+ case 'notipaddress': {
358
+ const common = valsA.filter((cidr) => valsB.includes(cidr));
359
+ if (common.length === 0) {
360
+ return undefined;
361
+ }
362
+ intersectedConds[op][key] = common;
363
+ break;
364
+ }
365
+ case 'datelessthan':
366
+ case 'datelessthanequals': {
367
+ const dA = valsA[0];
368
+ const dB = valsB[0];
369
+ intersectedConds[op][key] = [dA < dB ? dA : dB];
370
+ break;
371
+ }
372
+ case 'dategreaterthan':
373
+ case 'dategreaterthanequals': {
374
+ const dgA = valsA[0];
375
+ const dgB = valsB[0];
376
+ intersectedConds[op][key] = [dgA > dgB ? dgA : dgB];
377
+ break;
378
+ }
379
+ default:
380
+ return undefined;
381
+ }
382
+ }
383
+ else {
384
+ // Key only in one side: carry it through
385
+ intersectedConds[op][key] = key in condA ? Array.from(valsA) : Array.from(valsB);
386
+ }
387
+ }
388
+ }
389
+ // 4. Combine resource/notResource:
390
+ const thisResource = this.resource;
391
+ const thisNotResource = this.notResource;
392
+ const otherResource = other.resource;
393
+ const otherNotResource = other.notResource;
394
+ const eff = this.effect;
395
+ const svc = this.service;
396
+ const act = this.action;
397
+ const conds = Object.keys(intersectedConds).length > 0 ? intersectedConds : undefined;
398
+ // Both have resource[] => intersect patterns
399
+ if (thisResource !== undefined && otherResource !== undefined) {
400
+ // Keep any R1 that matches something in R2, and any R2 that matches something in R1
401
+ const part1 = thisResource.filter((r1) => otherResource.some((r2) => wildcardToRegex(r2).test(r1)));
402
+ const part2 = otherResource.filter((r2) => thisResource.some((r1) => wildcardToRegex(r1).test(r2)));
403
+ const intersectR = Array.from(new Set([...part1, ...part2]));
404
+ if (intersectR.length === 0) {
405
+ return undefined;
406
+ }
407
+ return new Permission(eff, svc, act, intersectR, undefined, conds, principalArgs.principal, principalArgs.notPrincipal);
408
+ }
409
+ // Both have notResource[] => union of exclusions (more restrictive), but remove subsumed patterns
410
+ if (thisNotResource !== undefined && otherNotResource !== undefined) {
411
+ // Compute union of both exclusion lists
412
+ const combined = Array.from(new Set([...thisNotResource, ...otherNotResource]));
413
+ // Remove any pattern that is subsumed by a more general pattern
414
+ const filtered = combined.filter((pat) => !combined.some((otherPat) => otherPat !== pat && wildcardToRegex(otherPat).test(pat)));
415
+ return new Permission(eff, svc, act, undefined, filtered, conds, principalArgs.principal, principalArgs.notPrincipal);
416
+ }
417
+ // One has resource, other has notResource
418
+ const resource = thisResource || otherResource;
419
+ const notResource = thisNotResource || otherNotResource;
420
+ if (resource !== undefined || notResource !== undefined) {
421
+ const filtered = resource.filter((r1) => !notResource.some((n2) => wildcardToRegex(n2).test(r1)));
422
+ if (filtered.length === 0) {
423
+ return undefined;
424
+ }
425
+ return new Permission(eff, svc, act, filtered, undefined, conds, principalArgs.principal, principalArgs.notPrincipal);
426
+ }
427
+ // This should never happen
428
+ return undefined;
429
+ }
430
+ /**
431
+ * Subtract a Deny permission from this Allow permission.
432
+ *
433
+ * Returns the resulting permissions, this can be:
434
+ * - An empty array if the Allow is fully denied by the Deny
435
+ * - A modified Allow permission or multiple Allow permissions
436
+ * - It could also return the original Allow and Deny permission if subtraction cannot be expressed purely in Allow statements
437
+ *
438
+ * @param other the Deny permission to subtract
439
+ */
440
+ subtract(other) {
441
+ // Only subtract Deny from Allow for the same service/action
442
+ if (this.effect !== 'Allow' ||
443
+ other.effect !== 'Deny' ||
444
+ this.service !== other.service ||
445
+ this.action !== other.action) {
446
+ // No subtraction applies
447
+ return [this];
448
+ }
449
+ const allowCondsNorm = normalizeConditionKeys(this.conditions || {});
450
+ const denyCondsNorm = normalizeConditionKeys(other.conditions || {});
451
+ const conditionsMatch = JSON.stringify(allowCondsNorm) === JSON.stringify(denyCondsNorm);
452
+ const allowPrincipalConstraint = this.principalConstraint();
453
+ const denyPrincipalConstraint = other.principalConstraint();
454
+ if (!principalConstraintsOverlap(allowPrincipalConstraint, denyPrincipalConstraint)) {
455
+ return [this];
456
+ }
457
+ const allowResource = this.resource;
458
+ const allowNotResource = this.notResource;
459
+ const denyResource = other.resource;
460
+ const denyNotResource = other.notResource;
461
+ const eff = this.effect;
462
+ const svc = this.service;
463
+ const act = this.action;
464
+ const principalResiduals = subtractPrincipalConstraint(allowPrincipalConstraint, denyPrincipalConstraint);
465
+ const sameResourceShape = JSON.stringify([...(allowResource ?? [])].sort()) ===
466
+ JSON.stringify([...(denyResource ?? [])].sort()) &&
467
+ JSON.stringify([...(allowNotResource ?? [])].sort()) ===
468
+ JSON.stringify([...(denyNotResource ?? [])].sort());
469
+ const denyFullyCoversPrincipal = principalIncludes(denyPrincipalConstraint, allowPrincipalConstraint);
470
+ const allowPrincipalArgs = principalArgsForConstraint(allowPrincipalConstraint);
471
+ const denyPrincipalArgs = principalArgsForConstraint(denyPrincipalConstraint);
472
+ if (!denyFullyCoversPrincipal && sameResourceShape && conditionsMatch) {
473
+ const principalResidualsEqualAllow = principalResiduals.length === 1 &&
474
+ JSON.stringify(principalArgsForConstraint(principalResiduals[0])) ===
475
+ JSON.stringify(allowPrincipalArgs);
476
+ if (principalResidualsEqualAllow) {
477
+ return [this, other];
478
+ }
479
+ return principalResiduals.map((constraint) => {
480
+ const args = principalArgsForConstraint(constraint);
481
+ return new Permission(eff, svc, act, allowResource, allowNotResource, this.conditions, args.principal, args.notPrincipal);
482
+ });
483
+ }
484
+ if (!denyFullyCoversPrincipal) {
485
+ return [this, other];
486
+ }
487
+ // Case: Allow.resource & Deny.resource
488
+ if (allowResource !== undefined && denyResource !== undefined) {
489
+ const overlappingResources = allowResource.some((a) => {
490
+ return denyResource.some((d) => {
491
+ return wildcardToRegex(d).test(a) || wildcardToRegex(a).test(d);
492
+ });
493
+ });
494
+ // If the resources in the allow and deny do not overlap, return the allow as is
495
+ if (!overlappingResources) {
496
+ return [this];
497
+ }
498
+ // Categories for allows, a single allow could be more than one, because the deny could have multiple
499
+ // Without Conditions:
500
+ //1. Exactly the same as a deny - remove the allow
501
+ //2. A subset of a deny - remove the allow
502
+ //3. A superset of a deny - keep the allow and the deny
503
+ //4. No overlap with any deny - keep the allow as is
504
+ //
505
+ // With Conditions:
506
+ //1. Exactly the same as a deny - invert the conditions and keep the allow
507
+ //2. A subset of a deny - invert the conditions and keep the allow
508
+ //3. A superset of a deny - keep the allow and the deny
509
+ //4. No overlap with any deny - keep the allow as is
510
+ const allowMatches = [];
511
+ const allowSubsets = [];
512
+ const allowSupersets = [];
513
+ const allowNoOverlap = [];
514
+ const denySubsets = [];
515
+ for (const allowedResource of allowResource) {
516
+ let isMatch = false;
517
+ let isSubset = false;
518
+ let isSuperset = false;
519
+ for (const deniedResource of denyResource) {
520
+ if (deniedResource === allowedResource) {
521
+ isMatch = true;
522
+ break;
523
+ }
524
+ if (wildcardToRegex(deniedResource).test(allowedResource)) {
525
+ isSubset = true;
526
+ break;
527
+ }
528
+ if (wildcardToRegex(allowedResource).test(deniedResource)) {
529
+ isSuperset = true;
530
+ denySubsets.push(deniedResource);
531
+ }
532
+ }
533
+ if (isMatch) {
534
+ allowMatches.push(allowedResource);
535
+ }
536
+ else if (isSubset) {
537
+ allowSubsets.push(allowedResource);
538
+ }
539
+ else if (isSuperset) {
540
+ allowSupersets.push(allowedResource);
541
+ }
542
+ else {
543
+ allowNoOverlap.push(allowedResource);
544
+ }
545
+ }
546
+ const permissionsToReturn = [];
547
+ if (allowNoOverlap.length > 0) {
548
+ permissionsToReturn.push(new Permission(eff, svc, act, allowNoOverlap, undefined, this.conditions, allowPrincipalArgs.principal, allowPrincipalArgs.notPrincipal));
549
+ }
550
+ if (allowSupersets.length > 0) {
551
+ permissionsToReturn.push(new Permission(eff, svc, act, allowSupersets, undefined, this.conditions, allowPrincipalArgs.principal, allowPrincipalArgs.notPrincipal));
552
+ }
553
+ if (allowMatches.length > 0 || allowSubsets.length > 0) {
554
+ // If conditions are identical, these are fully dropped from the Allow. If not, they need to be kept with inverted conditions
555
+ if (!conditionsMatch) {
556
+ const newAllow = new Permission(eff, svc, act, [...allowMatches, ...allowSubsets], undefined, this.conditions, allowPrincipalArgs.principal, allowPrincipalArgs.notPrincipal);
557
+ permissionsToReturn.push(...applyDenyConditionsToAllow(newAllow, other));
558
+ }
559
+ }
560
+ if (denySubsets.length > 0) {
561
+ permissionsToReturn.push(new Permission('Deny', svc, act, denySubsets, undefined, other.conditions, denyPrincipalArgs.principal, denyPrincipalArgs.notPrincipal));
562
+ }
563
+ return permissionsToReturn;
564
+ }
565
+ // Case: Allow.resource & Deny.notResource
566
+ // =======================================================================
567
+ // SEMANTICS:
568
+ // Deny.notResource means: "deny everything EXCEPT these patterns"
569
+ // So the deny APPLIES to resources that do NOT match denyNotResource patterns
570
+ // And the deny does NOT apply to resources that DO match denyNotResource patterns
571
+ // =======================================================================
572
+ if (allowResource !== undefined && denyNotResource !== undefined) {
573
+ // STEP 1: Categorize each allow resource based on relationship to denyNotResource patterns
574
+ //
575
+ // Categories:
576
+ // ExcludedFromDeny - Matches a denyNotResource pattern (deny doesn't apply to these)
577
+ // AffectedByDeny - Does NOT match any denyNotResource (deny applies to these)
578
+ // Superset - Covers (is broader than) a denyNotResource pattern
579
+ //
580
+ // Also track which denyNotResource patterns are covered by superset allow resources
581
+ const excludedFromDeny = [];
582
+ const affectedByDeny = [];
583
+ const supersets = [];
584
+ const coveredDenyNotResourcePatterns = [];
585
+ for (const allowedResource of allowResource) {
586
+ let isExcluded = false;
587
+ let isSuperset = false;
588
+ for (const deniedNotResource of denyNotResource) {
589
+ // Check if allowResource exactly matches or is covered by denyNotResource pattern
590
+ if (allowedResource === deniedNotResource ||
591
+ wildcardToRegex(deniedNotResource).test(allowedResource)) {
592
+ isExcluded = true;
593
+ break;
594
+ }
595
+ // Check if allowResource covers (is broader than) denyNotResource pattern
596
+ if (wildcardToRegex(allowedResource).test(deniedNotResource)) {
597
+ isSuperset = true;
598
+ if (!coveredDenyNotResourcePatterns.includes(deniedNotResource)) {
599
+ coveredDenyNotResourcePatterns.push(deniedNotResource);
600
+ }
601
+ }
602
+ }
603
+ if (isExcluded) {
604
+ excludedFromDeny.push(allowedResource);
605
+ }
606
+ else if (isSuperset) {
607
+ supersets.push(allowedResource);
608
+ }
609
+ else {
610
+ affectedByDeny.push(allowedResource);
611
+ }
612
+ }
613
+ // STEP 2: Early exit - if all allow resources are excluded from deny, return unchanged
614
+ if (excludedFromDeny.length === allowResource.length) {
615
+ return [this];
616
+ }
617
+ const denyHasConditions = other.conditions && Object.keys(other.conditions).length > 0;
618
+ // STEP 3: Build output permissions by category
619
+ const permissionsToReturn = [];
620
+ // ExcludedFromDeny: Keep as-is with original conditions (deny doesn't touch these)
621
+ if (excludedFromDeny.length > 0) {
622
+ permissionsToReturn.push(new Permission(eff, svc, act, excludedFromDeny, undefined, this.conditions, allowPrincipalArgs.principal, allowPrincipalArgs.notPrincipal));
623
+ }
624
+ // Superset: Allow resource is broader than denyNotResource patterns
625
+ // - The covered denyNotResource patterns are excluded from deny (allow unconditionally)
626
+ // - The superset allow resources are affected by deny (apply inverted conditions)
627
+ if (supersets.length > 0) {
628
+ // First: Allow the covered patterns unconditionally (they're excluded from deny)
629
+ if (coveredDenyNotResourcePatterns.length > 0) {
630
+ permissionsToReturn.push(new Permission(eff, svc, act, coveredDenyNotResourcePatterns, undefined, this.conditions, allowPrincipalArgs.principal, allowPrincipalArgs.notPrincipal));
631
+ }
632
+ // Second: Apply inverted deny conditions to the superset resources
633
+ if (denyHasConditions && !conditionsMatch) {
634
+ const supersetAllow = new Permission(eff, svc, act, supersets, undefined, this.conditions, allowPrincipalArgs.principal, allowPrincipalArgs.notPrincipal);
635
+ permissionsToReturn.push(...applyDenyConditionsToAllow(supersetAllow, other));
636
+ }
637
+ // If no conditions or conditions match, the superset is fully denied (nothing to add)
638
+ }
639
+ // AffectedByDeny: These resources are hit by the deny
640
+ if (affectedByDeny.length > 0) {
641
+ // If there are no conditions on deny - these are fully denied (drop them)
642
+ // If the conditions match - these are fully denied (drop them)
643
+ if (denyHasConditions && !conditionsMatch) {
644
+ // Different conditions - keep with inverted deny conditions
645
+ const newAllow = new Permission(eff, svc, act, affectedByDeny, undefined, this.conditions, allowPrincipalArgs.principal, allowPrincipalArgs.notPrincipal);
646
+ permissionsToReturn.push(...applyDenyConditionsToAllow(newAllow, other));
647
+ }
648
+ }
649
+ return permissionsToReturn;
650
+ }
651
+ // Scenario 3: Allow.notResource & Deny.resource
652
+ if (allowNotResource !== undefined && denyResource !== undefined) {
653
+ // STEP 1: Categorize relationships and track which denyResources are already covered
654
+ const coveredDenyResources = new Set(); // denyResources already excluded by allowNotResource
655
+ const uncoveredDenyResources = []; // denyResources that affect allowed resources
656
+ const subsetReplacements = [];
657
+ // For each denyResource, check if it's covered by any allowNotResource
658
+ for (const denyPattern of denyResource) {
659
+ let isCovered = false;
660
+ for (const allowPattern of allowNotResource) {
661
+ // ExactMatch or Superset - denyResource is already excluded
662
+ if (allowPattern === denyPattern || wildcardToRegex(allowPattern).test(denyPattern)) {
663
+ isCovered = true;
664
+ coveredDenyResources.add(denyPattern);
665
+ break;
666
+ }
667
+ }
668
+ if (!isCovered) {
669
+ uncoveredDenyResources.push(denyPattern);
670
+ }
671
+ }
672
+ // Check for Subset patterns (denyResource covers allowNotResource)
673
+ for (const allowPattern of allowNotResource) {
674
+ for (const denyPattern of denyResource) {
675
+ if (wildcardToRegex(denyPattern).test(allowPattern) && allowPattern !== denyPattern) {
676
+ subsetReplacements.push({ allowPattern, denyPattern });
677
+ }
678
+ }
679
+ }
680
+ // Filter out subset deny patterns from uncoveredDenyResources to get true NoOverlap patterns
681
+ const subsetDenyPatternsSet = new Set(subsetReplacements.map((s) => s.denyPattern));
682
+ const noOverlapDenyResources = uncoveredDenyResources.filter((dr) => !subsetDenyPatternsSet.has(dr));
683
+ // STEP 2: If all denyResources are covered, deny has no effect
684
+ if (noOverlapDenyResources.length === 0 && subsetReplacements.length === 0) {
685
+ return [this];
686
+ }
687
+ // STEP 3: Build output permissions
688
+ const denyHasConditions = other.conditions && Object.keys(other.conditions).length > 0;
689
+ // Build the expanded notResource (original + noOverlap deny resources + subset replacements)
690
+ const subsetAllowPatterns = new Set(subsetReplacements.map((s) => s.allowPattern));
691
+ const keptPatterns = allowNotResource.filter((p) => !subsetAllowPatterns.has(p));
692
+ const subsetDenyPatterns = Array.from(new Set(subsetReplacements.map((s) => s.denyPattern)));
693
+ const expandedNotResource = Array.from(new Set([...keptPatterns, ...subsetDenyPatterns, ...noOverlapDenyResources]));
694
+ // Same conditions or no deny conditions: simply expand notResource
695
+ if (conditionsMatch || !denyHasConditions) {
696
+ return [
697
+ new Permission(eff, svc, act, undefined, expandedNotResource, this.conditions, allowPrincipalArgs.principal, allowPrincipalArgs.notPrincipal)
698
+ ];
699
+ }
700
+ // Different conditions: handle Subset and NoOverlap cases separately
701
+ const permissionsToReturn = [];
702
+ const hasSubsetReplacements = subsetReplacements.length > 0;
703
+ const hasNoOverlapAdditions = noOverlapDenyResources.length > 0;
704
+ // Part 1: Original allowNotResource with inverted deny conditions
705
+ // (when deny condition is NOT met, original allow applies)
706
+ const originalAllow = new Permission(eff, svc, act, undefined, allowNotResource, this.conditions, allowPrincipalArgs.principal, allowPrincipalArgs.notPrincipal);
707
+ permissionsToReturn.push(...applyDenyConditionsToAllow(originalAllow, other));
708
+ // Part 2a: For SUBSET replacements - expanded notResource WITH deny conditions
709
+ // (replacing smaller exclusion with larger one, only valid when condition is met)
710
+ if (hasSubsetReplacements) {
711
+ // Build notResource with just the subset replacements (not the noOverlap additions)
712
+ const subsetExpandedNotResource = Array.from(new Set([...keptPatterns, ...subsetDenyPatterns]));
713
+ const subsetConditions = intersectConditions(this.conditions || {}, other.conditions || {});
714
+ permissionsToReturn.push(new Permission(eff, svc, act, undefined, subsetExpandedNotResource, subsetConditions || other.conditions, allowPrincipalArgs.principal, allowPrincipalArgs.notPrincipal));
715
+ }
716
+ // Part 2b: For NO OVERLAP additions - expanded notResource WITHOUT conditions
717
+ // (adding new exclusion is always safe, no condition needed)
718
+ if (hasNoOverlapAdditions) {
719
+ permissionsToReturn.push(new Permission(eff, svc, act, undefined, expandedNotResource, this.conditions, allowPrincipalArgs.principal, allowPrincipalArgs.notPrincipal));
720
+ }
721
+ return permissionsToReturn;
722
+ }
723
+ // ═══════════════════════════════════════════════════════════════════════════
724
+ // SCENARIO 4: Allow.NotResource & Deny.NotResource
725
+ // ═══════════════════════════════════════════════════════════════════════════
726
+ //
727
+ // Semantics:
728
+ // Allow.notResource = [A]: allow ALL resources EXCEPT those matching A
729
+ // Deny.notResource = [D]: deny ALL resources EXCEPT those matching D
730
+ //
731
+ // The deny blocks everything except D (the "safe zone").
732
+ // The allow permits everything except A.
733
+ //
734
+ // Surviving resources = (allowed) ∩ (not denied)
735
+ // = (NOT in A) ∩ (in D)
736
+ // = resources in D that are not covered by A
737
+ //
738
+ // Result: resource: [D patterns not covered by A]
739
+ //
740
+ // ═══════════════════════════════════════════════════════════════════════════
741
+ if (allowNotResource !== undefined && denyNotResource !== undefined) {
742
+ const denyHasConditions = other.conditions && Object.keys(other.conditions).length > 0;
743
+ // Helper: Check if pattern A covers pattern B (A is superset of or equal to B)
744
+ const patternCovers = (a, b) => {
745
+ return a === b || wildcardToRegex(a).test(b);
746
+ };
747
+ // Find D patterns that survive (not covered by any A pattern)
748
+ // These are the resources that are both allowed AND protected from deny
749
+ const survivingResources = denyNotResource.filter((dPattern) => !allowNotResource.some((aPattern) => patternCovers(aPattern, dPattern)));
750
+ // If nothing survives, return empty
751
+ if (survivingResources.length === 0) {
752
+ return [];
753
+ }
754
+ // Handle conditions
755
+ if (!denyHasConditions || conditionsMatch) {
756
+ // No deny conditions or same conditions: apply directly
757
+ return [
758
+ new Permission(eff, svc, act, survivingResources, undefined, this.conditions, allowPrincipalArgs.principal, allowPrincipalArgs.notPrincipal)
759
+ ];
760
+ }
761
+ // Different conditions: split into two parts
762
+ const permissionsToReturn = [];
763
+ // Part 1: When deny condition is NOT met → original allow applies
764
+ const originalAllow = new Permission(eff, svc, act, undefined, allowNotResource, this.conditions, allowPrincipalArgs.principal, allowPrincipalArgs.notPrincipal);
765
+ permissionsToReturn.push(...applyDenyConditionsToAllow(originalAllow, other));
766
+ // Part 2: When deny condition IS met → surviving resources with deny's condition
767
+ const denyConditionCount = Object.values(other.conditions || {}).reduce((sum, keyMap) => sum + Object.keys(keyMap).length, 0);
768
+ const part2Conditions = denyConditionCount === 1 ? other.conditions : undefined;
769
+ permissionsToReturn.push(new Permission(eff, svc, act, survivingResources, undefined, part2Conditions, allowPrincipalArgs.principal, allowPrincipalArgs.notPrincipal));
770
+ return permissionsToReturn;
771
+ }
772
+ // This should never happen
773
+ throw new Error('Permission.subtract: This should never happen—invalid state.');
774
+ }
775
+ }
776
+ /**
777
+ * Attempt to union two sets of permission conditions.
778
+ *
779
+ * If the conditions can be merged into a single block that allows all cases allowed by either,
780
+ * returns the merged conditions. If they cannot be merged cleanly (e.g., differing operators
781
+ * or incompatible numeric boundaries), returns null.
782
+ *
783
+ * @param a First set of conditions
784
+ * @param b Second set of conditions
785
+ * @returns Merged conditions or null if they cannot be merged
786
+ */
787
+ export function unionConditions(a, b) {
788
+ // 1. If the set of operators in 'a' differs from the set in 'b', return null.
789
+ a = normalizeConditionKeys(a);
790
+ b = normalizeConditionKeys(b);
791
+ const opsA = Object.keys(a).sort();
792
+ const opsB = Object.keys(b).sort();
793
+ if (JSON.stringify(opsA) !== JSON.stringify(opsB)) {
794
+ return null;
795
+ }
796
+ const merged = {};
797
+ // 2. For each operator op that appears in both:
798
+ for (const op of opsA) {
799
+ const keysA = Object.keys(a[op]).sort();
800
+ const keysB = Object.keys(b[op]).sort();
801
+ // If the set of context‐keys under this operator differs, we can't merge as one block
802
+ if (JSON.stringify(keysA) !== JSON.stringify(keysB)) {
803
+ return null;
804
+ }
805
+ // Now we know op and its context keys align. Build the merged set for this operator:
806
+ merged[op] = {};
807
+ for (const key of keysA) {
808
+ const valsA = a[op][key];
809
+ const valsB = b[op][key];
810
+ // How we combine depends on operator semantics:
811
+ switch (conditionBaseOperator(op)) {
812
+ case 'stringequals':
813
+ case 'stringlike':
814
+ case 'stringnotequals':
815
+ case 'stringnotlike':
816
+ case 'arnequals':
817
+ case 'arnlike':
818
+ case 'arnnotequals':
819
+ case 'arnnotlike':
820
+ // String‐based operators: just union the value arrays
821
+ merged[op][key] = Array.from(new Set([...valsA, ...valsB]));
822
+ break;
823
+ case 'numericlessthan':
824
+ case 'numericlessthanequals':
825
+ case 'numericgreaterthan':
826
+ case 'numericgreaterthanequals':
827
+ case 'numericequals':
828
+ case 'numericnotequals':
829
+ // Numeric operators: pick the “widest” comparison that still covers both sets
830
+ // For simplicity, convert all valsA/valsB to numbers; find the min or max
831
+ const numsA = valsA.map((v) => Number(v));
832
+ const numsB = valsB.map((v) => Number(v));
833
+ if (numsA.some(isNaN) || numsB.some(isNaN)) {
834
+ // Malformed number—cannot merge
835
+ return null;
836
+ }
837
+ if (op === 'numericlessthan' || op === 'numericlessthanequals') {
838
+ // We want the largest boundary
839
+ const candidate = Math.max(...numsA, ...numsB);
840
+ merged[op][key] = [String(candidate)];
841
+ }
842
+ else if (op === 'numericgreaterthan' || op === 'numericgreaterthanequals') {
843
+ // We want the smallest boundary
844
+ const candidate = Math.min(...numsA, ...numsB);
845
+ merged[op][key] = [String(candidate)];
846
+ }
847
+ else if (op === 'numericequals' || op === 'numericnotequals') {
848
+ // Union the sets of allowed/not‐allowed numbers
849
+ merged[op][key] = Array.from(new Set([...valsA.map(String), ...valsB.map(String)]));
850
+ }
851
+ break;
852
+ case 'datelessthan':
853
+ case 'datelessthanequals':
854
+ case 'dategreaterthan':
855
+ case 'dategreaterthanequals':
856
+ // Similar idea: choose the “widest” date limit
857
+ // Assume ISO‐8601 strings so lex‐compare works
858
+ if (op === 'datelessthan' || op === 'datelessthanequals') {
859
+ // pick the LARGEST date (latest) because “< latest” covers “< earlier”
860
+ const candidate = [...valsA, ...valsB].sort().reverse()[0];
861
+ merged[op][key] = [candidate];
862
+ }
863
+ else {
864
+ // "DateGreaterThan"/"DateGreaterThanEquals": pick the EARLIEST date
865
+ const candidate = [...valsA, ...valsB].sort()[0];
866
+ merged[op][key] = [candidate];
867
+ }
868
+ break;
869
+ case 'bool':
870
+ // Typically valsA and valsB are ["true"] or ["false"].
871
+ // If either contains "true", then the union is ["true","false"]? No—
872
+ // Bool doesn't make sense with an array. In IAM, Bool only works with a single value.
873
+ // If values differ (one says ["true"], the other says ["false"]), you cannot
874
+ // express (Bool==true OR Bool==false) as a single Bool. You’d need two separate
875
+ // statements. So bail out.
876
+ if (valsA[0] === valsB[0]) {
877
+ merged[op][key] = [valsA[0]];
878
+ }
879
+ else {
880
+ return null;
881
+ }
882
+ break;
883
+ case 'ipaddress':
884
+ case 'notipaddress':
885
+ // You can pass multiple CIDR blocks under a single IpAddress. So union them.
886
+ merged[op][key] = Array.from(new Set([...valsA, ...valsB]));
887
+ break;
888
+ // Any other operators (e.g., “ArnNotLike” etc.) behave similarly to their base type
889
+ default:
890
+ // If we don’t explicitly handle the operator, reject merging
891
+ return null;
892
+ }
893
+ }
894
+ }
895
+ return merged;
896
+ }
897
+ /**
898
+ * Intersect two sets of permission conditions.
899
+ *
900
+ * Attempt to find the intersection of two sets of IAM condition clauses. This will
901
+ * combine condition operators and context keys, retaining only values that satisfy
902
+ * both sets of conditions. If the intersection is empty or cannot be expressed
903
+ * cleanly, returns null.
904
+ *
905
+ * @param conditionsA First set of conditions
906
+ * @param conditionsB Second set of conditions
907
+ * @returns Intersected conditions or null if intersection is empty or cannot be expressed
908
+ */
909
+ export function intersectConditions(a, b) {
910
+ // Normalize both condition sets to lowercase operators and keys
911
+ const normalizedA = normalizeConditionKeys(a);
912
+ const normalizedB = normalizeConditionKeys(b);
913
+ // Collect all unique operators from both sides
914
+ const allOperators = Array.from(new Set([...Object.keys(normalizedA), ...Object.keys(normalizedB)]));
915
+ const result = {};
916
+ for (const operator of allOperators) {
917
+ const keysA = normalizedA[operator] || {};
918
+ const keysB = normalizedB[operator] || {};
919
+ // Collect all unique context keys for this operator
920
+ const allContextKeys = Array.from(new Set([...Object.keys(keysA), ...Object.keys(keysB)]));
921
+ result[operator] = {};
922
+ for (const contextKey of allContextKeys) {
923
+ const valsA = keysA[contextKey];
924
+ const valsB = keysB[contextKey];
925
+ // If key exists in both sides, apply intersection logic based on operator type
926
+ if (valsA !== undefined && valsB !== undefined) {
927
+ const intersectedValues = intersectValuesForOperator(operator, valsA, valsB);
928
+ if (intersectedValues === null) {
929
+ // Empty intersection means no overlap - return null
930
+ return null;
931
+ }
932
+ result[operator][contextKey] = intersectedValues;
933
+ }
934
+ else {
935
+ // Key only exists in one side - carry it through (both conditions must be satisfied)
936
+ result[operator][contextKey] = valsA !== undefined ? Array.from(valsA) : Array.from(valsB);
937
+ }
938
+ }
939
+ // Remove empty operator objects
940
+ if (Object.keys(result[operator]).length === 0) {
941
+ delete result[operator];
942
+ }
943
+ }
944
+ const merged = mergeComplementaryConditions(result);
945
+ // Check if any values array became empty after merging complementary conditions
946
+ // (e.g., StringEquals: ['a'] merged with StringNotEquals: ['a'] results in StringEquals: [])
947
+ for (const [, keyMap] of Object.entries(merged)) {
948
+ for (const [, values] of Object.entries(keyMap)) {
949
+ if (values.length === 0) {
950
+ return null;
951
+ }
952
+ }
953
+ }
954
+ return merged;
955
+ }
956
+ /**
957
+ * Intersect values for a specific operator type.
958
+ *
959
+ * Returns the intersected values, or null if the intersection is empty
960
+ * (meaning the conditions are mutually exclusive).
961
+ */
962
+ function intersectValuesForOperator(operator, valsA, valsB) {
963
+ const baseOp = conditionBaseOperator(operator);
964
+ switch (baseOp) {
965
+ // String/ARN equality operators: intersection of allowed values
966
+ case 'stringequals':
967
+ case 'stringlike':
968
+ case 'arnequals':
969
+ case 'arnlike': {
970
+ const common = valsA.filter((v) => valsB.includes(v));
971
+ return common.length > 0 ? common : null;
972
+ }
973
+ // String/ARN negation operators: union of exclusions (more restrictive)
974
+ case 'stringnotequals':
975
+ case 'stringnotlike':
976
+ case 'arnnotequals':
977
+ case 'arnnotlike': {
978
+ return Array.from(new Set([...valsA, ...valsB]));
979
+ }
980
+ // Numeric less-than operators: take the minimum (more restrictive)
981
+ case 'numericlessthan':
982
+ case 'numericlessthanequals': {
983
+ const numA = Number(valsA[0]);
984
+ const numB = Number(valsB[0]);
985
+ if (isNaN(numA) || isNaN(numB)) {
986
+ return null;
987
+ }
988
+ return [String(Math.min(numA, numB))];
989
+ }
990
+ // Numeric greater-than operators: take the maximum (more restrictive)
991
+ case 'numericgreaterthan':
992
+ case 'numericgreaterthanequals': {
993
+ const numA = Number(valsA[0]);
994
+ const numB = Number(valsB[0]);
995
+ if (isNaN(numA) || isNaN(numB)) {
996
+ return null;
997
+ }
998
+ return [String(Math.max(numA, numB))];
999
+ }
1000
+ // Boolean operators: values must match exactly
1001
+ case 'bool':
1002
+ case 'null': {
1003
+ if (valsA[0]?.toLowerCase() !== valsB[0]?.toLowerCase()) {
1004
+ return null;
1005
+ }
1006
+ return [valsA[0]];
1007
+ }
1008
+ // IP address operators: intersection of CIDR blocks
1009
+ case 'ipaddress':
1010
+ case 'notipaddress': {
1011
+ const common = valsA.filter((cidr) => valsB.includes(cidr));
1012
+ return common.length > 0 ? common : null;
1013
+ }
1014
+ // Date less-than operators: take the earlier date (more restrictive)
1015
+ case 'datelessthan':
1016
+ case 'datelessthanequals': {
1017
+ const dateA = valsA[0];
1018
+ const dateB = valsB[0];
1019
+ return [dateA < dateB ? dateA : dateB];
1020
+ }
1021
+ // Date greater-than operators: take the later date (more restrictive)
1022
+ case 'dategreaterthan':
1023
+ case 'dategreaterthanequals': {
1024
+ const dateA = valsA[0];
1025
+ const dateB = valsB[0];
1026
+ return [dateA > dateB ? dateA : dateB];
1027
+ }
1028
+ // Unknown operator - cannot handle
1029
+ default:
1030
+ return null;
1031
+ }
1032
+ }
1033
+ /**
1034
+ * Checks if an IAM condition operator ends with "IfExists".
1035
+ *
1036
+ * @param op the IAM condition operator, e.g., "StringEqualsIfExists"
1037
+ * @returns true if the operator ends with "IfExists", false otherwise.
1038
+ */
1039
+ function isIfExists(op) {
1040
+ // Check if the operator ends with "IfExists"
1041
+ return op.toLowerCase().endsWith('ifexists');
1042
+ }
1043
+ /**
1044
+ * Get the set operator from an IAM condition operator such as "ForAllValues" or "ForAnyValue".
1045
+ *
1046
+ * @param op the IAM condition operator, e.g., "ForAllValues:StringEquals"
1047
+ * @returns the set operator, e.g., "forallvalues" or "foranyvalue", or undefined if no set operator is present.
1048
+ */
1049
+ function conditionSetOperator(op) {
1050
+ return op.includes(':') ? op.split(':')[0].toLowerCase() : undefined;
1051
+ }
1052
+ /**
1053
+ * Gets the base operator name from an IAM condition operator. Removes any set operator prefix or
1054
+ * "IfExists" suffix.
1055
+ *
1056
+ * @param op the IAM condition operator, e.g., "ForAllValues:StringEqualsIfExists"
1057
+ * @returns the base operator name, e.g., "stringequals" or "arnequals".
1058
+ */
1059
+ function conditionBaseOperator(op) {
1060
+ // Return the base operator name for IAM condition operators
1061
+ return op
1062
+ .split(':')
1063
+ .at(-1)
1064
+ .toLowerCase()
1065
+ .replace(/ifexists$/, '');
1066
+ }
1067
+ /**
1068
+ * Returns a new PermissionConditions object with all operator and context keys lowercased.
1069
+ */
1070
+ export function normalizeConditionKeys(conds) {
1071
+ const result = {};
1072
+ for (const [op, keyMap] of Object.entries(conds)) {
1073
+ const lowerOp = op.toLowerCase();
1074
+ result[lowerOp] = {};
1075
+ for (const [contextKey, values] of Object.entries(keyMap)) {
1076
+ const lowerContextKey = contextKey.toLowerCase();
1077
+ result[lowerOp][lowerContextKey] = Array.from(values);
1078
+ }
1079
+ }
1080
+ return result;
1081
+ }
1082
+ const invertOperatorMap = {
1083
+ stringequals: 'StringNotEquals',
1084
+ stringlike: 'StringNotLike',
1085
+ arnequals: 'ArnNotEquals',
1086
+ arnlike: 'ArnNotLike',
1087
+ stringnotequals: 'StringEquals',
1088
+ stringnotlike: 'StringLike',
1089
+ arnnotequals: 'ArnEquals',
1090
+ arnnotlike: 'ArnLike',
1091
+ numericlessthan: 'NumericGreaterThanEquals',
1092
+ numericlessthanequals: 'NumericGreaterThan',
1093
+ numericgreaterthan: 'NumericLessThanEquals',
1094
+ numericgreaterthanequals: 'NumericLessThan',
1095
+ numericequals: 'NumericNotEquals',
1096
+ numericnotequals: 'NumericEquals',
1097
+ datelessthan: 'DateGreaterThanEquals',
1098
+ datelessthanequals: 'DateGreaterThan',
1099
+ dategreaterthan: 'DateLessThanEquals',
1100
+ dategreaterthanequals: 'DateLessThan',
1101
+ bool: 'Bool',
1102
+ ipaddress: 'NotIpAddress',
1103
+ notipaddress: 'IpAddress'
1104
+ };
1105
+ const invertedSetOperatorMap = {
1106
+ forallvalues: 'ForAnyValue',
1107
+ foranyvalue: 'ForAllValues'
1108
+ };
1109
+ /**
1110
+ * Invert a set of IAM condition clauses for Deny → allow inversion.
1111
+ * Preserves ForAllValues:/ForAnyValue: prefixes and IfExists suffixes.
1112
+ *
1113
+ * @param conds the condition clauses to invert
1114
+ * @return a new set of inverted conditions
1115
+ */
1116
+ export function invertConditions(conds) {
1117
+ const normalized = normalizeConditionKeys(conds);
1118
+ const inverted = {};
1119
+ for (const [op, keyMap] of Object.entries(normalized)) {
1120
+ const setOperator = conditionSetOperator(op) || undefined;
1121
+ const setOperatorPrefix = setOperator ? invertedSetOperatorMap[setOperator] + ':' : '';
1122
+ const hasIfExists = isIfExists(op);
1123
+ const coreOp = conditionBaseOperator(op);
1124
+ const invertedCore = invertOperatorMap[coreOp] || coreOp;
1125
+ const invertedOp = `${setOperatorPrefix}${invertedCore}${hasIfExists ? 'IfExists' : ''}`.toLowerCase();
1126
+ inverted[invertedOp] = {};
1127
+ for (const [key, vals] of Object.entries(keyMap)) {
1128
+ if (coreOp === 'bool' || coreOp === 'null') {
1129
+ inverted[invertedOp][key] = vals.map((v) => (v.toLowerCase() === 'true' ? 'false' : 'true'));
1130
+ }
1131
+ else {
1132
+ inverted[invertedOp][key] = Array.from(vals);
1133
+ }
1134
+ }
1135
+ }
1136
+ return inverted;
1137
+ }
1138
+ function mergeComplementaryConditions(c) {
1139
+ const complement = {
1140
+ stringequals: 'stringnotequals',
1141
+ stringlike: 'stringnotlike',
1142
+ arnequals: 'arnnotequals',
1143
+ arnlike: 'arnnotlike',
1144
+ numericequals: 'numericnotequals',
1145
+ numericnotequals: 'numericequals',
1146
+ numericlessthan: 'numericgreaterthanequals',
1147
+ numericgreaterthanequals: 'numericlessthan',
1148
+ numericlessthanequals: 'numericgreaterthan',
1149
+ numericgreaterthan: 'numericlessthanequals',
1150
+ datelessthan: 'dategreaterthanequals',
1151
+ dategreaterthanequals: 'datelessthan',
1152
+ datelessthanequals: 'dategreaterthan',
1153
+ dategreaterthan: 'datelessthanequals',
1154
+ ipaddress: 'notipaddress',
1155
+ notipaddress: 'ipaddress'
1156
+ // bool: 'bool'
1157
+ };
1158
+ const out = structuredClone(c);
1159
+ for (const [base, comp] of Object.entries(complement)) {
1160
+ if (out[base] && out[comp]) {
1161
+ for (const key of Object.keys(out[base])) {
1162
+ if (key in out[comp]) {
1163
+ out[base][key] = out[base][key].filter((v) => !out[comp][key].includes(v));
1164
+ delete out[comp][key];
1165
+ }
1166
+ }
1167
+ if (out[comp] && Object.keys(out[comp]).length === 0)
1168
+ delete out[comp];
1169
+ }
1170
+ }
1171
+ return out;
1172
+ }
1173
+ /**
1174
+ * Apply Deny conditions to an Allow permission.
1175
+ *
1176
+ * A Deny permission with conditions (whether multiple operators or multiple keys under one
1177
+ * operator) acts as an AND, meaning the Allow needs to escape ANY one of them (OR when inverted).
1178
+ * Each condition key-value pair is inverted and creates a separate Allow permission.
1179
+ *
1180
+ * It is possible for any given condition to fully deny the Allow, in which case
1181
+ * that condition will produce no resulting Allow permission. The result is an array
1182
+ * of Allow permissions that apply after each Deny condition is applied.
1183
+ *
1184
+ * This may result in multiple Allow permission or an empty array if all are denied.
1185
+ *
1186
+ * @param allow the Allow permission
1187
+ * @param deny the Deny permission
1188
+ * @returns an array of resulting Allow permissions after applying Deny conditions
1189
+ */
1190
+ export function applyDenyConditionsToAllow(allow, deny) {
1191
+ // If Deny has no conditions, it fully denies the Allow
1192
+ if (!deny.conditions || Object.keys(deny.conditions).length === 0) {
1193
+ return [allow];
1194
+ }
1195
+ const results = [];
1196
+ // Each Deny condition key-value pair creates a separate inverted condition for Allow
1197
+ // (multiple keys under the same operator are each inverted separately)
1198
+ for (const [operator, keyMap] of Object.entries(deny.conditions || {})) {
1199
+ for (const [contextKey, values] of Object.entries(keyMap)) {
1200
+ // Invert this specific condition
1201
+ const singleCondition = { [operator]: { [contextKey]: values } };
1202
+ const invertedCondition = invertConditions(singleCondition);
1203
+ // Merge with the original Allow conditions
1204
+ const mergedConditions = intersectConditions(allow.conditions || {}, invertedCondition);
1205
+ if (mergedConditions !== null) {
1206
+ results.push(new Permission(allow.effect, allow.service, allow.action, allow.resource, allow.notResource, mergedConditions, allow.principal, allow.notPrincipal));
1207
+ }
1208
+ }
1209
+ }
1210
+ return results;
1211
+ }
1212
+ //# sourceMappingURL=permission.js.map