@empire-builder-kit/nx 1.0.0-rc.30 → 1.0.0-rc.32

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 (310) hide show
  1. package/executors.json +8 -3
  2. package/package.json +14 -7
  3. package/src/aws/child-environment.d.ts +10 -0
  4. package/src/aws/child-environment.js +77 -0
  5. package/src/aws/child-environment.js.map +1 -0
  6. package/src/aws/identity.d.ts +13 -0
  7. package/src/aws/identity.js +41 -0
  8. package/src/aws/identity.js.map +1 -0
  9. package/src/aws-authority/index.d.ts +1 -0
  10. package/src/aws-authority/index.js +5 -0
  11. package/src/aws-authority/index.js.map +1 -0
  12. package/src/aws-authority/stage-authority.d.ts +240 -0
  13. package/src/aws-authority/stage-authority.js +2968 -0
  14. package/src/aws-authority/stage-authority.js.map +1 -0
  15. package/src/cli/main.d.ts +2 -0
  16. package/src/cli/main.js +11 -0
  17. package/src/cli/main.js.map +1 -1
  18. package/src/developer-agent/generator-planning.js +4 -4
  19. package/src/developer-agent/generator-planning.js.map +1 -1
  20. package/src/developer-agent/operation-registry.js +14 -6
  21. package/src/developer-agent/operation-registry.js.map +1 -1
  22. package/src/developer-agent/workspace-inspection.js +11 -2
  23. package/src/developer-agent/workspace-inspection.js.map +1 -1
  24. package/src/executors/deployment-plan/deployment-plan.js +1 -1
  25. package/src/executors/deployment-plan/deployment-plan.js.map +1 -1
  26. package/src/executors/foundation-preflight/foundation-preflight.d.ts +0 -1
  27. package/src/executors/foundation-preflight/foundation-preflight.js.map +1 -1
  28. package/src/executors/foundation-preflight/schema.json +0 -5
  29. package/src/executors/foundation-profile-preflight/foundation-profile-preflight.d.ts +12 -3
  30. package/src/executors/foundation-profile-preflight/foundation-profile-preflight.js +95 -32
  31. package/src/executors/foundation-profile-preflight/foundation-profile-preflight.js.map +1 -1
  32. package/src/executors/foundation-profile-preflight/production-database-safeguard.d.ts +64 -0
  33. package/src/executors/foundation-profile-preflight/production-database-safeguard.js +221 -0
  34. package/src/executors/foundation-profile-preflight/production-database-safeguard.js.map +1 -0
  35. package/src/executors/foundation-profile-preflight/schema.json +1 -6
  36. package/src/executors/foundation-remove-preflight/foundation-remove-preflight.d.ts +11 -1
  37. package/src/executors/foundation-remove-preflight/foundation-remove-preflight.js +44 -21
  38. package/src/executors/foundation-remove-preflight/foundation-remove-preflight.js.map +1 -1
  39. package/src/executors/foundation-remove-preflight/schema.json +6 -1
  40. package/src/executors/github-bootstrap/github-bootstrap.d.ts +1 -0
  41. package/src/executors/github-bootstrap/github-bootstrap.js +1 -0
  42. package/src/executors/github-bootstrap/github-bootstrap.js.map +1 -1
  43. package/src/executors/github-bootstrap/schema.json +7 -1
  44. package/src/executors/infrastructure-plan/infrastructure-plan.d.ts +27 -369
  45. package/src/executors/infrastructure-plan/infrastructure-plan.js +237 -5470
  46. package/src/executors/infrastructure-plan/infrastructure-plan.js.map +1 -1
  47. package/src/executors/infrastructure-plan/schema.json +8 -64
  48. package/src/executors/local-deploy/federated-credential-transport.d.ts +48 -0
  49. package/src/executors/local-deploy/federated-credential-transport.js +252 -0
  50. package/src/executors/local-deploy/federated-credential-transport.js.map +1 -0
  51. package/src/executors/local-deploy/local-approval.d.ts +4 -9
  52. package/src/executors/local-deploy/local-approval.js +3 -7
  53. package/src/executors/local-deploy/local-approval.js.map +1 -1
  54. package/src/executors/local-deploy/local-deploy.d.ts +60 -43
  55. package/src/executors/local-deploy/local-deploy.js +955 -804
  56. package/src/executors/local-deploy/local-deploy.js.map +1 -1
  57. package/src/executors/local-deploy/schema.json +3 -20
  58. package/src/executors/refresh/refresh.js +2 -2
  59. package/src/executors/refresh/refresh.js.map +1 -1
  60. package/src/executors/remove/remove.js +2 -2
  61. package/src/executors/remove/remove.js.map +1 -1
  62. package/src/executors/router-waf-rehearsal/router-waf-rehearsal.d.ts +27 -1
  63. package/src/executors/router-waf-rehearsal/router-waf-rehearsal.js +907 -22
  64. package/src/executors/router-waf-rehearsal/router-waf-rehearsal.js.map +1 -1
  65. package/src/executors/router-waf-rehearsal/schema.json +17 -2
  66. package/src/executors/secret-initialize/schema.json +42 -0
  67. package/src/executors/secret-initialize/secret-initialize.d.ts +32 -0
  68. package/src/executors/secret-initialize/secret-initialize.js +243 -0
  69. package/src/executors/secret-initialize/secret-initialize.js.map +1 -0
  70. package/src/executors/secret-metadata/secret-metadata.d.ts +4 -1
  71. package/src/executors/secret-metadata/secret-metadata.js +76 -29
  72. package/src/executors/secret-metadata/secret-metadata.js.map +1 -1
  73. package/src/executors/sst-lifecycle/generated-secret-retirement.d.ts +53 -0
  74. package/src/executors/sst-lifecycle/generated-secret-retirement.js +192 -0
  75. package/src/executors/sst-lifecycle/generated-secret-retirement.js.map +1 -0
  76. package/src/executors/sst-lifecycle/governed-native-operation.d.ts +46 -0
  77. package/src/executors/sst-lifecycle/governed-native-operation.js +153 -0
  78. package/src/executors/sst-lifecycle/governed-native-operation.js.map +1 -0
  79. package/src/executors/sst-lifecycle/run-sst-command.d.ts +27 -7
  80. package/src/executors/sst-lifecycle/run-sst-command.js +474 -89
  81. package/src/executors/sst-lifecycle/run-sst-command.js.map +1 -1
  82. package/src/executors/sst-lifecycle/schema.json +0 -5
  83. package/src/executors/sst-lifecycle/secret-preflight.d.ts +2 -0
  84. package/src/executors/sst-lifecycle/secret-preflight.js +13 -7
  85. package/src/executors/sst-lifecycle/secret-preflight.js.map +1 -1
  86. package/src/executors/unlock/unlock.js +2 -2
  87. package/src/executors/unlock/unlock.js.map +1 -1
  88. package/src/foundation/provision-logical-database.js +2 -0
  89. package/src/foundation/provision-logical-database.js.map +1 -1
  90. package/src/foundation/router-waf-rate-rehearsal.d.ts +97 -0
  91. package/src/foundation/router-waf-rate-rehearsal.js +210 -0
  92. package/src/foundation/router-waf-rate-rehearsal.js.map +1 -0
  93. package/src/foundation/router-waf.d.ts +59 -9
  94. package/src/foundation/router-waf.js +427 -32
  95. package/src/foundation/router-waf.js.map +1 -1
  96. package/src/foundation/stages.d.ts +1 -0
  97. package/src/foundation/stages.js +11 -0
  98. package/src/foundation/stages.js.map +1 -1
  99. package/src/generators/formatting.d.ts +8 -0
  100. package/src/generators/formatting.js +118 -0
  101. package/src/generators/formatting.js.map +1 -0
  102. package/src/generators/function/function.js +37 -17
  103. package/src/generators/function/function.js.map +1 -1
  104. package/src/generators/post-generation-install.js +2 -2
  105. package/src/generators/post-generation-install.js.map +1 -1
  106. package/src/generators/preset/generator.js +28 -8
  107. package/src/generators/preset/generator.js.map +1 -1
  108. package/src/generators/preset/schema.d.ts +3 -0
  109. package/src/generators/preset/schema.json +5 -5
  110. package/src/generators/preset/workspace-files-developer-agent.js +15 -7
  111. package/src/generators/preset/workspace-files-developer-agent.js.map +1 -1
  112. package/src/generators/preset/workspace-files-ecr-inspector.d.ts +18 -0
  113. package/src/generators/preset/workspace-files-ecr-inspector.js +298 -0
  114. package/src/generators/preset/workspace-files-ecr-inspector.js.map +1 -0
  115. package/src/generators/preset/workspace-files-performance-cost.d.ts +2 -2
  116. package/src/generators/preset/workspace-files-performance-cost.js +8 -8
  117. package/src/generators/preset/workspace-files-performance-cost.js.map +1 -1
  118. package/src/generators/preset/workspace-files-performance-load.d.ts +1 -1
  119. package/src/generators/preset/workspace-files-performance-load.js +29 -24
  120. package/src/generators/preset/workspace-files-performance-load.js.map +1 -1
  121. package/src/generators/preset/workspace-files-stage-authority.d.ts +6 -0
  122. package/src/generators/preset/workspace-files-stage-authority.js +479 -0
  123. package/src/generators/preset/workspace-files-stage-authority.js.map +1 -0
  124. package/src/generators/preset/workspace-files-thin-deploy.d.ts +40 -0
  125. package/src/generators/preset/workspace-files-thin-deploy.js +1500 -0
  126. package/src/generators/preset/workspace-files-thin-deploy.js.map +1 -0
  127. package/src/generators/preset/workspace-files-thin-docs.d.ts +5 -0
  128. package/src/generators/preset/workspace-files-thin-docs.js +240 -0
  129. package/src/generators/preset/workspace-files-thin-docs.js.map +1 -0
  130. package/src/generators/preset/workspace-files-thin-release.d.ts +14 -0
  131. package/src/generators/preset/workspace-files-thin-release.js +26 -0
  132. package/src/generators/preset/workspace-files-thin-release.js.map +1 -0
  133. package/src/generators/preset/workspace-files.d.ts +2 -1
  134. package/src/generators/preset/workspace-files.js +448 -680
  135. package/src/generators/preset/workspace-files.js.map +1 -1
  136. package/src/generators/repository-policy/repository-policy.js +149 -19
  137. package/src/generators/repository-policy/repository-policy.js.map +1 -1
  138. package/src/generators/repository-policy/schema.d.ts +2 -0
  139. package/src/generators/repository-policy/schema.json +11 -1
  140. package/src/generators/repository-policy/tree-policy.d.ts +3 -0
  141. package/src/generators/repository-policy/tree-policy.js +25 -0
  142. package/src/generators/repository-policy/tree-policy.js.map +1 -0
  143. package/src/generators/slice/built-in-plan-api-files.js +1 -0
  144. package/src/generators/slice/built-in-plan-api-files.js.map +1 -1
  145. package/src/generators/slice/built-in-plan-async-work-files.js +41 -0
  146. package/src/generators/slice/built-in-plan-async-work-files.js.map +1 -1
  147. package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js +6 -0
  148. package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js.map +1 -1
  149. package/src/generators/slice/built-in-plan-data-export-infrastructure-files.js +5 -31
  150. package/src/generators/slice/built-in-plan-data-export-infrastructure-files.js.map +1 -1
  151. package/src/generators/slice/built-in-plan-data-files.d.ts +1 -1
  152. package/src/generators/slice/built-in-plan-data-files.js +89 -30
  153. package/src/generators/slice/built-in-plan-data-files.js.map +1 -1
  154. package/src/generators/slice/built-in-plan-data-lifecycle-runtime-files.js +19 -0
  155. package/src/generators/slice/built-in-plan-data-lifecycle-runtime-files.js.map +1 -1
  156. package/src/generators/slice/built-in-plan-data-retention-launcher-files.js +21 -3
  157. package/src/generators/slice/built-in-plan-data-retention-launcher-files.js.map +1 -1
  158. package/src/generators/slice/built-in-plan-database-retirement-rehearsal-files.js +20 -1
  159. package/src/generators/slice/built-in-plan-database-retirement-rehearsal-files.js.map +1 -1
  160. package/src/generators/slice/built-in-plan-foundation-files.js +3 -5
  161. package/src/generators/slice/built-in-plan-foundation-files.js.map +1 -1
  162. package/src/generators/slice/built-in-plan-frontend-files.js +5 -1
  163. package/src/generators/slice/built-in-plan-frontend-files.js.map +1 -1
  164. package/src/generators/slice/built-in-plan-infrastructure-files.d.ts +1 -1
  165. package/src/generators/slice/built-in-plan-infrastructure-files.js +199 -188
  166. package/src/generators/slice/built-in-plan-infrastructure-files.js.map +1 -1
  167. package/src/generators/slice/built-in-plan-metadata-files.js +13 -0
  168. package/src/generators/slice/built-in-plan-metadata-files.js.map +1 -1
  169. package/src/generators/slice/built-in-plan-native-runtime-authority-files.d.ts +2 -0
  170. package/src/generators/slice/built-in-plan-native-runtime-authority-files.js +307 -0
  171. package/src/generators/slice/built-in-plan-native-runtime-authority-files.js.map +1 -0
  172. package/src/generators/slice/built-in-plan-operations-files.js +65 -51
  173. package/src/generators/slice/built-in-plan-operations-files.js.map +1 -1
  174. package/src/generators/slice/built-in-plan-performance-files.js +97 -19
  175. package/src/generators/slice/built-in-plan-performance-files.js.map +1 -1
  176. package/src/generators/slice/built-in-plan-project-files.js +9 -68
  177. package/src/generators/slice/built-in-plan-project-files.js.map +1 -1
  178. package/src/generators/slice/built-in-plan-secret-runtime-files.d.ts +1 -1
  179. package/src/generators/slice/built-in-plan-secret-runtime-files.js +87 -76
  180. package/src/generators/slice/built-in-plan-secret-runtime-files.js.map +1 -1
  181. package/src/generators/slice/built-in-plan-testing-files.js +94 -47
  182. package/src/generators/slice/built-in-plan-testing-files.js.map +1 -1
  183. package/src/generators/slice/built-in-plan-thin-release-files.d.ts +11 -0
  184. package/src/generators/slice/built-in-plan-thin-release-files.js +129 -0
  185. package/src/generators/slice/built-in-plan-thin-release-files.js.map +1 -0
  186. package/src/generators/slice/built-in-plan-verification-files.js +6 -2
  187. package/src/generators/slice/built-in-plan-verification-files.js.map +1 -1
  188. package/src/generators/slice/built-in-plan-web-files.js +4 -2
  189. package/src/generators/slice/built-in-plan-web-files.js.map +1 -1
  190. package/src/generators/slice/built-in-plan-workload-role-files.js +26 -4
  191. package/src/generators/slice/built-in-plan-workload-role-files.js.map +1 -1
  192. package/src/generators/slice/built-in-plan.d.ts +1 -0
  193. package/src/generators/slice/built-in-plan.js +28 -11
  194. package/src/generators/slice/built-in-plan.js.map +1 -1
  195. package/src/generators/slice/slice.js +30 -32
  196. package/src/generators/slice/slice.js.map +1 -1
  197. package/src/generators/upgrade/upgrade.d.ts +1 -1
  198. package/src/generators/upgrade/upgrade.js +200 -48
  199. package/src/generators/upgrade/upgrade.js.map +1 -1
  200. package/src/generators/workload/performance-registration.d.ts +1 -0
  201. package/src/generators/workload/performance-registration.js +9 -11
  202. package/src/generators/workload/performance-registration.js.map +1 -1
  203. package/src/generators/workload/service-performance-files.d.ts +1 -0
  204. package/src/generators/workload/service-performance-files.js +27 -7
  205. package/src/generators/workload/service-performance-files.js.map +1 -1
  206. package/src/generators/workload/workload.d.ts +1 -1
  207. package/src/generators/workload/workload.js +55 -28
  208. package/src/generators/workload/workload.js.map +1 -1
  209. package/src/git-policy/github-bootstrap.d.ts +28 -7
  210. package/src/git-policy/github-bootstrap.js +205 -163
  211. package/src/git-policy/github-bootstrap.js.map +1 -1
  212. package/src/index.d.ts +1 -0
  213. package/src/index.js +1 -0
  214. package/src/index.js.map +1 -1
  215. package/src/local-dev/private-state.d.ts +3 -0
  216. package/src/local-dev/private-state.js +17 -5
  217. package/src/local-dev/private-state.js.map +1 -1
  218. package/src/local-dev/tls.js +5 -1
  219. package/src/local-dev/tls.js.map +1 -1
  220. package/src/performance/cost-collection.d.ts +1 -1
  221. package/src/performance/cost-collection.js +4 -2
  222. package/src/performance/cost-collection.js.map +1 -1
  223. package/src/performance/cost-controls.d.ts +3 -1
  224. package/src/performance/cost-controls.js +9 -4
  225. package/src/performance/cost-controls.js.map +1 -1
  226. package/src/performance/evidence.d.ts +1 -1
  227. package/src/performance/evidence.js +5 -3
  228. package/src/performance/evidence.js.map +1 -1
  229. package/src/performance/load-safety.js +5 -3
  230. package/src/performance/load-safety.js.map +1 -1
  231. package/src/performance/model.d.ts +2 -2
  232. package/src/performance/model.js +4 -3
  233. package/src/performance/model.js.map +1 -1
  234. package/src/release/index.d.ts +0 -2
  235. package/src/release/index.js +0 -2
  236. package/src/release/index.js.map +1 -1
  237. package/src/release/source-authority.d.ts +4 -5
  238. package/src/release/source-authority.js +13 -123
  239. package/src/release/source-authority.js.map +1 -1
  240. package/src/repository-policy/aurora-preflight.d.ts +1 -1
  241. package/src/repository-policy/aurora-preflight.js +4 -1
  242. package/src/repository-policy/aurora-preflight.js.map +1 -1
  243. package/src/repository-policy/policy.d.ts +21 -2
  244. package/src/repository-policy/policy.js +290 -25
  245. package/src/repository-policy/policy.js.map +1 -1
  246. package/src/schemas/performance-policy-v1.json +3 -1
  247. package/src/schemas/repository-policy-v1.json +46 -2
  248. package/src/secrets/adapters.d.ts +7 -2
  249. package/src/secrets/adapters.js +34 -3
  250. package/src/secrets/adapters.js.map +1 -1
  251. package/src/secrets/aws-control-plane.d.ts +10 -1
  252. package/src/secrets/aws-control-plane.js +67 -0
  253. package/src/secrets/aws-control-plane.js.map +1 -1
  254. package/src/secrets/generated-aws-control-plane.d.ts +23 -0
  255. package/src/secrets/generated-aws-control-plane.js +259 -0
  256. package/src/secrets/generated-aws-control-plane.js.map +1 -0
  257. package/src/secrets/metadata.js +38 -10
  258. package/src/secrets/metadata.js.map +1 -1
  259. package/src/secrets/model.d.ts +8 -1
  260. package/src/secrets/model.js +24 -3
  261. package/src/secrets/model.js.map +1 -1
  262. package/src/secrets/operator.d.ts +89 -3
  263. package/src/secrets/operator.js +422 -9
  264. package/src/secrets/operator.js.map +1 -1
  265. package/src/secrets/registry.d.ts +2 -1
  266. package/src/secrets/registry.js +18 -1
  267. package/src/secrets/registry.js.map +1 -1
  268. package/src/secrets/workload-access.js +10 -2
  269. package/src/secrets/workload-access.js.map +1 -1
  270. package/src/supply-chain/action-pins.js +61 -7
  271. package/src/supply-chain/action-pins.js.map +1 -1
  272. package/src/supply-chain/build-output-bounds.d.ts +2 -0
  273. package/src/supply-chain/build-output-bounds.js +12 -0
  274. package/src/supply-chain/build-output-bounds.js.map +1 -0
  275. package/src/supply-chain/build-output.js +2 -4
  276. package/src/supply-chain/build-output.js.map +1 -1
  277. package/src/supply-chain/ecr-scanning.d.ts +5 -3
  278. package/src/supply-chain/ecr-scanning.js +11 -7
  279. package/src/supply-chain/ecr-scanning.js.map +1 -1
  280. package/src/supply-chain/ecs-deployed-image.d.ts +5 -3
  281. package/src/supply-chain/ecs-deployed-image.js +6 -4
  282. package/src/supply-chain/ecs-deployed-image.js.map +1 -1
  283. package/src/supply-chain/generated-container-release-command.js +849 -44
  284. package/src/supply-chain/generated-container-release-command.js.map +1 -1
  285. package/src/supply-chain/generated-ecr-collector-command.d.ts +0 -1
  286. package/src/supply-chain/generated-ecr-collector-command.js +188 -91
  287. package/src/supply-chain/generated-ecr-collector-command.js.map +1 -1
  288. package/src/supply-chain/generated-files.js +4 -3
  289. package/src/supply-chain/generated-files.js.map +1 -1
  290. package/src/supply-chain/index.d.ts +1 -1
  291. package/src/supply-chain/index.js +2 -1
  292. package/src/supply-chain/index.js.map +1 -1
  293. package/src/workload-roles/index.d.ts +10 -17
  294. package/src/workload-roles/index.js +5 -18
  295. package/src/workload-roles/index.js.map +1 -1
  296. package/src/workspace-deployment/identity.d.ts +3 -0
  297. package/src/workspace-deployment/identity.js +22 -0
  298. package/src/workspace-deployment/identity.js.map +1 -0
  299. package/src/workspace-deployment/index.d.ts +3 -0
  300. package/src/workspace-deployment/index.js +7 -0
  301. package/src/workspace-deployment/index.js.map +1 -0
  302. package/src/workspace-deployment/migration-evidence.d.ts +29 -0
  303. package/src/workspace-deployment/migration-evidence.js +83 -0
  304. package/src/workspace-deployment/migration-evidence.js.map +1 -0
  305. package/src/workspace-deployment/migration.d.ts +27 -0
  306. package/src/workspace-deployment/migration.js +109 -0
  307. package/src/workspace-deployment/migration.js.map +1 -0
  308. package/src/workspace-deployment/ownership.d.ts +81 -0
  309. package/src/workspace-deployment/ownership.js +381 -0
  310. package/src/workspace-deployment/ownership.js.map +1 -0
@@ -0,0 +1,2968 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.STAGE_AUTHORITY_CROSS_STAGE_DESTRUCTIVE_ACTIONS = exports.STAGE_AUTHORITY_CROSS_ACCOUNT_MUTATION_ACTIONS = exports.STAGE_AUTHORITY_CROSS_ACCOUNT_EC2_MUTATION_ACTIONS = exports.STAGE_AUTHORITY_SST_417_EXTERNAL_AMI_OWNERS_BY_PARTITION = exports.STAGE_AUTHORITY_SCOPE_GUARD_MUTATION_FAMILIES = exports.STAGE_AUTHORITY_WORKLOAD_IDENTITY_POLICY_ACTIONS = exports.STAGE_AUTHORITY_APPROVED_AWS_MANAGED_POLICIES = exports.STAGE_RUNTIME_REGIONAL_SERVICE_FAMILIES = exports.STAGE_AUTHORITY_LOGS_MUTATION_FAMILIES = exports.STAGE_AUTHORITY_REGIONAL_MUTATION_FAMILIES = exports.STAGE_RUNTIME_GLOBAL_ACTIONS = exports.STAGE_RUNTIME_REGIONAL_ACTIONS = exports.STAGE_AUTHORITY_ACCOUNT_SCOPED_GLOBAL_WRITE_ACTIONS = exports.STAGE_AUTHORITY_GLOBAL_WRITE_ACTIONS = exports.STAGE_AUTHORITY_UNSUPPORTED_REPLICATION_AND_TOPOLOGY_ACTIONS = exports.STAGE_AUTHORITY_WAF_EDGE_MUTATION_ACTIONS = exports.STAGE_AUTHORITY_S3_REGIONAL_MUTATION_ACTIONS = exports.STAGE_AUTHORITY_UNSCOPED_CLOUDFRONT_CREATE_ACTIONS = exports.STAGE_AUTHORITY_REGIONAL_WRITE_ACTIONS = exports.STAGE_AUTHORITY_LAMBDA_INFRASTRUCTURE_ACTIONS = exports.STAGE_AUTHORITY_KMS_INFRASTRUCTURE_ACTIONS = exports.STAGE_AUTHORITY_EC2_INFRASTRUCTURE_ACTIONS = exports.STAGE_AUTHORITY_BACKUP_INFRASTRUCTURE_ACTIONS = exports.STAGE_AUTHORITY_BROAD_REGIONAL_SERVICE_ACTIONS = exports.STAGE_AUTHORITY_EDGE_OBSERVABILITY_READ_ACTIONS = exports.STAGE_AUTHORITY_GLOBAL_READ_ACTIONS = exports.STAGE_AUTHORITY_REGIONAL_READ_ACTIONS = exports.STAGE_AUTHORITY_COST_ANOMALY_WRITE_ACTIONS = exports.STAGE_AUTHORITY_COST_ANOMALY_RESOURCE_WRITE_ACTIONS = exports.STAGE_AUTHORITY_COST_ANOMALY_CREATE_ACTIONS = exports.STAGE_AUTHORITY_COST_ANOMALY_READ_ACTIONS = exports.STAGE_AUTHORITY_KMS_ROTATION_ACTIONS = exports.STAGE_AUTHORITY_RDS_EVENT_SUBSCRIPTION_SOURCE_ACTIONS = exports.STAGE_AUTHORITY_BUDGET_MUTATION_ACTIONS = exports.STAGE_AUTHORITY_EDGE_WAF_LOG_DELIVERY_MUTATION_ACTIONS = exports.STAGE_AUTHORITY_EDGE_WAF_LOG_GROUP_ACTIONS = exports.STAGE_AUTHORITY_WAF_LOGGING_ACTIONS = exports.STAGE_AUTHORITY_CLOUDFRONT_INVALIDATION_ACTIONS = exports.STAGE_AUTHORITY_LAMBDA_DEPLOYMENT_INVOKE_ACTIONS = exports.STAGE_AUTHORITY_EVENT_ARCHIVE_RESOURCE_ACTIONS = exports.STAGE_AUTHORITY_EVENT_ARCHIVE_CREATE_ACTIONS = exports.STAGE_AUTHORITY_CLOUDFORMATION_LIFECYCLE_ACTIONS = exports.STAGE_AUTHORITY_BOUND_TEMPLATE_BODY_MINIMUM_HEADROOM = exports.STAGE_AUTHORITY_TEMPLATE_BODY_BYTE_LIMIT = exports.STAGE_AUTHORITY_MANAGED_POLICY_CHARACTER_HEADROOM = exports.STAGE_AUTHORITY_MANAGED_POLICY_CHARACTER_LIMIT = exports.STAGE_AUTHORITY_DEPLOYMENT_POLICY_KINDS = exports.STAGE_AUTHORITY_PARTITIONS = exports.STAGE_AUTHORITY_STAGES = exports.STAGE_AUTHORITY_SCHEMA_VERSION = void 0;
4
+ exports.STAGE_AUTHORITY_SERVICE_LINKED_ROLES = exports.STAGE_AUTHORITY_UNSUPPORTED_WORKFLOW_EXECUTION_ACTIONS = exports.STAGE_AUTHORITY_NON_DEPLOYMENT_ACCOUNT_AND_DATA_ACTIONS = exports.STAGE_AUTHORITY_UNREVIEWED_PERMISSION_MANAGEMENT_ACTION_PATTERNS = exports.STAGE_AUTHORITY_REVIEWED_PERMISSION_MANAGEMENT_ACTIONS = exports.STAGE_AUTHORITY_S3_BUCKET_POLICY_MUTATION_ACTIONS = exports.STAGE_AUTHORITY_KNOWN_STAGE_MUTATION_ACTIONS = exports.STAGE_AUTHORITY_UNTAGGED_DESTRUCTIVE_ACTIONS = void 0;
5
+ exports.createStageAuthorityIdentity = createStageAuthorityIdentity;
6
+ exports.canonicalStageAuthorityTemplate = canonicalStageAuthorityTemplate;
7
+ exports.digestStageAuthorityTemplate = digestStageAuthorityTemplate;
8
+ exports.assertBoundStageAuthorityTemplateFitsBodyLimit = assertBoundStageAuthorityTemplateFitsBodyLimit;
9
+ exports.createStageAuthorityPlan = createStageAuthorityPlan;
10
+ exports.createStageIamBoundaryTransformContract = createStageIamBoundaryTransformContract;
11
+ exports.installStageIamBoundaryTransforms = installStageIamBoundaryTransforms;
12
+ const node_crypto_1 = require("node:crypto");
13
+ exports.STAGE_AUTHORITY_SCHEMA_VERSION = 1;
14
+ exports.STAGE_AUTHORITY_STAGES = [
15
+ 'development',
16
+ 'staging',
17
+ 'production',
18
+ ];
19
+ exports.STAGE_AUTHORITY_PARTITIONS = [
20
+ 'aws',
21
+ 'aws-cn',
22
+ 'aws-us-gov',
23
+ ];
24
+ exports.STAGE_AUTHORITY_DEPLOYMENT_POLICY_KINDS = [
25
+ 'read',
26
+ 'regional-mutation',
27
+ 'global-iam-mutation',
28
+ 'global-resource-mutation',
29
+ 'generated-resource-mutation',
30
+ 'stage-containment',
31
+ 'permission-guardrails',
32
+ 'guardrails',
33
+ ];
34
+ exports.STAGE_AUTHORITY_MANAGED_POLICY_CHARACTER_LIMIT = 6_144;
35
+ exports.STAGE_AUTHORITY_MANAGED_POLICY_CHARACTER_HEADROOM = 200;
36
+ exports.STAGE_AUTHORITY_TEMPLATE_BODY_BYTE_LIMIT = 51_200;
37
+ exports.STAGE_AUTHORITY_BOUND_TEMPLATE_BODY_MINIMUM_HEADROOM = 1_024;
38
+ const STAGE_AUTHORITY_MAX_AWS_PRINCIPALS = 16;
39
+ const STAGE_AUTHORITY_MAX_AWS_PRINCIPAL_ARN_BYTES = 2_048;
40
+ const ACCOUNT_ID = /^\d{12}$/u;
41
+ const REGION = /^(?:(?:af|ap|ca|cn|eu|il|me|mx|sa|us)(?:-(?:gov|iso|isob|isoe|isof))?|eusc-[a-z]{2})-[a-z]+-\d$/u;
42
+ const AWS_PRINCIPAL_ARN = /^arn:(aws|aws-cn|aws-us-gov):iam::(\d{12}):role\/[A-Za-z0-9+=,.@_/-]+$/u;
43
+ const GITHUB_OIDC_PROVIDER_ARN = /^arn:(aws|aws-cn|aws-us-gov):iam::(\d{12}):oidc-provider\/token[.]actions[.]githubusercontent[.]com$/u;
44
+ const GITHUB_SUBJECT = /^repo:[A-Za-z0-9_.-]+@[1-9][0-9]{0,19}\/[A-Za-z0-9_.-]+@[1-9][0-9]{0,19}:environment:(development|staging|production)$/u;
45
+ /** Native lifecycle required by the generated machine-authorization nested
46
+ * stack. Recovery-only and stack-policy mutation APIs remain ungranted. */
47
+ exports.STAGE_AUTHORITY_CLOUDFORMATION_LIFECYCLE_ACTIONS = Object.freeze([
48
+ 'cloudformation:CreateStack',
49
+ 'cloudformation:DeleteStack',
50
+ 'cloudformation:UpdateStack',
51
+ ].sort(compareStrings));
52
+ /** EventBridge archives expose no request-tag or resource-tag IAM condition
53
+ * keys. Their lifecycle is contained by the exact canonical archive ARN and
54
+ * requested Region instead. */
55
+ exports.STAGE_AUTHORITY_EVENT_ARCHIVE_CREATE_ACTIONS = Object.freeze([
56
+ 'events:CreateArchive',
57
+ ]);
58
+ exports.STAGE_AUTHORITY_EVENT_ARCHIVE_RESOURCE_ACTIONS = Object.freeze(['events:DeleteArchive', 'events:UpdateArchive'].sort(compareStrings));
59
+ exports.STAGE_AUTHORITY_LAMBDA_DEPLOYMENT_INVOKE_ACTIONS = Object.freeze([
60
+ 'lambda:InvokeFunction',
61
+ ]);
62
+ exports.STAGE_AUTHORITY_CLOUDFRONT_INVALIDATION_ACTIONS = Object.freeze([
63
+ 'cloudfront:CreateInvalidation',
64
+ ]);
65
+ exports.STAGE_AUTHORITY_WAF_LOGGING_ACTIONS = Object.freeze([
66
+ 'wafv2:PutLoggingConfiguration',
67
+ ]);
68
+ /** SST's CloudFront-scope WAF logging component owns exactly one generated log
69
+ * group in the edge control Region. These resource-capable actions stay bound
70
+ * to that generated ARN below. */
71
+ exports.STAGE_AUTHORITY_EDGE_WAF_LOG_GROUP_ACTIONS = Object.freeze([
72
+ 'logs:CreateLogGroup',
73
+ 'logs:DeleteLogGroup',
74
+ 'logs:DeleteRetentionPolicy',
75
+ 'logs:PutRetentionPolicy',
76
+ 'logs:TagLogGroup',
77
+ 'logs:TagResource',
78
+ 'logs:UntagLogGroup',
79
+ 'logs:UntagResource',
80
+ ].sort(compareStrings));
81
+ /** CloudWatch Logs exposes these WAF delivery mutations only at account scope.
82
+ * In particular, PutResourcePolicy supports neither resource ARNs nor
83
+ * action-specific condition keys. Requested Region and reviewed generated
84
+ * source are therefore the strongest honest controls for that one exception. */
85
+ exports.STAGE_AUTHORITY_EDGE_WAF_LOG_DELIVERY_MUTATION_ACTIONS = Object.freeze([
86
+ 'logs:CreateLogDelivery',
87
+ 'logs:DeleteLogDelivery',
88
+ 'logs:PutResourcePolicy',
89
+ ].sort(compareStrings));
90
+ exports.STAGE_AUTHORITY_BUDGET_MUTATION_ACTIONS = Object.freeze(['budgets:ModifyBudget', 'budgets:TagResource', 'budgets:UntagResource'].sort(compareStrings));
91
+ exports.STAGE_AUTHORITY_RDS_EVENT_SUBSCRIPTION_SOURCE_ACTIONS = Object.freeze([
92
+ 'rds:AddSourceIdentifierToSubscription',
93
+ 'rds:RemoveSourceIdentifierFromSubscription',
94
+ ].sort(compareStrings));
95
+ exports.STAGE_AUTHORITY_KMS_ROTATION_ACTIONS = Object.freeze([
96
+ 'kms:EnableKeyRotation',
97
+ ]);
98
+ /** Commercial AWS exposes CloudFront, CloudFront-scope WAF, and edge
99
+ * certificates through us-east-1 even when the application stage is deployed
100
+ * elsewhere. This batch intentionally preserves the selected Region for
101
+ * nonstandard partitions; CloudFront is not available inside GovCloud. */
102
+ function edgeControlRegion(partition, deploymentRegion) {
103
+ return partition === 'aws' ? 'us-east-1' : deploymentRegion;
104
+ }
105
+ /** Cost Explorer creation is account-global, while existing anomaly controls
106
+ * support account-bound ARNs. These exact verbs are admitted only to the
107
+ * production stage role below. */
108
+ exports.STAGE_AUTHORITY_COST_ANOMALY_READ_ACTIONS = Object.freeze([
109
+ 'ce:GetAnomalyMonitors',
110
+ 'ce:GetAnomalySubscriptions',
111
+ 'ce:ListTagsForResource',
112
+ ].sort(compareStrings));
113
+ exports.STAGE_AUTHORITY_COST_ANOMALY_CREATE_ACTIONS = Object.freeze(['ce:CreateAnomalyMonitor', 'ce:CreateAnomalySubscription'].sort(compareStrings));
114
+ exports.STAGE_AUTHORITY_COST_ANOMALY_RESOURCE_WRITE_ACTIONS = Object.freeze([
115
+ 'ce:DeleteAnomalyMonitor',
116
+ 'ce:DeleteAnomalySubscription',
117
+ 'ce:TagResource',
118
+ 'ce:UntagResource',
119
+ 'ce:UpdateAnomalyMonitor',
120
+ 'ce:UpdateAnomalySubscription',
121
+ ].sort(compareStrings));
122
+ exports.STAGE_AUTHORITY_COST_ANOMALY_WRITE_ACTIONS = Object.freeze([
123
+ ...exports.STAGE_AUTHORITY_COST_ANOMALY_CREATE_ACTIONS,
124
+ ...exports.STAGE_AUTHORITY_COST_ANOMALY_RESOURCE_WRITE_ACTIONS,
125
+ ].sort(compareStrings));
126
+ exports.STAGE_AUTHORITY_REGIONAL_READ_ACTIONS = Object.freeze([
127
+ 'access-analyzer:Get*',
128
+ 'access-analyzer:List*',
129
+ 'acm:Describe*',
130
+ 'acm:Get*',
131
+ 'acm:List*',
132
+ 'application-autoscaling:Describe*',
133
+ 'appconfig:Get*',
134
+ 'appconfig:List*',
135
+ 'backup:Describe*',
136
+ 'backup:Get*',
137
+ 'backup:List*',
138
+ 'cloudformation:Describe*',
139
+ 'cloudformation:Get*',
140
+ 'cloudformation:List*',
141
+ 'cloudformation:ValidateTemplate',
142
+ 'cloudwatch:Describe*',
143
+ 'cloudwatch:Get*',
144
+ 'cloudwatch:List*',
145
+ 'cognito-idp:Describe*',
146
+ 'cognito-idp:Get*',
147
+ 'cognito-idp:List*',
148
+ 'dynamodb:Describe*',
149
+ 'dynamodb:Get*',
150
+ 'dynamodb:List*',
151
+ 'ec2:Describe*',
152
+ 'ec2:GetInstanceUefiData',
153
+ 'ec2:GetResourcePolicy',
154
+ 'ecr:Describe*',
155
+ 'ecr:Get*',
156
+ 'ecr:List*',
157
+ 'ecs:Describe*',
158
+ 'ecs:List*',
159
+ 'elasticloadbalancing:Describe*',
160
+ 'events:Describe*',
161
+ 'events:List*',
162
+ 'kms:Describe*',
163
+ 'kms:Get*',
164
+ 'kms:List*',
165
+ 'lambda:Get*',
166
+ 'lambda:List*',
167
+ 'logs:Describe*',
168
+ 'logs:FilterLogEvents',
169
+ 'logs:Get*',
170
+ 'logs:List*',
171
+ 'rds:Describe*',
172
+ 'rds:List*',
173
+ 'secretsmanager:Describe*',
174
+ 'secretsmanager:Get*',
175
+ 'secretsmanager:List*',
176
+ 'servicequotas:Get*',
177
+ 'servicequotas:List*',
178
+ 'servicediscovery:Get*',
179
+ 'servicediscovery:List*',
180
+ 'sns:Get*',
181
+ 'sns:List*',
182
+ 'sqs:Get*',
183
+ 'sqs:List*',
184
+ 'ssm:Describe*',
185
+ 'ssm:Get*',
186
+ 'ssm:List*',
187
+ 'states:Describe*',
188
+ 'states:List*',
189
+ ].sort(compareStrings));
190
+ exports.STAGE_AUTHORITY_GLOBAL_READ_ACTIONS = Object.freeze([
191
+ 'apigateway:GET',
192
+ 'budgets:Describe*',
193
+ 'budgets:List*',
194
+ 'budgets:ViewBudget',
195
+ 'cloudfront-keyvaluestore:DescribeKeyValueStore',
196
+ 'cloudfront-keyvaluestore:GetKey',
197
+ 'cloudfront-keyvaluestore:ListKeys',
198
+ 'cloudfront:Describe*',
199
+ 'cloudfront:Get*',
200
+ 'cloudfront:List*',
201
+ 'iam:Get*',
202
+ 'iam:List*',
203
+ 'route53:Get*',
204
+ 'route53:List*',
205
+ 's3:Get*',
206
+ 's3:List*',
207
+ 'sts:GetCallerIdentity',
208
+ 'tag:GetResources',
209
+ 'wafv2:Get*',
210
+ 'wafv2:List*',
211
+ ].sort(compareStrings));
212
+ /** CloudFront-scope WAF publishes its observability signals through the edge
213
+ * control Region. Keep this read surface exact instead of broadening every
214
+ * regional read when the application Region differs from us-east-1. */
215
+ exports.STAGE_AUTHORITY_EDGE_OBSERVABILITY_READ_ACTIONS = Object.freeze([
216
+ 'cloudwatch:GetMetricData',
217
+ 'logs:DescribeLogGroups',
218
+ 'logs:DescribeResourcePolicies',
219
+ ].sort(compareStrings));
220
+ /** A deployment role is a temporary infrastructure administrator, not a
221
+ * runtime workload identity. Routine regional services therefore use one
222
+ * stable service-family contract instead of a provider-call inventory. The
223
+ * families whose policy, protection, launch, or trust semantics need tighter
224
+ * treatment remain on the bounded action sets below. */
225
+ exports.STAGE_AUTHORITY_BROAD_REGIONAL_SERVICE_ACTIONS = Object.freeze([
226
+ 'acm:*',
227
+ 'apigateway:*',
228
+ 'appconfig:*',
229
+ 'application-autoscaling:*',
230
+ 'cloudwatch:*',
231
+ 'cognito-idp:*',
232
+ 'dynamodb:*',
233
+ 'ecr:*',
234
+ 'ecs:*',
235
+ 'elasticloadbalancing:*',
236
+ 'events:*',
237
+ 'logs:*',
238
+ 'rds:*',
239
+ 'secretsmanager:*',
240
+ 'servicediscovery:*',
241
+ 'sns:*',
242
+ 'sqs:*',
243
+ 'ssm:*',
244
+ 'states:*',
245
+ ].sort(compareStrings));
246
+ exports.STAGE_AUTHORITY_BACKUP_INFRASTRUCTURE_ACTIONS = Object.freeze([
247
+ 'backup:CreateBackupPlan',
248
+ 'backup:CreateBackupSelection',
249
+ 'backup:CreateBackupVault',
250
+ 'backup:DeleteBackupPlan',
251
+ 'backup:DeleteBackupSelection',
252
+ 'backup:DeleteBackupVault',
253
+ 'backup:StartBackupJob',
254
+ 'backup:TagResource',
255
+ 'backup:UntagResource',
256
+ 'backup:UpdateBackupPlan',
257
+ ].sort(compareStrings));
258
+ exports.STAGE_AUTHORITY_EC2_INFRASTRUCTURE_ACTIONS = Object.freeze([
259
+ 'ec2:AllocateAddress',
260
+ 'ec2:Associate*',
261
+ 'ec2:Attach*',
262
+ 'ec2:Authorize*',
263
+ 'ec2:Create*',
264
+ 'ec2:Delete*',
265
+ 'ec2:Detach*',
266
+ 'ec2:Disassociate*',
267
+ 'ec2:ImportKeyPair',
268
+ 'ec2:Modify*',
269
+ 'ec2:ReleaseAddress',
270
+ 'ec2:Replace*',
271
+ 'ec2:Revoke*',
272
+ 'ec2:RunInstances',
273
+ 'ec2:StartInstances',
274
+ 'ec2:StopInstances',
275
+ 'ec2:TerminateInstances',
276
+ 'ec2:UpdateSecurityGroupRuleDescriptions*',
277
+ ].sort(compareStrings));
278
+ exports.STAGE_AUTHORITY_KMS_INFRASTRUCTURE_ACTIONS = Object.freeze([
279
+ 'kms:CreateAlias',
280
+ 'kms:CreateKey',
281
+ 'kms:DeleteAlias',
282
+ 'kms:TagResource',
283
+ 'kms:UntagResource',
284
+ 'kms:UpdateAlias',
285
+ 'kms:UpdateKeyDescription',
286
+ ].sort(compareStrings));
287
+ /** AddPermission, RemovePermission, and InvokeFunction remain in exact
288
+ * statements so the family allow cannot bypass Lambda resource-policy and
289
+ * deployment-smoke constraints. */
290
+ exports.STAGE_AUTHORITY_LAMBDA_INFRASTRUCTURE_ACTIONS = Object.freeze([
291
+ 'lambda:Create*',
292
+ 'lambda:Delete*',
293
+ 'lambda:PublishVersion',
294
+ 'lambda:Put*',
295
+ 'lambda:TagResource',
296
+ 'lambda:UntagResource',
297
+ 'lambda:Update*',
298
+ ].sort(compareStrings));
299
+ exports.STAGE_AUTHORITY_REGIONAL_WRITE_ACTIONS = Object.freeze([
300
+ ...exports.STAGE_AUTHORITY_BACKUP_INFRASTRUCTURE_ACTIONS,
301
+ ...exports.STAGE_AUTHORITY_BROAD_REGIONAL_SERVICE_ACTIONS,
302
+ ...exports.STAGE_AUTHORITY_EC2_INFRASTRUCTURE_ACTIONS,
303
+ ...exports.STAGE_AUTHORITY_KMS_INFRASTRUCTURE_ACTIONS,
304
+ ...exports.STAGE_AUTHORITY_LAMBDA_INFRASTRUCTURE_ACTIONS,
305
+ ].sort(compareStrings));
306
+ exports.STAGE_AUTHORITY_UNSCOPED_CLOUDFRONT_CREATE_ACTIONS = Object.freeze([
307
+ 'cloudfront:CreateCachePolicy',
308
+ 'cloudfront:CreateDistribution',
309
+ 'cloudfront:CreateFunction',
310
+ 'cloudfront:CreateKeyValueStore',
311
+ 'cloudfront:CreateOriginAccessControl',
312
+ ].sort(compareStrings));
313
+ exports.STAGE_AUTHORITY_S3_REGIONAL_MUTATION_ACTIONS = Object.freeze([
314
+ 's3:AbortMultipartUpload',
315
+ 's3:DeleteBucket',
316
+ 's3:DeleteBucketPolicy',
317
+ 's3:DeleteObject',
318
+ 's3:DeleteObjectTagging',
319
+ 's3:DeleteObjectVersion',
320
+ 's3:PutBucket*',
321
+ 's3:PutEncryptionConfiguration',
322
+ 's3:PutLifecycleConfiguration',
323
+ 's3:PutObject',
324
+ 's3:PutObjectTagging',
325
+ 's3:RestoreObject',
326
+ ].sort(compareStrings));
327
+ exports.STAGE_AUTHORITY_WAF_EDGE_MUTATION_ACTIONS = Object.freeze([
328
+ 'wafv2:AssociateWebACL',
329
+ 'wafv2:Create*',
330
+ 'wafv2:Delete*',
331
+ 'wafv2:DisassociateWebACL',
332
+ 'wafv2:TagResource',
333
+ 'wafv2:UntagResource',
334
+ 'wafv2:Update*',
335
+ ].sort(compareStrings));
336
+ /** These APIs establish unsupported replicas, global resources, alternate
337
+ * database topologies, integrations, or cross-Region copies. The generated
338
+ * architecture does not use them. */
339
+ exports.STAGE_AUTHORITY_UNSUPPORTED_REPLICATION_AND_TOPOLOGY_ACTIONS = Object.freeze([
340
+ 'backup:StartCopyJob',
341
+ 'backup:UpdateRegionSettings',
342
+ 'dynamodb:AssociateTableReplica',
343
+ 'dynamodb:CreateGlobalTable',
344
+ 'dynamodb:CreateGlobalTableWitness',
345
+ 'dynamodb:CreateTableReplica',
346
+ 'dynamodb:DeleteGlobalTableWitness',
347
+ 'dynamodb:DeleteTableReplica',
348
+ 'dynamodb:UpdateGlobalTable',
349
+ 'dynamodb:UpdateGlobalTableSettings',
350
+ 'dynamodb:UpdateTableReplicaAutoScaling',
351
+ 'ecr:PutReplicationConfiguration',
352
+ 'kms:ReplicateKey',
353
+ 'kms:UpdatePrimaryRegion',
354
+ 'rds:CopyDBClusterSnapshot',
355
+ 'rds:CopyDBSnapshot',
356
+ 'rds:CreateBlueGreenDeployment',
357
+ 'rds:CreateDBInstanceReadReplica',
358
+ 'rds:CreateDBShardGroup',
359
+ 'rds:CreateGlobalCluster',
360
+ 'rds:CreateIntegration',
361
+ 'rds:CrossRegionCommunication',
362
+ 'rds:DeleteBlueGreenDeployment',
363
+ 'rds:DeleteDBShardGroup',
364
+ 'rds:DeleteGlobalCluster',
365
+ 'rds:DeleteIntegration',
366
+ 'rds:FailoverGlobalCluster',
367
+ 'rds:ModifyDBShardGroup',
368
+ 'rds:ModifyGlobalCluster',
369
+ 'rds:ModifyIntegration',
370
+ 'rds:PromoteReadReplica',
371
+ 'rds:PromoteReadReplicaDBCluster',
372
+ 'rds:RebootDBShardGroup',
373
+ 'rds:RemoveFromGlobalCluster',
374
+ 'rds:StartDBInstanceAutomatedBackupsReplication',
375
+ 'rds:StopDBInstanceAutomatedBackupsReplication',
376
+ 'rds:SwitchoverBlueGreenDeployment',
377
+ 'rds:SwitchoverGlobalCluster',
378
+ 'rds:SwitchoverReadReplica',
379
+ 's3:PutReplicationConfiguration',
380
+ 'secretsmanager:RemoveRegionsFromReplication',
381
+ 'secretsmanager:ReplicateSecretToRegions',
382
+ 'secretsmanager:StopReplicationToReplica',
383
+ ].sort(compareStrings));
384
+ exports.STAGE_AUTHORITY_GLOBAL_WRITE_ACTIONS = Object.freeze([
385
+ 'route53:ChangeResourceRecordSets',
386
+ 'route53:ChangeTagsForResource',
387
+ 'route53:CreateHealthCheck',
388
+ 'route53:DeleteHealthCheck',
389
+ 'route53:UpdateHealthCheck',
390
+ ].sort(compareStrings));
391
+ exports.STAGE_AUTHORITY_ACCOUNT_SCOPED_GLOBAL_WRITE_ACTIONS = Object.freeze([
392
+ 'cloudfront:DeleteCachePolicy',
393
+ 'cloudfront:DeleteDistribution',
394
+ 'cloudfront:DeleteFunction',
395
+ 'cloudfront:DeleteKeyValueStore',
396
+ 'cloudfront:DeleteOriginAccessControl',
397
+ 'cloudfront:PublishFunction',
398
+ 'cloudfront:TagResource',
399
+ 'cloudfront:UntagResource',
400
+ 'cloudfront:UpdateCachePolicy',
401
+ 'cloudfront:UpdateDistribution',
402
+ 'cloudfront:UpdateFunction',
403
+ 'cloudfront:UpdateKeyValueStore',
404
+ 'cloudfront:UpdateOriginAccessControl',
405
+ 'cloudfront-keyvaluestore:DeleteKey',
406
+ 'cloudfront-keyvaluestore:PutKey',
407
+ 'cloudfront-keyvaluestore:UpdateKeys',
408
+ ].sort(compareStrings));
409
+ exports.STAGE_RUNTIME_REGIONAL_ACTIONS = Object.freeze([
410
+ 'appconfig:GetLatestConfiguration',
411
+ 'appconfig:StartConfigurationSession',
412
+ 'cloudwatch:DescribeAlarms',
413
+ 'cloudwatch:PutMetricData',
414
+ 'cognito-idp:CreateUserPoolClient',
415
+ 'cognito-idp:DeleteUserPoolClient',
416
+ 'cognito-idp:DescribeUserPoolClient',
417
+ 'cognito-idp:ListUserPoolClients',
418
+ 'dynamodb:BatchGetItem',
419
+ 'dynamodb:BatchWriteItem',
420
+ 'dynamodb:ConditionCheckItem',
421
+ 'dynamodb:DeleteItem',
422
+ 'dynamodb:DescribeTable',
423
+ 'dynamodb:GetItem',
424
+ 'dynamodb:GetRecords',
425
+ 'dynamodb:GetShardIterator',
426
+ 'dynamodb:PutItem',
427
+ 'dynamodb:Query',
428
+ 'dynamodb:Scan',
429
+ 'dynamodb:TransactWriteItems',
430
+ 'dynamodb:UpdateItem',
431
+ 'ec2:AssignPrivateIpAddresses',
432
+ 'ec2:CreateNetworkInterface',
433
+ 'ec2:DeleteNetworkInterface',
434
+ 'ec2:DescribeNetworkInterfaces',
435
+ 'ec2:DescribeSubnets',
436
+ 'ec2:UnassignPrivateIpAddresses',
437
+ 'ecr:BatchCheckLayerAvailability',
438
+ 'ecr:BatchGetImage',
439
+ 'ecr:GetAuthorizationToken',
440
+ 'ecr:GetDownloadUrlForLayer',
441
+ 'ecs:DescribeTasks',
442
+ 'ecs:RunTask',
443
+ 'ecs:StopTask',
444
+ 'events:DescribeRule',
445
+ 'events:PutEvents',
446
+ 'events:PutRule',
447
+ 'events:PutTargets',
448
+ 'kms:Decrypt',
449
+ 'kms:DescribeKey',
450
+ 'kms:Encrypt',
451
+ 'kms:GenerateDataKey',
452
+ 'lambda:InvokeFunction',
453
+ 'logs:CreateLogDelivery',
454
+ 'logs:CreateLogStream',
455
+ 'logs:DeleteLogDelivery',
456
+ 'logs:DescribeLogGroups',
457
+ 'logs:DescribeResourcePolicies',
458
+ 'logs:GetLogDelivery',
459
+ 'logs:ListLogDeliveries',
460
+ 'logs:PutLogEvents',
461
+ 'logs:PutResourcePolicy',
462
+ 'logs:UpdateLogDelivery',
463
+ 'rds-db:connect',
464
+ 's3:AbortMultipartUpload',
465
+ 's3:DeleteObject',
466
+ 's3:DeleteObjectVersion',
467
+ 's3:GetObject',
468
+ 's3:GetObjectVersion',
469
+ 's3:ListBucket',
470
+ 's3:ListBucketVersions',
471
+ 's3:PutObject',
472
+ 'secretsmanager:DeleteSecret',
473
+ 'secretsmanager:DescribeSecret',
474
+ 'secretsmanager:GetSecretValue',
475
+ 'secretsmanager:PutSecretValue',
476
+ 'secretsmanager:UpdateSecretVersionStage',
477
+ 'sns:Publish',
478
+ 'sqs:ChangeMessageVisibility',
479
+ 'sqs:DeleteMessage',
480
+ 'sqs:GetQueueAttributes',
481
+ 'sqs:GetQueueUrl',
482
+ 'sqs:ReceiveMessage',
483
+ 'sqs:SendMessage',
484
+ 'ssmmessages:CreateControlChannel',
485
+ 'ssmmessages:CreateDataChannel',
486
+ 'ssmmessages:OpenControlChannel',
487
+ 'ssmmessages:OpenDataChannel',
488
+ 'ssm:GetParameter',
489
+ 'ssm:GetParameters',
490
+ 'states:DescribeExecution',
491
+ 'states:SendTaskFailure',
492
+ 'states:SendTaskHeartbeat',
493
+ 'states:SendTaskSuccess',
494
+ 'states:StartExecution',
495
+ ].sort(compareStrings));
496
+ exports.STAGE_RUNTIME_GLOBAL_ACTIONS = Object.freeze(['cloudfront:CreateInvalidation'].sort(compareStrings));
497
+ exports.STAGE_AUTHORITY_REGIONAL_MUTATION_FAMILIES = Object.freeze([
498
+ ...new Set([
499
+ ...exports.STAGE_AUTHORITY_REGIONAL_WRITE_ACTIONS,
500
+ ...exports.STAGE_AUTHORITY_CLOUDFORMATION_LIFECYCLE_ACTIONS,
501
+ ]
502
+ .map((action) => `${action.split(':', 1)[0]}:*`)
503
+ .filter((family) => !['acm:*', 'cloudwatch:*', 'logs:*'].includes(family))),
504
+ 'cloudwatch:Delete*',
505
+ 'cloudwatch:Disable*',
506
+ 'cloudwatch:Enable*',
507
+ 'cloudwatch:Put*',
508
+ 'cloudwatch:Set*',
509
+ 'cloudwatch:Start*',
510
+ 'cloudwatch:Stop*',
511
+ 'cloudwatch:TagResource',
512
+ 'cloudwatch:UntagResource',
513
+ ].sort(compareStrings));
514
+ /** Logs application resources live in the deployment Region, while native WAF
515
+ * delivery resources live in the CloudFront edge control Region. The allow
516
+ * statements remain separate; this family exists only to deny mutations in a
517
+ * third Region. */
518
+ exports.STAGE_AUTHORITY_LOGS_MUTATION_FAMILIES = Object.freeze([
519
+ 'logs:Associate*',
520
+ 'logs:Cancel*',
521
+ 'logs:Create*',
522
+ 'logs:Delete*',
523
+ 'logs:Disassociate*',
524
+ 'logs:Put*',
525
+ 'logs:Start*',
526
+ 'logs:Stop*',
527
+ 'logs:Tag*',
528
+ 'logs:Untag*',
529
+ 'logs:Update*',
530
+ ].sort(compareStrings));
531
+ exports.STAGE_RUNTIME_REGIONAL_SERVICE_FAMILIES = Object.freeze([
532
+ ...new Set(exports.STAGE_RUNTIME_REGIONAL_ACTIONS.map((action) => `${action.split(':', 1)[0]}:*`)),
533
+ ].sort(compareStrings));
534
+ exports.STAGE_AUTHORITY_APPROVED_AWS_MANAGED_POLICIES = Object.freeze([
535
+ 'AmazonSSMManagedInstanceCore',
536
+ 'service-role/AmazonECSTaskExecutionRolePolicy',
537
+ 'service-role/AWSLambdaBasicDurableExecutionRolePolicy',
538
+ 'service-role/AWSLambdaBasicExecutionRole',
539
+ 'service-role/AWSLambdaVPCAccessExecutionRole',
540
+ ]);
541
+ exports.STAGE_AUTHORITY_WORKLOAD_IDENTITY_POLICY_ACTIONS = Object.freeze([
542
+ 'iam:AttachRolePolicy',
543
+ 'iam:DeleteRolePolicy',
544
+ 'iam:DetachRolePolicy',
545
+ 'iam:PutRolePolicy',
546
+ ].sort(compareStrings));
547
+ const IAM_AUTHORITY_MUTATION_ACTIONS = Object.freeze([
548
+ 'iam:AddRoleToInstanceProfile',
549
+ 'iam:AttachRolePolicy',
550
+ 'iam:CreatePolicyVersion',
551
+ 'iam:DeletePolicy',
552
+ 'iam:DeletePolicyVersion',
553
+ 'iam:DeleteRole',
554
+ 'iam:DeleteRolePermissionsBoundary',
555
+ 'iam:DeleteRolePolicy',
556
+ 'iam:DetachRolePolicy',
557
+ 'iam:PutRolePermissionsBoundary',
558
+ 'iam:PutRolePolicy',
559
+ 'iam:RemoveRoleFromInstanceProfile',
560
+ 'iam:SetDefaultPolicyVersion',
561
+ 'iam:TagPolicy',
562
+ 'iam:TagRole',
563
+ 'iam:UntagPolicy',
564
+ 'iam:UntagRole',
565
+ 'iam:UpdateAssumeRolePolicy',
566
+ 'iam:UpdateRole',
567
+ 'iam:UpdateRoleDescription',
568
+ ].sort(compareStrings));
569
+ /** Compact service-family scope guards keep mutation outside the selected AWS
570
+ * account/Region closed. They intentionally do not claim same-account stage
571
+ * isolation, because native SST owns account-shared bootstrap resources. */
572
+ exports.STAGE_AUTHORITY_SCOPE_GUARD_MUTATION_FAMILIES = Object.freeze([
573
+ ...new Set([
574
+ ...exports.STAGE_AUTHORITY_REGIONAL_MUTATION_FAMILIES,
575
+ 'acm:*',
576
+ 'apigateway:*',
577
+ 'cloudfront:*',
578
+ 'iam:*',
579
+ 'logs:*',
580
+ 'route53:*',
581
+ 's3:*',
582
+ 'wafv2:*',
583
+ ]),
584
+ ].sort(compareStrings));
585
+ /** SST 4.17.1 resolves the generated nonproduction VPC's EC2 NAT image from
586
+ * this commercial-partition fck-nat publisher. The generated development
587
+ * bastion reuses that NAT instance, while production uses managed NAT. No
588
+ * publisher is admitted in partitions where the pinned source is unproven. */
589
+ exports.STAGE_AUTHORITY_SST_417_EXTERNAL_AMI_OWNERS_BY_PARTITION = Object.freeze({
590
+ aws: Object.freeze(['568608671756']),
591
+ 'aws-cn': Object.freeze([]),
592
+ 'aws-us-gov': Object.freeze([]),
593
+ });
594
+ /** Preserve the generic cross-account guard for ordinary EC2 mutations while
595
+ * routing RunInstances through its resource-aware statements below. A service
596
+ * family wildcard would otherwise reject SST's deliberately external AMI. */
597
+ exports.STAGE_AUTHORITY_CROSS_ACCOUNT_EC2_MUTATION_ACTIONS = Object.freeze(exports.STAGE_AUTHORITY_REGIONAL_WRITE_ACTIONS.filter((action) => action.startsWith('ec2:') && action !== 'ec2:RunInstances'));
598
+ exports.STAGE_AUTHORITY_CROSS_ACCOUNT_MUTATION_ACTIONS = Object.freeze([
599
+ ...exports.STAGE_AUTHORITY_SCOPE_GUARD_MUTATION_FAMILIES.filter((action) => action !== 'ec2:*'),
600
+ ...exports.STAGE_AUTHORITY_CROSS_ACCOUNT_EC2_MUTATION_ACTIONS,
601
+ ].sort(compareStrings));
602
+ /** A deliberately narrow destructive set for same-account stage defense in
603
+ * depth. It protects named/tagged production resources without pretending the
604
+ * deployment roles form a complete IAM boundary. */
605
+ exports.STAGE_AUTHORITY_CROSS_STAGE_DESTRUCTIVE_ACTIONS = Object.freeze([
606
+ 'backup:Delete*',
607
+ 'budgets:ModifyBudget',
608
+ 'budgets:TagResource',
609
+ 'budgets:UntagResource',
610
+ 'cloudformation:DeleteStack',
611
+ 'cloudformation:UpdateStack',
612
+ 'cloudfront:CreateInvalidation',
613
+ 'cloudfront:Delete*',
614
+ 'cloudfront:PublishFunction',
615
+ 'cloudfront:TagResource',
616
+ 'cloudfront:UntagResource',
617
+ 'cloudfront:Update*',
618
+ 'cognito-idp:Delete*',
619
+ 'dynamodb:Delete*',
620
+ 'ec2:Replace*',
621
+ 'ecr:BatchDeleteImage',
622
+ 'ecr:Delete*',
623
+ 'events:DeleteArchive',
624
+ 'events:DeleteEventBus',
625
+ 'events:DeleteRule',
626
+ 'events:RemoveTargets',
627
+ 'events:UpdateArchive',
628
+ 'events:UpdateEventBus',
629
+ 'iam:Delete*',
630
+ 'kms:ScheduleKeyDeletion',
631
+ 'lambda:Delete*',
632
+ 'rds:Delete*',
633
+ 'rds:ModifyDBCluster',
634
+ 'rds:ModifyDBInstance',
635
+ 'rds:AddSourceIdentifierToSubscription',
636
+ 'rds:RemoveSourceIdentifierFromSubscription',
637
+ 's3:AbortMultipartUpload',
638
+ 's3:CreateBucket',
639
+ 's3:DeleteBucket',
640
+ 's3:DeleteBucketPolicy',
641
+ 's3:DeleteObject*',
642
+ 's3:PutBucket*',
643
+ 's3:PutObject*',
644
+ 's3:RestoreObject',
645
+ 'secretsmanager:DeleteSecret',
646
+ 'servicediscovery:DeleteNamespace',
647
+ 'sqs:DeleteQueue',
648
+ 'ssm:DeleteParameter*',
649
+ 'states:DeleteStateMachine',
650
+ 'wafv2:Delete*',
651
+ 'wafv2:DisassociateWebACL',
652
+ 'wafv2:PutLoggingConfiguration',
653
+ 'wafv2:Update*',
654
+ ].sort(compareStrings));
655
+ /** Existing-resource mutations must either retain the exact stage tag or use a
656
+ * generated stage/SST shared resource identity. Initial create/tag calls are
657
+ * governed by their request-tag or exact-resource statements instead. */
658
+ exports.STAGE_AUTHORITY_UNTAGGED_DESTRUCTIVE_ACTIONS = Object.freeze([
659
+ ...exports.STAGE_AUTHORITY_CROSS_STAGE_DESTRUCTIVE_ACTIONS.filter((action) => !action.startsWith('cloudfront:') && action !== 's3:CreateBucket'),
660
+ 'cloudfront:DeleteDistribution',
661
+ 'cloudfront:DeleteFunction',
662
+ 'cloudfront:PublishFunction',
663
+ 'cloudfront:UpdateDistribution',
664
+ 'cloudfront:UpdateFunction',
665
+ ].sort(compareStrings));
666
+ /** Operations whose target already exposes a stage tag may use the broader
667
+ * deployer contract. A known different-stage tag must still win as an explicit
668
+ * deny, including for provider calls that are not destructive by name. */
669
+ exports.STAGE_AUTHORITY_KNOWN_STAGE_MUTATION_ACTIONS = Object.freeze([
670
+ ...new Set([
671
+ ...exports.STAGE_AUTHORITY_REGIONAL_MUTATION_FAMILIES,
672
+ 'acm:*',
673
+ 'budgets:*',
674
+ 'cloudformation:*',
675
+ 'cloudfront:*',
676
+ 'iam:*',
677
+ 'logs:*',
678
+ 's3:*',
679
+ 'wafv2:*',
680
+ ]),
681
+ ].sort(compareStrings));
682
+ exports.STAGE_AUTHORITY_S3_BUCKET_POLICY_MUTATION_ACTIONS = Object.freeze(['s3:DeleteBucketPolicy', 's3:PutBucketPolicy'].sort(compareStrings));
683
+ const PRIVILEGE_CONSTRUCTION_ACTIONS = Object.freeze([
684
+ 'account:*',
685
+ 'iam:AttachGroupPolicy',
686
+ 'iam:AttachUserPolicy',
687
+ 'iam:CreateAccessKey',
688
+ 'iam:CreateGroup',
689
+ 'iam:CreateLoginProfile',
690
+ 'iam:CreatePolicy',
691
+ 'iam:CreatePolicyVersion',
692
+ 'iam:CreateUser',
693
+ 'iam:DeleteRolePermissionsBoundary',
694
+ 'iam:PutGroupPolicy',
695
+ 'iam:PutUserPermissionsBoundary',
696
+ 'iam:PutUserPolicy',
697
+ 'iam:SetDefaultPolicyVersion',
698
+ 'organizations:*',
699
+ 'sts:AssumeRole',
700
+ ].sort(compareStrings));
701
+ const RUNTIME_PRIVILEGE_ACTIONS = Object.freeze([
702
+ ...PRIVILEGE_CONSTRUCTION_ACTIONS,
703
+ 'iam:AttachRolePolicy',
704
+ 'iam:CreateInstanceProfile',
705
+ 'iam:CreateRole',
706
+ 'iam:DeleteRole',
707
+ 'iam:DeleteRolePolicy',
708
+ 'iam:DetachRolePolicy',
709
+ 'iam:PutRolePermissionsBoundary',
710
+ 'iam:PutRolePolicy',
711
+ 'iam:UpdateAssumeRolePolicy',
712
+ ].sort(compareStrings));
713
+ /** Permission-management actions that the generated lifecycle must call. Each
714
+ * exception is named exactly here and remains subject to the resource and
715
+ * condition containment statements below. */
716
+ exports.STAGE_AUTHORITY_REVIEWED_PERMISSION_MANAGEMENT_ACTIONS = Object.freeze([
717
+ 'iam:AttachRolePolicy',
718
+ 'iam:DeleteRolePolicy',
719
+ 'iam:DetachRolePolicy',
720
+ 'iam:PutRolePermissionsBoundary',
721
+ 'iam:PutRolePolicy',
722
+ 'kms:CreateGrant',
723
+ 'lambda:AddPermission',
724
+ 'lambda:RemovePermission',
725
+ 'logs:PutResourcePolicy',
726
+ 's3:DeleteBucketPolicy',
727
+ 's3:PutBucketOwnershipControls',
728
+ 's3:PutBucketPolicy',
729
+ 's3:PutBucketPublicAccessBlock',
730
+ 'sns:SetTopicAttributes',
731
+ 'sqs:SetQueueAttributes',
732
+ ].sort(compareStrings));
733
+ /** Compact deny patterns covering permission-management actions admitted by
734
+ * wildcard lifecycle grants but not named as reviewed exceptions. The focused
735
+ * oracle derives every wildcard service family from the policy and proves each
736
+ * currently admitted permission-management action is reviewed or denied, and
737
+ * that no compact pattern also captures a retained read-only action. */
738
+ exports.STAGE_AUTHORITY_UNREVIEWED_PERMISSION_MANAGEMENT_ACTION_PATTERNS = Object.freeze([
739
+ 'apigateway:*AccessAssociation',
740
+ 'apigateway:*Certificate*Domain',
741
+ 'apigateway:DeletePortalProductSharingPolicy',
742
+ 'apigateway:PutPortalProductSharingPolicy',
743
+ 'apigateway:SetWebACL',
744
+ 'apigateway:Update*Policy',
745
+ 'backup:DeleteBackupVault*Policy',
746
+ 'backup:ListIndexedRecoveryPointsForSearch',
747
+ 'backup:PutBackupVault*Policy',
748
+ 'backup:SearchRecoveryPoint',
749
+ 'cloudfront:AllowVendedLogDeliveryForResource',
750
+ 'dynamodb:DeleteResourcePolicy',
751
+ 'dynamodb:PutResourcePolicy',
752
+ 'dynamodb:UpdateAbacStatus',
753
+ 'ec2:*Appliance*NatGateway',
754
+ 'ec2:*ImageBlockPublicAccess',
755
+ 'ec2:*OdbNetworkPeering',
756
+ 'ec2:*Permission',
757
+ 'ec2:*Resources*PlacementGroup',
758
+ 'ec2:*SnapshotBlockPublicAccess',
759
+ 'ec2:DeleteResourcePolicy',
760
+ 'ec2:ModifySnapshotAttribute',
761
+ 'ec2:ModifyVpcEndpointServicePermissions',
762
+ 'ec2:PutResourcePolicy',
763
+ 'ec2:ResetSnapshotAttribute',
764
+ 'ecr:DeleteRegistryPolicy',
765
+ 'ecr:DeleteRepositoryPolicy',
766
+ 'ecr:PutRegistryPolicy',
767
+ 'ecr:SetRepositoryPolicy',
768
+ 'ecs:AllowVendedLogDeliveryForResource',
769
+ 'elasticloadbalancing:AllowVendedLogDeliveryForResource',
770
+ 'events:*Permission',
771
+ 'kms:PutKeyPolicy',
772
+ 'kms:RetireGrant',
773
+ 'kms:RevokeGrant',
774
+ 'lambda:*LayerVersionPermission',
775
+ 'lambda:*Replication',
776
+ 'lambda:DeleteResourcePolicy',
777
+ 'lambda:PutResourcePolicy',
778
+ 'logs:DeleteResourcePolicy',
779
+ 'rds:AuthorizeDBSecurityGroupIngress',
780
+ 's3:AssociateAccessGrantsIdentityCenter',
781
+ 's3:BypassGovernanceRetention',
782
+ 's3:CreateAccessGrant*',
783
+ 's3:Delete*AccessPointPolicy*',
784
+ 's3:DeleteAccessGrant*',
785
+ 's3:DissociateAccessGrantsIdentityCenter',
786
+ 's3:ObjectOwnerOverrideToBucketOwner',
787
+ 's3:Put*AccessPointPolicy*',
788
+ 's3:Put*Acl',
789
+ 's3:PutAccessGrantsInstanceResourcePolicy',
790
+ 's3:PutAccessPointPublicAccessBlock',
791
+ 's3:PutAccountPublicAccessBlock',
792
+ 's3:UpdateAccessGrantsLocation',
793
+ 'secretsmanager:DeleteResourcePolicy',
794
+ 'secretsmanager:PutResourcePolicy',
795
+ 'secretsmanager:ValidateResourcePolicy',
796
+ 'sns:*Permission',
797
+ 'sqs:*Permission',
798
+ 'ssm:*ResourcePolicy',
799
+ 'ssm:ModifyDocumentPermission',
800
+ 'wafv2:DeletePermissionPolicy',
801
+ 'wafv2:PutPermissionPolicy',
802
+ ].sort(compareStrings));
803
+ const ADDITIONAL_RESOURCE_POLICY_AND_TRUST_EXPANSION_ACTIONS = Object.freeze([
804
+ 'ec2:AcceptTransitGatewayPeeringAttachment',
805
+ 'ec2:AcceptVpcPeeringConnection',
806
+ 'ec2:CreateTransitGatewayPeeringAttachment',
807
+ 'ec2:CreateVpcEndpointServiceConfiguration',
808
+ 'ec2:CreateVpcPeeringConnection',
809
+ 'ec2:DeleteVpcEndpointServiceConfigurations',
810
+ 'ec2:ModifyImageAttribute',
811
+ 'ec2:ModifyVpcEndpointServiceConfiguration',
812
+ 'ec2:ModifyVpcPeeringConnectionOptions',
813
+ 'ecr:PutReplicationConfiguration',
814
+ 'iam:UpdateAssumeRolePolicy',
815
+ 'logs:DeleteAccountPolicy',
816
+ 'logs:DeleteDeliveryDestinationPolicy',
817
+ 'logs:PutAccountPolicy',
818
+ 'logs:PutDeliveryDestinationPolicy',
819
+ 'logs:PutDestinationPolicy',
820
+ 'rds:ModifyDBClusterSnapshotAttribute',
821
+ 'rds:ModifyDBSnapshotAttribute',
822
+ 'states:DeleteResourcePolicy',
823
+ 'states:PutResourcePolicy',
824
+ 'states:RevealSecrets',
825
+ 'states:TestState',
826
+ ].sort(compareStrings));
827
+ const RESOURCE_POLICY_AND_TRUST_EXPANSION_ACTIONS = Object.freeze([
828
+ ...exports.STAGE_AUTHORITY_UNREVIEWED_PERMISSION_MANAGEMENT_ACTION_PATTERNS,
829
+ ...ADDITIONAL_RESOURCE_POLICY_AND_TRUST_EXPANSION_ACTIONS,
830
+ ].sort(compareStrings));
831
+ /** Stable exclusions from the temporary infrastructure-administrator role.
832
+ * These are account administration, direct application data/user mutation,
833
+ * message handling, or export paths rather than native stack reconciliation.
834
+ * The generated deployed smoke retains only its four exact Cognito admin calls. */
835
+ exports.STAGE_AUTHORITY_NON_DEPLOYMENT_ACCOUNT_AND_DATA_ACTIONS = Object.freeze([
836
+ 'acm:*Acme*',
837
+ 'acm:ExportCertificate',
838
+ 'acm:PutAccountConfiguration',
839
+ 'appconfig:UpdateAccountSettings',
840
+ 'cognito-idp:AdminConfirmSignUp',
841
+ 'cognito-idp:AdminDeleteUserAttributes',
842
+ 'cognito-idp:AdminDisable*',
843
+ 'cognito-idp:AdminEnableUser',
844
+ 'cognito-idp:AdminForgetDevice',
845
+ 'cognito-idp:*Auth*',
846
+ 'cognito-idp:*Device*',
847
+ 'cognito-idp:*UserMFA*',
848
+ 'cognito-idp:AdminGetUser',
849
+ 'cognito-idp:AdminLinkProviderForUser',
850
+ 'cognito-idp:AdminList*',
851
+ 'cognito-idp:AdminRemoveUserFromGroup',
852
+ 'cognito-idp:AdminResetUserPassword',
853
+ 'cognito-idp:AdminSetUserSettings',
854
+ 'cognito-idp:AdminUpdateUserAttributes',
855
+ 'cognito-idp:AdminUserGlobalSignOut',
856
+ 'cognito-idp:ChangePassword',
857
+ 'cognito-idp:ConfirmForgotPassword',
858
+ 'cognito-idp:ConfirmSignUp',
859
+ 'cognito-idp:DeleteUser',
860
+ 'cognito-idp:DeleteUserAttributes',
861
+ 'cognito-idp:ForgotPassword',
862
+ 'cognito-idp:GetTokensFromRefreshToken',
863
+ 'cognito-idp:GetUser',
864
+ 'cognito-idp:GetUserAttributeVerificationCode',
865
+ 'cognito-idp:GlobalSignOut',
866
+ 'cognito-idp:ListUsers*',
867
+ 'cognito-idp:ResendConfirmationCode',
868
+ 'cognito-idp:RevokeToken',
869
+ 'cognito-idp:SetUser*',
870
+ 'cognito-idp:SignUp',
871
+ 'cognito-idp:UpdateUserAttributes',
872
+ 'cognito-idp:VerifySoftwareToken',
873
+ 'cognito-idp:VerifyUserAttribute',
874
+ 'dynamodb:*Item*',
875
+ 'dynamodb:ExportTableToPointInTime',
876
+ 'dynamodb:GetRecords',
877
+ 'dynamodb:GetShardIterator',
878
+ 'dynamodb:ImportTable',
879
+ 'dynamodb:PartiQL*',
880
+ 'dynamodb:PurchaseReservedCapacityOfferings',
881
+ 'dynamodb:Query',
882
+ 'dynamodb:ReadDataForReplication',
883
+ 'dynamodb:RestoreTableFrom*',
884
+ 'dynamodb:Scan',
885
+ 'dynamodb:WriteDataForReplication',
886
+ 'ecr:CreatePullThroughCacheRule',
887
+ 'ecr:CreateRepositoryCreationTemplate',
888
+ 'ecr:DeletePullThroughCacheRule',
889
+ 'ecr:DeleteRepositoryCreationTemplate',
890
+ 'ecr:DeleteSigningConfiguration',
891
+ 'ecr:PutAccountSetting',
892
+ 'ecr:PutRegistryScanningConfiguration',
893
+ 'ecr:PutSigningConfiguration',
894
+ 'ecr:UpdatePullThroughCacheRule',
895
+ 'ecr:UpdateRepositoryCreationTemplate',
896
+ 'ecs:DeleteAccountSetting',
897
+ 'ecs:ExecuteCommand',
898
+ 'ecs:PutAccountSetting',
899
+ 'ecs:PutAccountSettingDefault',
900
+ 'ecs:RunTask',
901
+ 'ecs:StartTask',
902
+ 'ecs:StopTask',
903
+ 'logs:CancelExportTask',
904
+ 'logs:CreateExportTask',
905
+ 'rds:CancelExportTask',
906
+ 'rds:ModifyCertificates',
907
+ 'rds:PurchaseReservedDBInstancesOffering',
908
+ 'rds:StartExportTask',
909
+ 'secretsmanager:CancelRotateSecret',
910
+ 'secretsmanager:RotateSecret',
911
+ 'secretsmanager:UpdateSecretVersionStage',
912
+ 'sns:*PhoneNumber*',
913
+ 'sns:*SMS*',
914
+ 'sqs:*Message*',
915
+ 'sqs:PurgeQueue',
916
+ 'ssm:CreateActivation',
917
+ 'ssm:ResumeSession',
918
+ 'ssm:SendCommand',
919
+ 'ssm:StartAutomationExecution',
920
+ 'ssm:StartChangeRequestExecution',
921
+ 'ssm:StartSession',
922
+ 'ssm:TerminateSession',
923
+ ].sort(compareStrings));
924
+ const SECRET_BATCH_VALUE_READ_ACTIONS = Object.freeze([
925
+ 'secretsmanager:BatchGetSecretValue',
926
+ ]);
927
+ const GENERATED_FOUNDATION_DATABASE_VALUE_ACTIONS = Object.freeze(['secretsmanager:GetSecretValue', 'secretsmanager:PutSecretValue'].sort(compareStrings));
928
+ const STAGE_SECRET_CONTAINER_METADATA_UPDATE_ACTIONS = Object.freeze([
929
+ 'secretsmanager:UpdateSecret',
930
+ ]);
931
+ const GENERATED_FOUNDATION_DATABASE_RECONCILIATION_ACTIONS = Object.freeze([
932
+ ...GENERATED_FOUNDATION_DATABASE_VALUE_ACTIONS,
933
+ ...STAGE_SECRET_CONTAINER_METADATA_UPDATE_ACTIONS,
934
+ ].sort(compareStrings));
935
+ const SSM_HISTORY_AND_RECURSIVE_VALUE_READ_ACTIONS = Object.freeze(['ssm:GetParameterHistory', 'ssm:GetParametersByPath'].sort(compareStrings));
936
+ const WORKFLOW_EXECUTION_MUTATION_ACTIONS = Object.freeze([
937
+ 'states:RedriveExecution',
938
+ 'states:StartExecution',
939
+ 'states:StartSyncExecution',
940
+ 'states:StopExecution',
941
+ ].sort(compareStrings));
942
+ exports.STAGE_AUTHORITY_UNSUPPORTED_WORKFLOW_EXECUTION_ACTIONS = Object.freeze([
943
+ 'states:RedriveExecution',
944
+ 'states:StartSyncExecution',
945
+ 'states:StopExecution',
946
+ ].sort(compareStrings));
947
+ const LAMBDA_INTEGRATION_PRINCIPALS = Object.freeze([
948
+ 'apigateway.amazonaws.com',
949
+ 'cloudfront.amazonaws.com',
950
+ 'events.amazonaws.com',
951
+ ].sort(compareStrings));
952
+ const REVIEWED_LAMBDA_PERMISSION_PRINCIPALS = Object.freeze([...LAMBDA_INTEGRATION_PRINCIPALS, '*'].sort(compareStrings));
953
+ exports.STAGE_AUTHORITY_SERVICE_LINKED_ROLES = Object.freeze([
954
+ Object.freeze({
955
+ path: 'ecs.amazonaws.com/AWSServiceRoleForECS',
956
+ serviceName: 'ecs.amazonaws.com',
957
+ }),
958
+ Object.freeze({
959
+ path: 'ecs.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_ECSService',
960
+ serviceName: 'ecs.application-autoscaling.amazonaws.com',
961
+ }),
962
+ Object.freeze({
963
+ path: 'rds.amazonaws.com/AWSServiceRoleForRDS',
964
+ serviceName: 'rds.amazonaws.com',
965
+ }),
966
+ Object.freeze({
967
+ path: 'rds.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_RDSCluster',
968
+ serviceName: 'rds.application-autoscaling.amazonaws.com',
969
+ }),
970
+ Object.freeze({
971
+ path: 'wafv2.amazonaws.com/AWSServiceRoleForWAFV2Logging',
972
+ serviceName: 'wafv2.amazonaws.com',
973
+ }),
974
+ ]);
975
+ const IAM_POLICY_VERSION = '2012-10-17';
976
+ function compareStrings(left, right) {
977
+ return left < right ? -1 : left > right ? 1 : 0;
978
+ }
979
+ function githubOidcAudience(partition) {
980
+ return partition === 'aws-cn' ? 'sts.amazonaws.com.cn' : 'sts.amazonaws.com';
981
+ }
982
+ function canonicalize(value) {
983
+ if (Array.isArray(value))
984
+ return value.map(canonicalize);
985
+ if (!value || typeof value !== 'object')
986
+ return value;
987
+ return Object.fromEntries(Object.entries(value)
988
+ .sort(([left], [right]) => compareStrings(left, right))
989
+ .map(([key, entry]) => [key, canonicalize(entry)]));
990
+ }
991
+ function deepFreeze(value) {
992
+ if (!value || typeof value !== 'object' || Object.isFrozen(value)) {
993
+ return value;
994
+ }
995
+ for (const entry of Object.values(value))
996
+ deepFreeze(entry);
997
+ return Object.freeze(value);
998
+ }
999
+ function policy(statements) {
1000
+ return {
1001
+ Statement: [...statements].sort((left, right) => compareStrings(left.Sid, right.Sid)),
1002
+ Version: IAM_POLICY_VERSION,
1003
+ };
1004
+ }
1005
+ function omitPolicyStatementSids(document) {
1006
+ const record = document;
1007
+ const statements = record['Statement'];
1008
+ if (!Array.isArray(statements)) {
1009
+ throw new Error('Stage authority template policy statements are invalid.');
1010
+ }
1011
+ return {
1012
+ ...record,
1013
+ Statement: statements.map((entry) => {
1014
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
1015
+ throw new Error('Stage authority template policy statement is invalid.');
1016
+ }
1017
+ return Object.fromEntries(Object.entries(entry).filter(([key]) => key !== 'Sid'));
1018
+ }),
1019
+ };
1020
+ }
1021
+ function replaceExactTemplateString(value, expected, replacement) {
1022
+ if (value === expected)
1023
+ return replacement;
1024
+ if (Array.isArray(value)) {
1025
+ return value.map((entry) => replaceExactTemplateString(entry, expected, replacement));
1026
+ }
1027
+ if (!value || typeof value !== 'object')
1028
+ return value;
1029
+ return Object.fromEntries(Object.entries(value).map(([key, entry]) => [
1030
+ key,
1031
+ replaceExactTemplateString(entry, expected, replacement),
1032
+ ]));
1033
+ }
1034
+ function compactExactTemplatePolicyStrings(documents, values) {
1035
+ const constants = {};
1036
+ const replacements = new Map();
1037
+ for (const value of [...new Set(values)].sort(compareStrings)) {
1038
+ const key = `A${replacements.size}`;
1039
+ const replacement = Object.freeze({
1040
+ 'Fn::FindInMap': Object.freeze(['A', 'V', key]),
1041
+ });
1042
+ constants[key] = value;
1043
+ replacements.set(value, replacement);
1044
+ }
1045
+ if (replacements.size === 0)
1046
+ return Object.freeze({ documents });
1047
+ const compactedDocuments = Object.fromEntries(Object.entries(documents).map(([logicalId, document]) => [
1048
+ logicalId,
1049
+ [...replacements.entries()].reduce((current, [arn, replacement]) => replaceExactTemplateString(current, arn, replacement), document),
1050
+ ]));
1051
+ return Object.freeze({
1052
+ documents: Object.freeze(compactedDocuments),
1053
+ // These short, template-private identifiers avoid repeating long exact
1054
+ // ARNs in policy documents. Fn::FindInMap resolves to the original literal
1055
+ // before IAM sees the policy; the executable policy model stays literal.
1056
+ mappings: Object.freeze({
1057
+ A: Object.freeze({ V: Object.freeze(constants) }),
1058
+ }),
1059
+ });
1060
+ }
1061
+ function statement(input) {
1062
+ if ((input.Resource === undefined) === (input.NotResource === undefined)) {
1063
+ throw new Error('Stage authority statements require exactly one Resource or NotResource scope.');
1064
+ }
1065
+ return {
1066
+ ...input,
1067
+ Action: [...input.Action].sort(compareStrings),
1068
+ ...(input.Resource
1069
+ ? { Resource: [...input.Resource].sort(compareStrings) }
1070
+ : {}),
1071
+ ...(input.NotResource
1072
+ ? { NotResource: [...input.NotResource].sort(compareStrings) }
1073
+ : {}),
1074
+ };
1075
+ }
1076
+ function requireIdentity(input) {
1077
+ const partition = input.partition ?? 'aws';
1078
+ if (!ACCOUNT_ID.test(input.accountId) ||
1079
+ !REGION.test(input.region) ||
1080
+ !exports.STAGE_AUTHORITY_STAGES.includes(input.stage) ||
1081
+ !exports.STAGE_AUTHORITY_PARTITIONS.includes(partition)) {
1082
+ throw new Error('Stage authority requires one exact account, Region, partition, and canonical stage.');
1083
+ }
1084
+ return {
1085
+ accountId: input.accountId,
1086
+ partition,
1087
+ region: input.region,
1088
+ stage: input.stage,
1089
+ };
1090
+ }
1091
+ function createStageAuthorityIdentity(input) {
1092
+ const identity = requireIdentity(input);
1093
+ const boundaryPath = `/ebk/${identity.stage}/`;
1094
+ const boundaryName = `workload-role-boundary-${identity.stage}`;
1095
+ const deploymentRolePath = `/ebk/${identity.stage}/deployment/`;
1096
+ const deploymentRoleName = `ebk-${identity.stage}-deploy`;
1097
+ const deploymentPolicyNames = Object.freeze([
1098
+ `stage-deployment-read-${identity.stage}`,
1099
+ `stage-deployment-regional-mutation-${identity.stage}`,
1100
+ `stage-deployment-global-iam-mutation-${identity.stage}`,
1101
+ `stage-deployment-global-resource-mutation-${identity.stage}`,
1102
+ `stage-deployment-generated-resource-mutation-${identity.stage}`,
1103
+ `stage-deployment-stage-containment-${identity.stage}`,
1104
+ `stage-deployment-permission-guardrails-${identity.stage}`,
1105
+ `stage-deployment-guardrails-${identity.stage}`,
1106
+ ]);
1107
+ const workloadRolePath = `/ebk/${identity.stage}/workloads/`;
1108
+ const workloadInstanceProfilePath = workloadRolePath;
1109
+ return Object.freeze({
1110
+ boundaryArn: `arn:${identity.partition}:iam::${identity.accountId}:policy${boundaryPath}${boundaryName}`,
1111
+ boundaryName,
1112
+ boundaryPath,
1113
+ deploymentPolicyArns: Object.freeze(deploymentPolicyNames.map((name) => `arn:${identity.partition}:iam::${identity.accountId}:policy${deploymentRolePath}${name}`)),
1114
+ deploymentPolicyNames,
1115
+ deploymentRoleArn: `arn:${identity.partition}:iam::${identity.accountId}:role${deploymentRolePath}${deploymentRoleName}`,
1116
+ deploymentRoleName,
1117
+ deploymentRolePath,
1118
+ stackName: `ebk-${identity.stage}-stage-authority`,
1119
+ workloadInstanceProfileArnPattern: `arn:${identity.partition}:iam::${identity.accountId}:instance-profile${workloadInstanceProfilePath}*`,
1120
+ workloadInstanceProfilePath,
1121
+ workloadRoleArnPattern: `arn:${identity.partition}:iam::${identity.accountId}:role${workloadRolePath}*`,
1122
+ workloadRolePath,
1123
+ });
1124
+ }
1125
+ function requireTrust(input, identity) {
1126
+ const principals = [...new Set(input.awsPrincipalArns ?? [])].sort(compareStrings);
1127
+ if (principals.length > STAGE_AUTHORITY_MAX_AWS_PRINCIPALS ||
1128
+ principals.some((arn) => {
1129
+ const match = AWS_PRINCIPAL_ARN.exec(arn);
1130
+ return (Buffer.byteLength(arn, 'utf8') >
1131
+ STAGE_AUTHORITY_MAX_AWS_PRINCIPAL_ARN_BYTES ||
1132
+ !match ||
1133
+ match[1] !== identity.partition);
1134
+ })) {
1135
+ throw new Error('Stage authority AWS trust must contain at most 16 bounded unique exact principal ARNs in the selected partition.');
1136
+ }
1137
+ const statements = [];
1138
+ if (principals.length > 0) {
1139
+ statements.push({
1140
+ Action: 'sts:AssumeRole',
1141
+ Effect: 'Allow',
1142
+ Principal: { AWS: principals },
1143
+ Sid: 'AssumeFromReviewedAwsPrincipals',
1144
+ });
1145
+ }
1146
+ if (input.github) {
1147
+ const match = GITHUB_OIDC_PROVIDER_ARN.exec(input.github.oidcProviderArn);
1148
+ const subjectMatch = GITHUB_SUBJECT.exec(input.github.subject);
1149
+ if (!match ||
1150
+ match[1] !== identity.partition ||
1151
+ match[2] !== identity.accountId ||
1152
+ !subjectMatch) {
1153
+ throw new Error('Stage authority GitHub trust must use the target account provider and one immutable environment subject.');
1154
+ }
1155
+ if (subjectMatch[1] !== identity.stage) {
1156
+ throw new Error('Stage authority GitHub trust environment must match the selected stage.');
1157
+ }
1158
+ statements.push({
1159
+ Action: 'sts:AssumeRoleWithWebIdentity',
1160
+ Condition: {
1161
+ StringEquals: {
1162
+ 'token.actions.githubusercontent.com:aud': githubOidcAudience(identity.partition),
1163
+ 'token.actions.githubusercontent.com:sub': input.github.subject,
1164
+ },
1165
+ },
1166
+ Effect: 'Allow',
1167
+ Principal: { Federated: input.github.oidcProviderArn },
1168
+ Sid: 'AssumeFromReviewedGithubEnvironment',
1169
+ });
1170
+ }
1171
+ if (statements.length === 0) {
1172
+ throw new Error('Stage authority requires at least one reviewed AWS or GitHub trust principal.');
1173
+ }
1174
+ return {
1175
+ Statement: statements.sort((left, right) => compareStrings(String(left['Sid']), String(right['Sid']))),
1176
+ Version: IAM_POLICY_VERSION,
1177
+ };
1178
+ }
1179
+ function runtimeBoundaryPolicy(options) {
1180
+ return policy([
1181
+ statement({
1182
+ Action: exports.STAGE_RUNTIME_REGIONAL_ACTIONS,
1183
+ Condition: {
1184
+ StringEquals: { 'aws:RequestedRegion': options.region },
1185
+ },
1186
+ Effect: 'Allow',
1187
+ Resource: ['*'],
1188
+ Sid: 'AllowFixedRuntimeServicesInStageRegion',
1189
+ }),
1190
+ statement({
1191
+ Action: ['sts:GetCallerIdentity'],
1192
+ Effect: 'Allow',
1193
+ Resource: ['*'],
1194
+ Sid: 'AllowRuntimeCallerIdentity',
1195
+ }),
1196
+ statement({
1197
+ Action: exports.STAGE_RUNTIME_GLOBAL_ACTIONS,
1198
+ Effect: 'Allow',
1199
+ Resource: ['*'],
1200
+ Sid: 'AllowFixedRuntimeGlobalServices',
1201
+ }),
1202
+ statement({
1203
+ Action: ['iam:PassRole'],
1204
+ Condition: {
1205
+ StringEquals: {
1206
+ 'iam:PassedToService': [
1207
+ 'appconfig.amazonaws.com',
1208
+ 'ec2.amazonaws.com',
1209
+ 'ecs-tasks.amazonaws.com',
1210
+ 'lambda.amazonaws.com',
1211
+ 'states.amazonaws.com',
1212
+ ].sort(compareStrings),
1213
+ },
1214
+ },
1215
+ Effect: 'Allow',
1216
+ Resource: [options.identity.workloadRoleArnPattern],
1217
+ Sid: 'AllowRuntimePassBoundedStageRoles',
1218
+ }),
1219
+ statement({
1220
+ Action: exports.STAGE_RUNTIME_REGIONAL_SERVICE_FAMILIES,
1221
+ Condition: {
1222
+ StringNotEquals: { 'aws:RequestedRegion': options.region },
1223
+ },
1224
+ Effect: 'Deny',
1225
+ Resource: ['*'],
1226
+ Sid: 'DenyRuntimeOutsideStageRegion',
1227
+ }),
1228
+ statement({
1229
+ Action: ['*'],
1230
+ Condition: {
1231
+ Null: { 'aws:ResourceAccount': 'false' },
1232
+ StringNotEquals: { 'aws:ResourceAccount': options.accountId },
1233
+ },
1234
+ Effect: 'Deny',
1235
+ Resource: ['*'],
1236
+ Sid: 'DenyRuntimeCrossAccountResources',
1237
+ }),
1238
+ statement({
1239
+ Action: ['*'],
1240
+ Condition: {
1241
+ Null: { 'aws:ResourceTag/ebk:stage': 'false' },
1242
+ StringNotEquals: { 'aws:ResourceTag/ebk:stage': options.stage },
1243
+ },
1244
+ Effect: 'Deny',
1245
+ Resource: ['*'],
1246
+ Sid: 'DenyRuntimeKnownOtherStageResources',
1247
+ }),
1248
+ statement({
1249
+ Action: RUNTIME_PRIVILEGE_ACTIONS,
1250
+ Effect: 'Deny',
1251
+ Resource: ['*'],
1252
+ Sid: 'DenyRuntimePrivilegeConstruction',
1253
+ }),
1254
+ ]);
1255
+ }
1256
+ const SST_4_17_1_PHYSICAL_NAME_RANDOM_SUFFIX_LENGTH = 9;
1257
+ const FOUNDATION_ROUTER_CLOUDFRONT_FUNCTION_NAMES = [
1258
+ 'FoundationRouterCloudfrontFunctionRequest',
1259
+ 'FoundationRouterCloudfrontFunctionResponse',
1260
+ ];
1261
+ function sst4171PhysicalNamePattern(input) {
1262
+ const maximumPrefixLength = input.maximumLength - SST_4_17_1_PHYSICAL_NAME_RANDOM_SUFFIX_LENGTH;
1263
+ const stageLength = input.stage.length;
1264
+ const nameLength = input.logicalName.length;
1265
+ const prefix = nameLength + 1 >= maximumPrefixLength
1266
+ ? input.logicalName.substring(0, maximumPrefixLength)
1267
+ : nameLength + stageLength + 2 >= maximumPrefixLength
1268
+ ? `${input.stage.substring(0, maximumPrefixLength - nameLength - 1)}-${input.logicalName}`
1269
+ : `${input.application.substring(0, maximumPrefixLength - stageLength - nameLength - 2)}-${input.stage}-${input.logicalName}`;
1270
+ return `${prefix}-*`;
1271
+ }
1272
+ function generatedRouterCloudFrontFunctionResources(options, stage) {
1273
+ return FOUNDATION_ROUTER_CLOUDFRONT_FUNCTION_NAMES.map((logicalName) => `arn:${options.partition}:cloudfront::${options.accountId}:function/${sst4171PhysicalNamePattern({
1274
+ application: 'foundation',
1275
+ logicalName,
1276
+ maximumLength: 64,
1277
+ stage,
1278
+ })}`).sort(compareStrings);
1279
+ }
1280
+ function deploymentPolicy(options) {
1281
+ const edgeRegion = edgeControlRegion(options.partition, options.region);
1282
+ const managedPolicies = exports.STAGE_AUTHORITY_APPROVED_AWS_MANAGED_POLICIES.map((name) => `arn:${options.partition}:iam::aws:policy/${name}`);
1283
+ const productionDatabaseResources = [
1284
+ `arn:${options.partition}:rds:${options.region}:${options.accountId}:cluster:*`,
1285
+ `arn:${options.partition}:rds:${options.region}:${options.accountId}:db:*`,
1286
+ ];
1287
+ const productionRetentionResources = [
1288
+ `arn:${options.partition}:backup:${options.region}:${options.accountId}:backup-plan:*`,
1289
+ `arn:${options.partition}:backup:${options.region}:${options.accountId}:backup-vault:*`,
1290
+ `arn:${options.partition}:backup:${options.region}:${options.accountId}:recovery-point:*`,
1291
+ ];
1292
+ const productionDataExportBucketResources = [
1293
+ `arn:${options.partition}:s3:::ebk-${options.accountId}-production-*-data-export`,
1294
+ `arn:${options.partition}:s3:::ebk-${options.accountId}-production-*-data-export/*`,
1295
+ ];
1296
+ const productionDataExportAliasResources = [
1297
+ `arn:${options.partition}:kms:${options.region}:${options.accountId}:alias/ebk-production-*-data-export`,
1298
+ ];
1299
+ const stageLambdaResources = [
1300
+ `arn:${options.partition}:lambda:${options.region}:${options.accountId}:function:*`,
1301
+ ];
1302
+ const foundationNatWorkloadRoleResources = [
1303
+ `arn:${options.partition}:iam::${options.accountId}:role${options.identity.workloadRolePath}ebk-${options.stage}-foundation-nat`,
1304
+ ];
1305
+ const stageCloudFrontDistributionResources = [
1306
+ `arn:${options.partition}:cloudfront::${options.accountId}:distribution/*`,
1307
+ ];
1308
+ const stageRouterWebAclResources = [
1309
+ `arn:${options.partition}:wafv2:${edgeRegion}:${options.accountId}:global/webacl/*-${options.stage}-*/*`,
1310
+ ];
1311
+ const stageRouterCloudFrontFunctionResources = generatedRouterCloudFrontFunctionResources(options, options.stage);
1312
+ const productionRouterCloudFrontFunctionResources = generatedRouterCloudFrontFunctionResources(options, 'production');
1313
+ const stageRouterWafLogDestinationResources = [
1314
+ `arn:${options.partition}:logs:${edgeRegion}:${options.accountId}:log-group:aws-waf-logs-foundation-${options.stage}-FoundationRouter-*`,
1315
+ ];
1316
+ const generatedEdgeLogGroupMutationResources = [
1317
+ ...stageRouterWafLogDestinationResources,
1318
+ ...(options.region === edgeRegion
1319
+ ? [
1320
+ `arn:${options.partition}:logs:${options.region}:${options.accountId}:log-group:/aws/vendedlogs/apis/*-${options.stage}-*`,
1321
+ `arn:${options.partition}:logs:${options.region}:${options.accountId}:log-group:/ebk/${options.stage}/*`,
1322
+ ]
1323
+ : []),
1324
+ ].sort(compareStrings);
1325
+ const productionEventSubscriptionResources = [
1326
+ `arn:${options.partition}:rds:${options.region}:${options.accountId}:es:*`,
1327
+ ];
1328
+ const stageAccountBudgetResources = [
1329
+ `arn:${options.partition}:budgets::${options.accountId}:budget/*`,
1330
+ ];
1331
+ const stageTopicResources = [
1332
+ `arn:${options.partition}:sns:${options.region}:${options.accountId}:*-${options.stage}-*`,
1333
+ ];
1334
+ const generatedFoundationAlertTopicResources = [
1335
+ `arn:${options.partition}:sns:${options.region}:${options.accountId}:foundation-${options.stage}-FoundationAlertsTopic-*`,
1336
+ ];
1337
+ const stageQueueResources = [
1338
+ `arn:${options.partition}:sqs:${options.region}:${options.accountId}:*-${options.stage}-*`,
1339
+ ];
1340
+ const stageReadableSsmValueResources = [
1341
+ `arn:${options.partition}:ssm:${options.region}:${options.accountId}:parameter/ebk/${options.stage}/workspace-owner`,
1342
+ `arn:${options.partition}:ssm:${options.region}:${options.accountId}:parameter/ebk/${options.stage}/foundation/*`,
1343
+ `arn:${options.partition}:ssm:${options.region}:${options.accountId}:parameter/sst/bootstrap`,
1344
+ `arn:${options.partition}:ssm:${options.region}:${options.accountId}:parameter/sst/passphrase/*/${options.stage}`,
1345
+ `arn:${options.partition}:ssm:${options.region}:${options.accountId}:parameter/sst/vpc/vpc-*/private-key-value`,
1346
+ ].sort(compareStrings);
1347
+ const immutableWorkspaceOwnerResources = [
1348
+ 'development',
1349
+ 'production',
1350
+ 'staging',
1351
+ ]
1352
+ .map((stage) => `arn:${options.partition}:ssm:${options.region}:${options.accountId}:parameter/ebk/${stage}/workspace-owner`)
1353
+ .sort(compareStrings);
1354
+ // SST 4.17.1 models the generated Aurora credential as a managed
1355
+ // SecretVersion. The pinned AWS provider needs PutSecretValue to create it
1356
+ // and GetSecretValue to reconcile its AWSCURRENT version during removal.
1357
+ // Keep those value operations bound to the one Foundation-owned
1358
+ // physical-name and stage-tag contract; operator/generated paths and every
1359
+ // other secret value remain explicitly outside this exception.
1360
+ const generatedFoundationDatabaseCredentialResources = [
1361
+ `arn:${options.partition}:secretsmanager:${options.region}:${options.accountId}:secret:foundation-${options.stage}-FoundationDatabaseProxySecret-*`,
1362
+ ];
1363
+ // Native providers reconcile an EBK-owned Secret container through
1364
+ // UpdateSecret regardless of whether it holds a generated database
1365
+ // credential, a machine credential, or operator-supplied material. Bind that
1366
+ // metadata lifecycle to the stable stage-owned namespace instead of
1367
+ // enumerating today's generated container purposes. UpdateSecret can itself
1368
+ // carry plaintext, so reviewed generated source, temporary deployer
1369
+ // credentials, and CloudTrail remain the honest controls. GetSecretValue and
1370
+ // PutSecretValue stay excluded from every Slice container.
1371
+ const stageOwnedSecretContainerResources = [
1372
+ `arn:${options.partition}:secretsmanager:${options.region}:${options.accountId}:secret:ebk/${options.stage}/*`,
1373
+ ];
1374
+ const stageSecretContainerMetadataUpdateResources = [
1375
+ ...generatedFoundationDatabaseCredentialResources,
1376
+ ...stageOwnedSecretContainerResources,
1377
+ ].sort(compareStrings);
1378
+ const machineAuthorizationStackResources = [
1379
+ `arn:${options.partition}:cloudformation:${options.region}:${options.accountId}:stack/*MachineAuthorization*/*`,
1380
+ ];
1381
+ const costAnomalyMonitorResources = [
1382
+ `arn:${options.partition}:ce::${options.accountId}:anomalymonitor/*`,
1383
+ ];
1384
+ const costAnomalySubscriptionResources = [
1385
+ `arn:${options.partition}:ce::${options.accountId}:anomalysubscription/*`,
1386
+ ];
1387
+ const costAnomalyResources = [
1388
+ ...costAnomalyMonitorResources,
1389
+ ...costAnomalySubscriptionResources,
1390
+ ].sort(compareStrings);
1391
+ const productionCostScope = '*/account/production';
1392
+ const eventArchiveResources = [
1393
+ `arn:${options.partition}:events:${options.region}:${options.accountId}:archive/ebk-${options.stage}-foundation-events`,
1394
+ ];
1395
+ const foundationEventBusResources = [
1396
+ `arn:${options.partition}:events:${options.region}:${options.accountId}:event-bus/foundation-${options.stage}-FoundationEventBusBus-*`,
1397
+ ];
1398
+ const stageKmsKeyResources = [
1399
+ `arn:${options.partition}:kms:${options.region}:${options.accountId}:key/*`,
1400
+ ];
1401
+ const stageKmsAliasResources = [
1402
+ `arn:${options.partition}:kms:${options.region}:${options.accountId}:alias/ebk-${options.stage}-*-data-export`,
1403
+ ];
1404
+ const stageStateMachineResources = [
1405
+ `arn:${options.partition}:states:${options.region}:${options.accountId}:stateMachine:*-${options.stage}-*`,
1406
+ ];
1407
+ // Native SST physical state-machine names are derived from Pulumi logical
1408
+ // names and do not necessarily contain the stage. Step Functions exposes the
1409
+ // state machine's resource tags while authorizing both the state machine and
1410
+ // its Standard executions, so the governed execution path uses the exact
1411
+ // account/Region namespace plus the stage tag instead of a name convention.
1412
+ const accountStateMachineResources = [
1413
+ `arn:${options.partition}:states:${options.region}:${options.accountId}:stateMachine:*`,
1414
+ ];
1415
+ const accountExecutionResources = [
1416
+ `arn:${options.partition}:states:${options.region}:${options.accountId}:execution:*:*`,
1417
+ ];
1418
+ const serviceLinkedRoleResources = exports.STAGE_AUTHORITY_SERVICE_LINKED_ROLES.map(({ path }) => `arn:${options.partition}:iam::${options.accountId}:role/aws-service-role/${path}`);
1419
+ const stageAndSharedMutationResources = [
1420
+ `arn:${options.partition}:backup:${options.region}:${options.accountId}:*:*${options.stage}*`,
1421
+ `arn:${options.partition}:budgets::${options.accountId}:budget/*`,
1422
+ ...stageRouterCloudFrontFunctionResources,
1423
+ `arn:${options.partition}:dynamodb:${options.region}:${options.accountId}:table/*-${options.stage}-*`,
1424
+ `arn:${options.partition}:ecr:${options.region}:${options.accountId}:repository/ebk/${options.stage}/*`,
1425
+ `arn:${options.partition}:events:${options.region}:${options.accountId}:archive/ebk-${options.stage}-*`,
1426
+ `arn:${options.partition}:events:${options.region}:${options.accountId}:event-bus/*-${options.stage}-*`,
1427
+ `arn:${options.partition}:events:${options.region}:${options.accountId}:rule/*-${options.stage}-*`,
1428
+ `arn:${options.partition}:iam::${options.accountId}:instance-profile/ebk/${options.stage}/workloads/*`,
1429
+ `arn:${options.partition}:iam::${options.accountId}:role/ebk/${options.stage}/workloads/*`,
1430
+ ...stageKmsAliasResources,
1431
+ `arn:${options.partition}:lambda:${options.region}:${options.accountId}:function:*-${options.stage}-*`,
1432
+ `arn:${options.partition}:rds:${options.region}:${options.accountId}:cluster:*-${options.stage}-*`,
1433
+ `arn:${options.partition}:rds:${options.region}:${options.accountId}:db:*-${options.stage}-*`,
1434
+ `arn:${options.partition}:rds:${options.region}:${options.accountId}:snapshot:*-${options.stage}-*`,
1435
+ `arn:${options.partition}:s3:::*-${options.stage}-*`,
1436
+ `arn:${options.partition}:s3:::*-${options.stage}-*/*`,
1437
+ `arn:${options.partition}:s3:::sst-asset-*`,
1438
+ `arn:${options.partition}:s3:::sst-asset-*/*`,
1439
+ `arn:${options.partition}:s3:::sst-state-*`,
1440
+ `arn:${options.partition}:s3:::sst-state-*/*`,
1441
+ `arn:${options.partition}:secretsmanager:${options.region}:${options.accountId}:secret:ebk/${options.stage}/*`,
1442
+ `arn:${options.partition}:sns:${options.region}:${options.accountId}:*-${options.stage}-*`,
1443
+ `arn:${options.partition}:sqs:${options.region}:${options.accountId}:*-${options.stage}-*`,
1444
+ `arn:${options.partition}:ssm:${options.region}:${options.accountId}:parameter/*${options.stage}*`,
1445
+ ...stageStateMachineResources,
1446
+ `arn:${options.partition}:wafv2:${edgeRegion}:${options.accountId}:global/webacl/*-${options.stage}-*/*`,
1447
+ ].sort(compareStrings);
1448
+ const namedProductionResources = [
1449
+ `arn:${options.partition}:backup:${options.region}:${options.accountId}:backup-vault:*production*`,
1450
+ `arn:${options.partition}:cloudwatch:${options.region}:${options.accountId}:alarm:*production*`,
1451
+ `arn:${options.partition}:cloudwatch::${options.accountId}:dashboard/*production*`,
1452
+ ...productionRouterCloudFrontFunctionResources,
1453
+ `arn:${options.partition}:dynamodb:${options.region}:${options.accountId}:table/*-production-*`,
1454
+ `arn:${options.partition}:ecr:${options.region}:${options.accountId}:repository/ebk/production/*`,
1455
+ `arn:${options.partition}:ecs:${options.region}:${options.accountId}:cluster/*production*`,
1456
+ `arn:${options.partition}:ecs:${options.region}:${options.accountId}:service/*production*/*`,
1457
+ `arn:${options.partition}:elasticloadbalancing:${options.region}:${options.accountId}:loadbalancer/*/*production*/*`,
1458
+ `arn:${options.partition}:elasticloadbalancing:${options.region}:${options.accountId}:targetgroup/*production*/*`,
1459
+ `arn:${options.partition}:events:${options.region}:${options.accountId}:event-bus/*-production-*`,
1460
+ `arn:${options.partition}:events:${options.region}:${options.accountId}:rule/*-production-*`,
1461
+ `arn:${options.partition}:iam::${options.accountId}:instance-profile/ebk/production/*`,
1462
+ `arn:${options.partition}:iam::${options.accountId}:policy/ebk/production/*`,
1463
+ `arn:${options.partition}:iam::${options.accountId}:role/ebk/production/*`,
1464
+ `arn:${options.partition}:kms:${options.region}:${options.accountId}:alias/ebk-production-*-data-export`,
1465
+ `arn:${options.partition}:lambda:${options.region}:${options.accountId}:function:*-production-*`,
1466
+ `arn:${options.partition}:logs:${options.region}:${options.accountId}:log-group:*production*`,
1467
+ `arn:${options.partition}:rds:${options.region}:${options.accountId}:cluster:*-production-*`,
1468
+ `arn:${options.partition}:rds:${options.region}:${options.accountId}:db:*-production-*`,
1469
+ `arn:${options.partition}:rds:${options.region}:${options.accountId}:snapshot:*-production-*`,
1470
+ `arn:${options.partition}:s3:::*-production-*`,
1471
+ `arn:${options.partition}:s3:::*-production-*/*`,
1472
+ `arn:${options.partition}:secretsmanager:${options.region}:${options.accountId}:secret:ebk/production/*`,
1473
+ `arn:${options.partition}:sns:${options.region}:${options.accountId}:*-production-*`,
1474
+ `arn:${options.partition}:sqs:${options.region}:${options.accountId}:*-production-*`,
1475
+ `arn:${options.partition}:ssm:${options.region}:${options.accountId}:parameter/*production*`,
1476
+ `arn:${options.partition}:states:${options.region}:${options.accountId}:stateMachine:*-production-*`,
1477
+ `arn:${options.partition}:wafv2:${edgeRegion}:${options.accountId}:global/webacl/*-production-*/*`,
1478
+ ].sort(compareStrings);
1479
+ const stageAndSharedSstBucketResources = [
1480
+ `arn:${options.partition}:s3:::*-${options.stage}-*`,
1481
+ `arn:${options.partition}:s3:::sst-asset-*`,
1482
+ `arn:${options.partition}:s3:::sst-state-*`,
1483
+ ].sort(compareStrings);
1484
+ const runInstancesExternalReferenceResources = [
1485
+ `arn:${options.partition}:ec2:${options.region}::image/ami-*`,
1486
+ `arn:${options.partition}:ec2:${options.region}::snapshot/snap-*`,
1487
+ ].sort(compareStrings);
1488
+ const runInstancesApprovedReferenceOwners = [
1489
+ options.accountId,
1490
+ ...(options.stage === 'production'
1491
+ ? []
1492
+ : exports.STAGE_AUTHORITY_SST_417_EXTERNAL_AMI_OWNERS_BY_PARTITION[options.partition]),
1493
+ ].sort(compareStrings);
1494
+ const statements = [
1495
+ statement({
1496
+ Action: exports.STAGE_AUTHORITY_REGIONAL_READ_ACTIONS,
1497
+ Condition: {
1498
+ StringEquals: { 'aws:RequestedRegion': options.region },
1499
+ },
1500
+ Effect: 'Allow',
1501
+ Resource: ['*'],
1502
+ Sid: 'ReadFixedRegionalServiceFamilies',
1503
+ }),
1504
+ statement({
1505
+ Action: ['acm:Describe*', 'acm:Get*', 'acm:List*'],
1506
+ Condition: {
1507
+ StringEquals: { 'aws:RequestedRegion': edgeRegion },
1508
+ },
1509
+ Effect: 'Allow',
1510
+ Resource: ['*'],
1511
+ Sid: 'ReadEdgeCertificateControlPlane',
1512
+ }),
1513
+ statement({
1514
+ Action: exports.STAGE_AUTHORITY_EDGE_OBSERVABILITY_READ_ACTIONS,
1515
+ Condition: {
1516
+ StringEquals: { 'aws:RequestedRegion': edgeRegion },
1517
+ },
1518
+ Effect: 'Allow',
1519
+ Resource: ['*'],
1520
+ Sid: 'ReadEdgeObservabilityControlPlane',
1521
+ }),
1522
+ statement({
1523
+ Action: exports.STAGE_AUTHORITY_EDGE_WAF_LOG_GROUP_ACTIONS,
1524
+ Condition: {
1525
+ StringEquals: { 'aws:RequestedRegion': edgeRegion },
1526
+ },
1527
+ Effect: 'Allow',
1528
+ Resource: stageRouterWafLogDestinationResources,
1529
+ Sid: 'ManageOnlyGeneratedEdgeWafLogGroup',
1530
+ }),
1531
+ // CloudWatch Logs models these native WAF delivery calls as account-level
1532
+ // operations. PutResourcePolicy exposes neither a resource ARN nor an
1533
+ // action-specific condition key; reviewed generated source is the control
1534
+ // for its uninspectable payload, and the request remains edge-Region-bound.
1535
+ statement({
1536
+ Action: exports.STAGE_AUTHORITY_EDGE_WAF_LOG_DELIVERY_MUTATION_ACTIONS,
1537
+ Condition: {
1538
+ StringEquals: { 'aws:RequestedRegion': edgeRegion },
1539
+ },
1540
+ Effect: 'Allow',
1541
+ Resource: ['*'],
1542
+ Sid: 'ManageEdgeWafLogDeliveryControlPlane',
1543
+ }),
1544
+ statement({
1545
+ Action: exports.STAGE_AUTHORITY_GLOBAL_READ_ACTIONS,
1546
+ Effect: 'Allow',
1547
+ Resource: ['*'],
1548
+ Sid: 'ReadFixedGlobalServiceFamilies',
1549
+ }),
1550
+ statement({
1551
+ Action: ['states:DescribeExecution'],
1552
+ Condition: {
1553
+ StringEquals: {
1554
+ 'aws:RequestedRegion': options.region,
1555
+ 'aws:ResourceTag/ebk:stage': options.stage,
1556
+ },
1557
+ },
1558
+ Effect: 'Allow',
1559
+ Resource: accountExecutionResources,
1560
+ Sid: 'DescribeOnlyTaggedStageWorkflowExecutions',
1561
+ }),
1562
+ statement({
1563
+ Action: ['states:DescribeExecution'],
1564
+ Condition: {
1565
+ StringNotEquals: {
1566
+ 'aws:ResourceTag/ebk:stage': options.stage,
1567
+ },
1568
+ },
1569
+ Effect: 'Deny',
1570
+ Resource: accountExecutionResources,
1571
+ Sid: 'DenyWorkflowExecutionReadWithoutStageTag',
1572
+ }),
1573
+ statement({
1574
+ Action: ['states:DescribeExecution'],
1575
+ Effect: 'Deny',
1576
+ NotResource: accountExecutionResources,
1577
+ Sid: 'DenyWorkflowExecutionReadOutsideAccount',
1578
+ }),
1579
+ statement({
1580
+ Action: exports.STAGE_AUTHORITY_REGIONAL_WRITE_ACTIONS,
1581
+ Condition: {
1582
+ StringEquals: { 'aws:RequestedRegion': options.region },
1583
+ StringEqualsIfExists: {
1584
+ 'aws:RequestTag/ebk:stage': options.stage,
1585
+ 'aws:ResourceAccount': options.accountId,
1586
+ 'aws:ResourceTag/ebk:stage': options.stage,
1587
+ },
1588
+ },
1589
+ Effect: 'Allow',
1590
+ Resource: ['*'],
1591
+ Sid: 'MutateFixedRegionalServiceFamilies',
1592
+ }),
1593
+ statement({
1594
+ Action: ['acm:*'],
1595
+ Condition: {
1596
+ StringEquals: { 'aws:RequestedRegion': edgeRegion },
1597
+ StringEqualsIfExists: {
1598
+ 'aws:RequestTag/ebk:stage': options.stage,
1599
+ 'aws:ResourceAccount': options.accountId,
1600
+ 'aws:ResourceTag/ebk:stage': options.stage,
1601
+ },
1602
+ },
1603
+ Effect: 'Allow',
1604
+ Resource: ['*'],
1605
+ Sid: 'MutateEdgeCertificates',
1606
+ }),
1607
+ statement({
1608
+ Action: exports.STAGE_AUTHORITY_S3_REGIONAL_MUTATION_ACTIONS,
1609
+ Condition: {
1610
+ StringEquals: { 'aws:RequestedRegion': options.region },
1611
+ },
1612
+ Effect: 'Allow',
1613
+ Resource: ['*'],
1614
+ Sid: 'MutateStageBucketsInStageRegion',
1615
+ }),
1616
+ statement({
1617
+ Action: ['s3:CreateBucket'],
1618
+ Condition: options.region === 'us-east-1'
1619
+ ? {
1620
+ Null: { 's3:LocationConstraint': 'true' },
1621
+ StringEquals: { 'aws:RequestedRegion': options.region },
1622
+ }
1623
+ : {
1624
+ StringEquals: {
1625
+ 'aws:RequestedRegion': options.region,
1626
+ 's3:LocationConstraint': options.region,
1627
+ },
1628
+ },
1629
+ Effect: 'Allow',
1630
+ Resource: ['*'],
1631
+ Sid: 'CreateBucketOnlyInStageRegion',
1632
+ }),
1633
+ // The generated architecture does not synthesize a stable Grant resource
1634
+ // whose grantee and operation set can be enumerated here. Follow AWS KMS's
1635
+ // integrated-service contract instead: a user cannot call CreateGrant
1636
+ // directly, and the grant remains bound to this account's stage key.
1637
+ statement({
1638
+ Action: ['kms:CreateGrant'],
1639
+ Condition: {
1640
+ Bool: { 'kms:GrantIsForAWSResource': 'true' },
1641
+ StringEquals: {
1642
+ 'aws:RequestedRegion': options.region,
1643
+ 'aws:ResourceTag/ebk:stage': options.stage,
1644
+ },
1645
+ StringEqualsIfExists: {
1646
+ 'aws:ResourceAccount': options.accountId,
1647
+ },
1648
+ },
1649
+ Effect: 'Allow',
1650
+ Resource: stageKmsKeyResources,
1651
+ Sid: 'CreateOnlyAwsResourceKmsGrants',
1652
+ }),
1653
+ statement({
1654
+ Action: ['ec2:RunInstances'],
1655
+ Condition: {
1656
+ StringEquals: { 'aws:RequestedRegion': options.region },
1657
+ },
1658
+ Effect: 'Allow',
1659
+ Resource: ['*'],
1660
+ Sid: 'LaunchStageInfrastructureFromApprovedReferences',
1661
+ }),
1662
+ statement({
1663
+ Action: SECRET_BATCH_VALUE_READ_ACTIONS,
1664
+ Effect: 'Deny',
1665
+ Resource: ['*'],
1666
+ Sid: 'DenySecretsManagerBatchValueReads',
1667
+ }),
1668
+ statement({
1669
+ Action: GENERATED_FOUNDATION_DATABASE_VALUE_ACTIONS,
1670
+ Effect: 'Deny',
1671
+ NotResource: generatedFoundationDatabaseCredentialResources,
1672
+ Sid: 'DenySecretValueAccessOutsideGeneratedFoundationDatabase',
1673
+ }),
1674
+ statement({
1675
+ Action: GENERATED_FOUNDATION_DATABASE_RECONCILIATION_ACTIONS,
1676
+ Condition: {
1677
+ StringNotEquals: {
1678
+ 'aws:ResourceTag/ebk:stage': options.stage,
1679
+ },
1680
+ },
1681
+ Effect: 'Deny',
1682
+ Resource: generatedFoundationDatabaseCredentialResources,
1683
+ Sid: 'DenyGeneratedDatabaseValueAccessWithWrongStageTag',
1684
+ }),
1685
+ statement({
1686
+ Action: STAGE_SECRET_CONTAINER_METADATA_UPDATE_ACTIONS,
1687
+ Effect: 'Deny',
1688
+ NotResource: stageSecretContainerMetadataUpdateResources,
1689
+ Sid: 'DenySecretMetadataUpdateOutsideStageContainers',
1690
+ }),
1691
+ statement({
1692
+ Action: ['ssm:GetParameter', 'ssm:GetParameters'],
1693
+ Effect: 'Deny',
1694
+ NotResource: stageReadableSsmValueResources,
1695
+ Sid: 'DenyUnapprovedSsmValueReads',
1696
+ }),
1697
+ statement({
1698
+ Action: SSM_HISTORY_AND_RECURSIVE_VALUE_READ_ACTIONS,
1699
+ Effect: 'Deny',
1700
+ Resource: ['*'],
1701
+ Sid: 'DenySsmHistoryAndRecursiveValueReads',
1702
+ }),
1703
+ statement({
1704
+ Action: [
1705
+ 'ssm:AddTagsToResource',
1706
+ 'ssm:DeleteParameter',
1707
+ 'ssm:DeleteParameters',
1708
+ 'ssm:LabelParameterVersion',
1709
+ 'ssm:PutParameter',
1710
+ 'ssm:RemoveTagsFromResource',
1711
+ 'ssm:UnlabelParameterVersion',
1712
+ ],
1713
+ Effect: 'Deny',
1714
+ Resource: immutableWorkspaceOwnerResources,
1715
+ Sid: 'DenyOwnerMutation',
1716
+ }),
1717
+ statement({
1718
+ Action: exports.STAGE_AUTHORITY_NON_DEPLOYMENT_ACCOUNT_AND_DATA_ACTIONS,
1719
+ Effect: 'Deny',
1720
+ Resource: ['*'],
1721
+ Sid: 'DenyNonDeploymentControlOperations',
1722
+ }),
1723
+ statement({
1724
+ Action: WORKFLOW_EXECUTION_MUTATION_ACTIONS,
1725
+ Effect: 'Deny',
1726
+ NotResource: [
1727
+ ...accountExecutionResources,
1728
+ ...accountStateMachineResources,
1729
+ ],
1730
+ Sid: 'DenyUnscopedWorkflowExecutionMutation',
1731
+ }),
1732
+ statement({
1733
+ Action: exports.STAGE_AUTHORITY_UNSUPPORTED_WORKFLOW_EXECUTION_ACTIONS,
1734
+ Effect: 'Deny',
1735
+ Resource: ['*'],
1736
+ Sid: 'DenyUnsupportedWorkflowExecutionMutation',
1737
+ }),
1738
+ statement({
1739
+ Action: ['states:StartExecution'],
1740
+ Condition: {
1741
+ Null: { 'aws:ResourceTag/ebk:stage': 'true' },
1742
+ },
1743
+ Effect: 'Deny',
1744
+ Resource: accountStateMachineResources,
1745
+ Sid: 'DenyUntaggedWorkflowExecutionStart',
1746
+ }),
1747
+ statement({
1748
+ Action: ['states:StartExecution'],
1749
+ Condition: {
1750
+ StringEquals: {
1751
+ 'aws:RequestedRegion': options.region,
1752
+ 'aws:ResourceTag/ebk:stage': options.stage,
1753
+ },
1754
+ },
1755
+ Effect: 'Allow',
1756
+ Resource: accountStateMachineResources,
1757
+ Sid: 'StartOnlyTaggedStageWorkflowExecutions',
1758
+ }),
1759
+ statement({
1760
+ Action: ['cloudformation:CreateStack'],
1761
+ Condition: {
1762
+ StringEquals: {
1763
+ 'aws:RequestTag/ebk:stage': options.stage,
1764
+ 'aws:RequestedRegion': options.region,
1765
+ },
1766
+ },
1767
+ Effect: 'Allow',
1768
+ Resource: machineAuthorizationStackResources,
1769
+ Sid: 'CreateTaggedMachineAuthorizationStacks',
1770
+ }),
1771
+ statement({
1772
+ Action: ['cloudformation:DeleteStack', 'cloudformation:UpdateStack'],
1773
+ Condition: {
1774
+ StringEquals: {
1775
+ 'aws:RequestedRegion': options.region,
1776
+ 'aws:ResourceTag/ebk:stage': options.stage,
1777
+ },
1778
+ },
1779
+ Effect: 'Allow',
1780
+ Resource: machineAuthorizationStackResources,
1781
+ Sid: 'MutateTaggedMachineAuthorizationStacks',
1782
+ }),
1783
+ statement({
1784
+ Action: exports.STAGE_AUTHORITY_EVENT_ARCHIVE_CREATE_ACTIONS,
1785
+ Condition: {
1786
+ StringEquals: {
1787
+ 'aws:RequestedRegion': options.region,
1788
+ },
1789
+ },
1790
+ Effect: 'Allow',
1791
+ Resource: eventArchiveResources,
1792
+ Sid: 'CreateNamedFoundationEventArchive',
1793
+ }),
1794
+ statement({
1795
+ Action: exports.STAGE_AUTHORITY_EVENT_ARCHIVE_CREATE_ACTIONS,
1796
+ Condition: {
1797
+ StringEquals: {
1798
+ 'aws:RequestedRegion': options.region,
1799
+ 'aws:ResourceTag/ebk:stage': options.stage,
1800
+ },
1801
+ },
1802
+ Effect: 'Allow',
1803
+ Resource: foundationEventBusResources,
1804
+ Sid: 'CreateArchiveFromTaggedFoundationEventBus',
1805
+ }),
1806
+ statement({
1807
+ Action: exports.STAGE_AUTHORITY_EVENT_ARCHIVE_RESOURCE_ACTIONS,
1808
+ Condition: {
1809
+ StringEquals: {
1810
+ 'aws:RequestedRegion': options.region,
1811
+ },
1812
+ },
1813
+ Effect: 'Allow',
1814
+ Resource: eventArchiveResources,
1815
+ Sid: 'MutateNamedFoundationEventArchive',
1816
+ }),
1817
+ statement({
1818
+ Action: [
1819
+ ...exports.STAGE_AUTHORITY_EVENT_ARCHIVE_CREATE_ACTIONS,
1820
+ ...exports.STAGE_AUTHORITY_EVENT_ARCHIVE_RESOURCE_ACTIONS,
1821
+ ],
1822
+ Effect: 'Deny',
1823
+ NotResource: [...eventArchiveResources, ...foundationEventBusResources],
1824
+ Sid: 'DenyEventArchiveMutationOutsideGeneratedFoundationArchive',
1825
+ }),
1826
+ statement({
1827
+ Action: exports.STAGE_AUTHORITY_KMS_ROTATION_ACTIONS,
1828
+ Condition: {
1829
+ StringEquals: {
1830
+ 'aws:RequestedRegion': options.region,
1831
+ 'aws:ResourceTag/ebk:stage': options.stage,
1832
+ },
1833
+ },
1834
+ Effect: 'Allow',
1835
+ Resource: stageKmsKeyResources,
1836
+ Sid: 'EnableRotationOnStageKmsKeys',
1837
+ }),
1838
+ statement({
1839
+ Action: ['iam:CreateServiceLinkedRole'],
1840
+ Condition: {
1841
+ StringEquals: {
1842
+ 'iam:AWSServiceName': exports.STAGE_AUTHORITY_SERVICE_LINKED_ROLES.map(({ serviceName }) => serviceName),
1843
+ },
1844
+ },
1845
+ Effect: 'Allow',
1846
+ Resource: serviceLinkedRoleResources,
1847
+ Sid: 'CreateOnlyRequiredServiceLinkedRoles',
1848
+ }),
1849
+ statement({
1850
+ Action: exports.STAGE_AUTHORITY_UNSCOPED_CLOUDFRONT_CREATE_ACTIONS,
1851
+ Condition: {
1852
+ StringEquals: { 'aws:RequestedRegion': edgeRegion },
1853
+ },
1854
+ Effect: 'Allow',
1855
+ Resource: ['*'],
1856
+ Sid: 'CreateOnlyGeneratedCloudFrontResources',
1857
+ }),
1858
+ statement({
1859
+ Action: exports.STAGE_AUTHORITY_GLOBAL_WRITE_ACTIONS,
1860
+ Effect: 'Allow',
1861
+ Resource: ['*'],
1862
+ Sid: 'MutateFixedGlobalServiceFamilies',
1863
+ }),
1864
+ statement({
1865
+ Action: exports.STAGE_AUTHORITY_WAF_EDGE_MUTATION_ACTIONS,
1866
+ Condition: {
1867
+ StringEquals: { 'aws:RequestedRegion': edgeRegion },
1868
+ },
1869
+ Effect: 'Allow',
1870
+ Resource: ['*'],
1871
+ Sid: 'MutateCloudFrontScopeWafInEdgeRegion',
1872
+ }),
1873
+ statement({
1874
+ Action: ['route53:CreateHostedZone'],
1875
+ Condition: {
1876
+ Bool: { 'aws:ViaAWSService': 'true' },
1877
+ StringEquals: {
1878
+ 'aws:CalledViaLast': 'servicediscovery.amazonaws.com',
1879
+ },
1880
+ },
1881
+ Effect: 'Allow',
1882
+ Resource: ['*'],
1883
+ Sid: 'CreateOnlyCloudMapPrivateHostedZones',
1884
+ }),
1885
+ statement({
1886
+ Action: exports.STAGE_AUTHORITY_ACCOUNT_SCOPED_GLOBAL_WRITE_ACTIONS,
1887
+ Condition: {
1888
+ StringEquals: { 'aws:RequestedRegion': edgeRegion },
1889
+ },
1890
+ Effect: 'Allow',
1891
+ Resource: [
1892
+ `arn:${options.partition}:cloudfront::${options.accountId}:*/*`,
1893
+ ],
1894
+ Sid: 'MutateAccountCloudFront',
1895
+ }),
1896
+ statement({
1897
+ Action: exports.STAGE_AUTHORITY_CLOUDFRONT_INVALIDATION_ACTIONS,
1898
+ Condition: {
1899
+ StringEquals: {
1900
+ 'aws:RequestedRegion': edgeRegion,
1901
+ 'aws:ResourceTag/ebk:stage': options.stage,
1902
+ },
1903
+ },
1904
+ Effect: 'Allow',
1905
+ Resource: stageCloudFrontDistributionResources,
1906
+ Sid: 'InvalidateOnlyStageCloudFrontDistributions',
1907
+ }),
1908
+ statement({
1909
+ Action: exports.STAGE_AUTHORITY_LAMBDA_DEPLOYMENT_INVOKE_ACTIONS,
1910
+ Condition: {
1911
+ StringEquals: {
1912
+ 'aws:RequestedRegion': options.region,
1913
+ 'aws:ResourceTag/ebk:stage': options.stage,
1914
+ },
1915
+ },
1916
+ Effect: 'Allow',
1917
+ Resource: stageLambdaResources,
1918
+ Sid: 'InvokeOnlyStageLambdaFunctions',
1919
+ }),
1920
+ statement({
1921
+ Action: ['lambda:AddPermission'],
1922
+ Condition: {
1923
+ StringEquals: {
1924
+ 'aws:RequestedRegion': options.region,
1925
+ 'aws:ResourceTag/ebk:stage': options.stage,
1926
+ 'lambda:Principal': LAMBDA_INTEGRATION_PRINCIPALS,
1927
+ },
1928
+ },
1929
+ Effect: 'Allow',
1930
+ Resource: stageLambdaResources,
1931
+ Sid: 'AddOnlyReviewedStageLambdaIntegrations',
1932
+ }),
1933
+ statement({
1934
+ Action: ['lambda:AddPermission'],
1935
+ Condition: {
1936
+ StringEquals: {
1937
+ 'aws:RequestedRegion': options.region,
1938
+ 'aws:ResourceTag/ebk:stage': options.stage,
1939
+ 'lambda:FunctionUrlAuthType': 'NONE',
1940
+ 'lambda:Principal': '*',
1941
+ },
1942
+ },
1943
+ Effect: 'Allow',
1944
+ Resource: stageLambdaResources,
1945
+ Sid: 'AddOnlyPublicStageLambdaFunctionUrlAccess',
1946
+ }),
1947
+ statement({
1948
+ Action: ['lambda:AddPermission'],
1949
+ Condition: {
1950
+ // AddPermission accepts InvokedViaFunctionUrl, but Lambda does not
1951
+ // expose that request member as a caller-side authorization context.
1952
+ // The companion request is distinguishable from the public
1953
+ // InvokeFunctionUrl permission by the absence of FunctionUrlAuthType.
1954
+ Null: {
1955
+ 'lambda:FunctionUrlAuthType': 'true',
1956
+ },
1957
+ StringEquals: {
1958
+ 'aws:RequestedRegion': options.region,
1959
+ 'aws:ResourceTag/ebk:stage': options.stage,
1960
+ 'lambda:Principal': '*',
1961
+ },
1962
+ },
1963
+ Effect: 'Allow',
1964
+ Resource: stageLambdaResources,
1965
+ Sid: 'AddOnlyPublicStageLambdaFunctionUrlInvoke',
1966
+ }),
1967
+ statement({
1968
+ Action: exports.STAGE_AUTHORITY_WAF_LOGGING_ACTIONS,
1969
+ Condition: {
1970
+ ArnLike: {
1971
+ 'wafv2:LogDestinationResource': stageRouterWafLogDestinationResources,
1972
+ },
1973
+ StringEquals: {
1974
+ 'aws:RequestedRegion': edgeRegion,
1975
+ 'aws:ResourceTag/ebk:stage': options.stage,
1976
+ 'wafv2:LogScope': 'CUSTOMER',
1977
+ },
1978
+ },
1979
+ Effect: 'Allow',
1980
+ Resource: stageRouterWebAclResources,
1981
+ Sid: 'PutOnlyStageRouterWafLogging',
1982
+ }),
1983
+ statement({
1984
+ Action: ['budgets:ModifyBudget', 'budgets:TagResource'],
1985
+ Condition: {
1986
+ StringEquals: {
1987
+ 'aws:RequestTag/ebk:stage': options.stage,
1988
+ },
1989
+ },
1990
+ Effect: 'Allow',
1991
+ Resource: stageAccountBudgetResources,
1992
+ Sid: 'CreateOnlyTaggedStageAccountBudget',
1993
+ }),
1994
+ statement({
1995
+ Action: exports.STAGE_AUTHORITY_BUDGET_MUTATION_ACTIONS,
1996
+ Condition: {
1997
+ StringEquals: {
1998
+ 'aws:ResourceTag/ebk:stage': options.stage,
1999
+ },
2000
+ },
2001
+ Effect: 'Allow',
2002
+ Resource: stageAccountBudgetResources,
2003
+ Sid: 'MutateOnlyTaggedStageAccountBudget',
2004
+ }),
2005
+ statement({
2006
+ Action: exports.STAGE_AUTHORITY_RDS_EVENT_SUBSCRIPTION_SOURCE_ACTIONS,
2007
+ Condition: {
2008
+ Null: { 'aws:ResourceTag/ebk:stage': 'true' },
2009
+ },
2010
+ Effect: 'Deny',
2011
+ Resource: productionEventSubscriptionResources,
2012
+ Sid: 'DenyUnscopedDatabaseEventSubscriptionSourceMutation',
2013
+ }),
2014
+ statement({
2015
+ Action: ['lambda:RemovePermission'],
2016
+ Condition: {
2017
+ StringEquals: {
2018
+ 'aws:RequestedRegion': options.region,
2019
+ 'aws:ResourceTag/ebk:stage': options.stage,
2020
+ },
2021
+ },
2022
+ Effect: 'Allow',
2023
+ Resource: stageLambdaResources,
2024
+ Sid: 'RemoveOnlyStageLambdaIntegrations',
2025
+ }),
2026
+ statement({
2027
+ Action: ['lambda:AddPermission', 'lambda:RemovePermission'],
2028
+ Condition: {
2029
+ Null: { 'aws:ResourceTag/ebk:stage': 'true' },
2030
+ },
2031
+ Effect: 'Deny',
2032
+ Resource: ['*'],
2033
+ Sid: 'DenyUnscopedLambdaPermissions',
2034
+ }),
2035
+ statement({
2036
+ Action: ['sns:SetTopicAttributes'],
2037
+ Condition: {
2038
+ StringEquals: {
2039
+ 'aws:RequestedRegion': options.region,
2040
+ },
2041
+ },
2042
+ Effect: 'Allow',
2043
+ Resource: stageTopicResources,
2044
+ Sid: 'SetOnlyStageTopicAttributes',
2045
+ }),
2046
+ // RDS validates that the CreateEventSubscription caller can publish to
2047
+ // its destination topic. The regional service-family allow admits that
2048
+ // control-plane check; this exact deny keeps every other topic closed.
2049
+ // Runtime workloads retain their separately generated least-privilege roles.
2050
+ statement({
2051
+ Action: ['sns:Publish'],
2052
+ Effect: 'Deny',
2053
+ NotResource: generatedFoundationAlertTopicResources,
2054
+ Sid: 'DenyPublishOutsideGeneratedFoundationAlerts',
2055
+ }),
2056
+ statement({
2057
+ Action: ['sqs:SetQueueAttributes'],
2058
+ Condition: {
2059
+ StringEquals: {
2060
+ 'aws:RequestedRegion': options.region,
2061
+ },
2062
+ },
2063
+ Effect: 'Allow',
2064
+ Resource: stageQueueResources,
2065
+ Sid: 'SetOnlyStageQueueAttributes',
2066
+ }),
2067
+ statement({
2068
+ Action: ['sns:SetTopicAttributes'],
2069
+ Effect: 'Deny',
2070
+ NotResource: stageTopicResources,
2071
+ Sid: 'DenyTopicAttributeMutationOutsideStage',
2072
+ }),
2073
+ statement({
2074
+ Action: ['sqs:SetQueueAttributes'],
2075
+ Effect: 'Deny',
2076
+ NotResource: stageQueueResources,
2077
+ Sid: 'DenyQueueAttributeMutationOutsideStage',
2078
+ }),
2079
+ statement({
2080
+ Action: [
2081
+ 'iam:AddRoleToInstanceProfile',
2082
+ 'iam:CreateInstanceProfile',
2083
+ 'iam:DeleteInstanceProfile',
2084
+ 'iam:DeleteRole',
2085
+ 'iam:DeleteRolePolicy',
2086
+ 'iam:PutRolePolicy',
2087
+ 'iam:RemoveRoleFromInstanceProfile',
2088
+ 'iam:TagInstanceProfile',
2089
+ 'iam:TagRole',
2090
+ 'iam:UntagInstanceProfile',
2091
+ 'iam:UntagRole',
2092
+ 'iam:UpdateRole',
2093
+ 'iam:UpdateRoleDescription',
2094
+ ],
2095
+ Effect: 'Allow',
2096
+ Resource: [
2097
+ options.identity.workloadInstanceProfileArnPattern,
2098
+ options.identity.workloadRoleArnPattern,
2099
+ ],
2100
+ Sid: 'ManageStageWorkloadIdentities',
2101
+ }),
2102
+ statement({
2103
+ Action: ['iam:CreateRole'],
2104
+ Condition: {
2105
+ StringEquals: {
2106
+ 'iam:PermissionsBoundary': options.identity.boundaryArn,
2107
+ },
2108
+ },
2109
+ Effect: 'Allow',
2110
+ Resource: [options.identity.workloadRoleArnPattern],
2111
+ Sid: 'CreateOnlyBoundaryConstrainedStageWorkloadRoles',
2112
+ }),
2113
+ statement({
2114
+ Action: ['iam:PutRolePermissionsBoundary'],
2115
+ Condition: {
2116
+ StringEquals: {
2117
+ 'iam:PermissionsBoundary': options.identity.boundaryArn,
2118
+ },
2119
+ },
2120
+ Effect: 'Allow',
2121
+ Resource: [options.identity.workloadRoleArnPattern],
2122
+ Sid: 'ApplyExactStageRuntimeBoundary',
2123
+ }),
2124
+ statement({
2125
+ Action: ['iam:AttachRolePolicy', 'iam:DetachRolePolicy'],
2126
+ Condition: {
2127
+ ArnEquals: {
2128
+ 'iam:PermissionsBoundary': options.identity.boundaryArn,
2129
+ 'iam:PolicyARN': managedPolicies,
2130
+ },
2131
+ },
2132
+ Effect: 'Allow',
2133
+ Resource: [options.identity.workloadRoleArnPattern],
2134
+ Sid: 'AttachOnlyPinnedAwsRuntimePolicies',
2135
+ }),
2136
+ statement({
2137
+ Action: ['iam:PassRole'],
2138
+ Condition: {
2139
+ StringEquals: {
2140
+ 'iam:PassedToService': [
2141
+ 'appconfig.amazonaws.com',
2142
+ 'ec2.amazonaws.com',
2143
+ 'ecs-tasks.amazonaws.com',
2144
+ 'lambda.amazonaws.com',
2145
+ 'states.amazonaws.com',
2146
+ ].sort(compareStrings),
2147
+ },
2148
+ },
2149
+ Effect: 'Allow',
2150
+ Resource: [options.identity.workloadRoleArnPattern],
2151
+ Sid: 'PassOnlyStageWorkloadRoles',
2152
+ }),
2153
+ statement({
2154
+ Action: ['iam:PassRole'],
2155
+ Condition: {
2156
+ StringNotEquals: {
2157
+ 'iam:PassedToService': 'ec2.amazonaws.com',
2158
+ },
2159
+ },
2160
+ Effect: 'Deny',
2161
+ Resource: foundationNatWorkloadRoleResources,
2162
+ Sid: 'DenyFoundationNatRolePassOutsideEc2',
2163
+ }),
2164
+ statement({
2165
+ Action: PRIVILEGE_CONSTRUCTION_ACTIONS,
2166
+ Effect: 'Deny',
2167
+ Resource: ['*'],
2168
+ Sid: 'DenyAccountAndPrivilegeAdministration',
2169
+ }),
2170
+ statement({
2171
+ Action: RESOURCE_POLICY_AND_TRUST_EXPANSION_ACTIONS,
2172
+ Effect: 'Deny',
2173
+ Resource: ['*'],
2174
+ Sid: 'DenyUnreviewableResourcePolicyAndTrustExpansion',
2175
+ }),
2176
+ statement({
2177
+ Action: exports.STAGE_AUTHORITY_UNSUPPORTED_REPLICATION_AND_TOPOLOGY_ACTIONS,
2178
+ Effect: 'Deny',
2179
+ Resource: ['*'],
2180
+ Sid: 'DenyUnsupportedReplicationAndTopology',
2181
+ }),
2182
+ statement({
2183
+ Action: [
2184
+ 'servicediscovery:CreateHttpNamespace',
2185
+ 'servicediscovery:CreatePublicDnsNamespace',
2186
+ ],
2187
+ Effect: 'Deny',
2188
+ Resource: ['*'],
2189
+ Sid: 'DenyNonPrivateServiceDiscoveryNamespaces',
2190
+ }),
2191
+ statement({
2192
+ Action: exports.STAGE_AUTHORITY_S3_BUCKET_POLICY_MUTATION_ACTIONS,
2193
+ Effect: 'Deny',
2194
+ NotResource: stageAndSharedSstBucketResources,
2195
+ Sid: 'DenyBucketPolicyMutationOutsideStageAndSstBootstrap',
2196
+ }),
2197
+ statement({
2198
+ Action: ['lambda:AddPermission'],
2199
+ Condition: {
2200
+ StringNotEquals: {
2201
+ 'lambda:Principal': REVIEWED_LAMBDA_PERMISSION_PRINCIPALS,
2202
+ },
2203
+ },
2204
+ Effect: 'Deny',
2205
+ Resource: stageLambdaResources,
2206
+ Sid: 'DenyUnreviewedLambdaIntegrationPrincipal',
2207
+ }),
2208
+ statement({
2209
+ Action: ['budgets:UntagResource'],
2210
+ Condition: {
2211
+ 'ForAnyValue:StringEquals': {
2212
+ 'aws:TagKeys': 'ebk:stage',
2213
+ },
2214
+ },
2215
+ Effect: 'Deny',
2216
+ Resource: stageAccountBudgetResources,
2217
+ Sid: 'DenyStageAccountBudgetStageTagRemoval',
2218
+ }),
2219
+ statement({
2220
+ Action: IAM_AUTHORITY_MUTATION_ACTIONS,
2221
+ Effect: 'Deny',
2222
+ Resource: [
2223
+ options.identity.boundaryArn,
2224
+ ...options.identity.deploymentPolicyArns,
2225
+ options.identity.deploymentRoleArn,
2226
+ ],
2227
+ Sid: 'DenyStageAuthorityMutation',
2228
+ }),
2229
+ statement({
2230
+ Action: ['iam:DeleteRolePermissionsBoundary'],
2231
+ Effect: 'Deny',
2232
+ Resource: [options.identity.workloadRoleArnPattern],
2233
+ Sid: 'DenyRuntimeBoundaryRemoval',
2234
+ }),
2235
+ statement({
2236
+ Action: ['iam:UpdateAssumeRolePolicy'],
2237
+ Effect: 'Deny',
2238
+ Resource: [options.identity.workloadRoleArnPattern],
2239
+ Sid: 'DenyRuntimeTrustPolicyMutation',
2240
+ }),
2241
+ statement({
2242
+ Action: ['iam:PutRolePermissionsBoundary'],
2243
+ Condition: {
2244
+ ArnNotEquals: {
2245
+ 'iam:PermissionsBoundary': options.identity.boundaryArn,
2246
+ },
2247
+ },
2248
+ Effect: 'Deny',
2249
+ Resource: [options.identity.workloadRoleArnPattern],
2250
+ Sid: 'DenyRuntimeBoundarySubstitution',
2251
+ }),
2252
+ statement({
2253
+ Action: exports.STAGE_AUTHORITY_WORKLOAD_IDENTITY_POLICY_ACTIONS,
2254
+ Condition: {
2255
+ ArnNotEquals: {
2256
+ 'iam:PermissionsBoundary': options.identity.boundaryArn,
2257
+ },
2258
+ },
2259
+ Effect: 'Deny',
2260
+ Resource: [options.identity.workloadRoleArnPattern],
2261
+ Sid: 'DenyUnboundedStageWorkloadIdentityPolicyMutation',
2262
+ }),
2263
+ statement({
2264
+ Action: ['iam:AttachRolePolicy'],
2265
+ Condition: {
2266
+ ArnNotEquals: { 'iam:PolicyARN': managedPolicies },
2267
+ },
2268
+ Effect: 'Deny',
2269
+ Resource: [options.identity.workloadRoleArnPattern],
2270
+ Sid: 'DenyUnapprovedManagedPolicyAttachment',
2271
+ }),
2272
+ statement({
2273
+ Action: ['acm:*'],
2274
+ Condition: {
2275
+ StringNotEquals: {
2276
+ 'aws:RequestedRegion': [options.region, edgeRegion],
2277
+ },
2278
+ },
2279
+ Effect: 'Deny',
2280
+ Resource: ['*'],
2281
+ Sid: 'DenyAcmMutationOutsideStageAndEdgeRegions',
2282
+ }),
2283
+ statement({
2284
+ Action: [
2285
+ ...exports.STAGE_AUTHORITY_S3_REGIONAL_MUTATION_ACTIONS,
2286
+ 's3:CreateBucket',
2287
+ ],
2288
+ Condition: {
2289
+ StringNotEquals: { 'aws:RequestedRegion': options.region },
2290
+ },
2291
+ Effect: 'Deny',
2292
+ Resource: ['*'],
2293
+ Sid: 'DenyS3MutationOutsideStageRegion',
2294
+ }),
2295
+ statement({
2296
+ Action: [
2297
+ ...exports.STAGE_AUTHORITY_ACCOUNT_SCOPED_GLOBAL_WRITE_ACTIONS,
2298
+ ...exports.STAGE_AUTHORITY_CLOUDFRONT_INVALIDATION_ACTIONS,
2299
+ ...exports.STAGE_AUTHORITY_EDGE_WAF_LOG_DELIVERY_MUTATION_ACTIONS,
2300
+ ...exports.STAGE_AUTHORITY_UNSCOPED_CLOUDFRONT_CREATE_ACTIONS,
2301
+ ...exports.STAGE_AUTHORITY_WAF_EDGE_MUTATION_ACTIONS,
2302
+ ...exports.STAGE_AUTHORITY_WAF_LOGGING_ACTIONS,
2303
+ ],
2304
+ Condition: {
2305
+ StringNotEquals: { 'aws:RequestedRegion': edgeRegion },
2306
+ },
2307
+ Effect: 'Deny',
2308
+ Resource: ['*'],
2309
+ Sid: 'DenyEdgeControlMutationOutsideEdgeRegion',
2310
+ }),
2311
+ statement({
2312
+ Action: exports.STAGE_AUTHORITY_REGIONAL_MUTATION_FAMILIES,
2313
+ Condition: {
2314
+ StringNotEquals: { 'aws:RequestedRegion': options.region },
2315
+ },
2316
+ Effect: 'Deny',
2317
+ Resource: ['*'],
2318
+ Sid: 'DenyMutationOutsideStageRegion',
2319
+ }),
2320
+ statement({
2321
+ Action: exports.STAGE_AUTHORITY_LOGS_MUTATION_FAMILIES,
2322
+ Condition: {
2323
+ StringNotEquals: {
2324
+ 'aws:RequestedRegion': [options.region, edgeRegion],
2325
+ },
2326
+ },
2327
+ Effect: 'Deny',
2328
+ Resource: ['*'],
2329
+ Sid: 'DenyLogsMutationOutsideStageAndEdgeRegions',
2330
+ }),
2331
+ // When the application and CloudFront edge control planes share a Region,
2332
+ // RequestedRegion cannot distinguish their log groups. In that case admit
2333
+ // only the generated application and API execution-log namespaces so both
2334
+ // create-time and existing-resource lifecycle calls remain bounded.
2335
+ statement({
2336
+ Action: exports.STAGE_AUTHORITY_EDGE_WAF_LOG_GROUP_ACTIONS,
2337
+ Condition: {
2338
+ StringEquals: { 'aws:RequestedRegion': edgeRegion },
2339
+ },
2340
+ Effect: 'Deny',
2341
+ NotResource: generatedEdgeLogGroupMutationResources,
2342
+ Sid: 'DenyEdgeLogGroupMutationOutsideGeneratedStageLogs',
2343
+ }),
2344
+ statement({
2345
+ Action: exports.STAGE_AUTHORITY_CROSS_ACCOUNT_MUTATION_ACTIONS,
2346
+ Condition: {
2347
+ Null: { 'aws:ResourceAccount': 'false' },
2348
+ StringNotEquals: { 'aws:ResourceAccount': options.accountId },
2349
+ },
2350
+ Effect: 'Deny',
2351
+ Resource: ['*'],
2352
+ Sid: 'DenyCrossAccountMutationWhenResourceAccountIsKnown',
2353
+ }),
2354
+ statement({
2355
+ Action: ['ec2:RunInstances'],
2356
+ Condition: {
2357
+ Null: { 'aws:ResourceAccount': 'false' },
2358
+ StringNotEquals: { 'aws:ResourceAccount': options.accountId },
2359
+ },
2360
+ Effect: 'Deny',
2361
+ NotResource: runInstancesExternalReferenceResources,
2362
+ Sid: 'DenyCrossAccountRunInstancesTargets',
2363
+ }),
2364
+ statement({
2365
+ Action: ['ec2:RunInstances'],
2366
+ Condition: {
2367
+ StringNotEquals: {
2368
+ 'ec2:Owner': runInstancesApprovedReferenceOwners,
2369
+ },
2370
+ },
2371
+ Effect: 'Deny',
2372
+ Resource: runInstancesExternalReferenceResources,
2373
+ Sid: 'DenyRunInstancesFromUnapprovedReferenceOwners',
2374
+ }),
2375
+ statement({
2376
+ Action: exports.STAGE_AUTHORITY_KNOWN_STAGE_MUTATION_ACTIONS,
2377
+ Condition: {
2378
+ Null: { 'aws:ResourceTag/ebk:stage': 'false' },
2379
+ StringNotEquals: { 'aws:ResourceTag/ebk:stage': options.stage },
2380
+ },
2381
+ Effect: 'Deny',
2382
+ Resource: ['*'],
2383
+ Sid: 'DenyKnownOtherStageResourceMutation',
2384
+ }),
2385
+ statement({
2386
+ Action: exports.STAGE_AUTHORITY_UNTAGGED_DESTRUCTIVE_ACTIONS,
2387
+ Condition: {
2388
+ Null: { 'aws:ResourceTag/ebk:stage': 'true' },
2389
+ },
2390
+ Effect: 'Deny',
2391
+ NotResource: stageAndSharedMutationResources,
2392
+ Sid: 'DenyUntaggedMutationOutsideStageAndSstResources',
2393
+ }),
2394
+ statement({
2395
+ Action: ['*'],
2396
+ Condition: {
2397
+ Null: { 'aws:RequestTag/ebk:stage': 'false' },
2398
+ StringNotEquals: { 'aws:RequestTag/ebk:stage': options.stage },
2399
+ },
2400
+ Effect: 'Deny',
2401
+ Resource: ['*'],
2402
+ Sid: 'DenyExplicitOtherStageRequestTag',
2403
+ }),
2404
+ ];
2405
+ if (options.stage !== 'production') {
2406
+ statements.push(statement({
2407
+ Action: ['kms:ScheduleKeyDeletion'],
2408
+ Condition: {
2409
+ StringEquals: {
2410
+ 'aws:RequestedRegion': options.region,
2411
+ 'aws:ResourceTag/ebk:management': 'standard-data-export',
2412
+ 'aws:ResourceTag/ebk:stage': options.stage,
2413
+ },
2414
+ },
2415
+ Effect: 'Allow',
2416
+ Resource: stageKmsKeyResources,
2417
+ Sid: 'RetireOnlyTaggedNonProductionKmsKeys',
2418
+ }), statement({
2419
+ Action: exports.STAGE_AUTHORITY_KNOWN_STAGE_MUTATION_ACTIONS,
2420
+ Condition: {
2421
+ StringEquals: { 'aws:ResourceTag/ebk:stage': 'production' },
2422
+ },
2423
+ Effect: 'Deny',
2424
+ Resource: ['*'],
2425
+ Sid: 'DenyNonProductionAccessToProductionTaggedResources',
2426
+ }), statement({
2427
+ Action: exports.STAGE_AUTHORITY_KNOWN_STAGE_MUTATION_ACTIONS,
2428
+ Effect: 'Deny',
2429
+ Resource: namedProductionResources,
2430
+ Sid: 'DenyNonProductionAccessToNamedProductionResources',
2431
+ }), statement({
2432
+ Action: exports.STAGE_AUTHORITY_RDS_EVENT_SUBSCRIPTION_SOURCE_ACTIONS,
2433
+ Effect: 'Deny',
2434
+ Resource: ['*'],
2435
+ Sid: 'DenyNonProductionDatabaseEventSubscriptionSourceMutation',
2436
+ }));
2437
+ }
2438
+ if (options.stage === 'production') {
2439
+ statements.push(statement({
2440
+ Action: ['kms:ScheduleKeyDeletion'],
2441
+ Effect: 'Deny',
2442
+ Resource: stageKmsKeyResources,
2443
+ Sid: 'DenyProtectedProductionKmsKeyDeletion',
2444
+ }), statement({
2445
+ Action: ['kms:DeleteAlias'],
2446
+ Effect: 'Deny',
2447
+ Resource: productionDataExportAliasResources,
2448
+ Sid: 'DenyProtectedProductionDataExportAliasDeletion',
2449
+ }), statement({
2450
+ Action: [
2451
+ 's3:DeleteBucket',
2452
+ 's3:DeleteBucketPolicy',
2453
+ 's3:DeleteObject',
2454
+ 's3:DeleteObjectVersion',
2455
+ ],
2456
+ Effect: 'Deny',
2457
+ Resource: productionDataExportBucketResources,
2458
+ Sid: 'DenyProtectedProductionDataExportDeletion',
2459
+ }), statement({
2460
+ Action: exports.STAGE_AUTHORITY_RDS_EVENT_SUBSCRIPTION_SOURCE_ACTIONS,
2461
+ Condition: {
2462
+ StringEquals: {
2463
+ 'aws:RequestedRegion': options.region,
2464
+ 'aws:ResourceTag/ebk:stage': 'production',
2465
+ },
2466
+ },
2467
+ Effect: 'Allow',
2468
+ Resource: productionEventSubscriptionResources,
2469
+ Sid: 'MutateOnlyProductionDatabaseEventSubscriptionSources',
2470
+ }), statement({
2471
+ Action: exports.STAGE_AUTHORITY_COST_ANOMALY_READ_ACTIONS,
2472
+ Condition: {
2473
+ StringLike: {
2474
+ 'aws:ResourceTag/ebk:cost-scope': productionCostScope,
2475
+ },
2476
+ },
2477
+ Effect: 'Allow',
2478
+ Resource: costAnomalyResources,
2479
+ Sid: 'ReadProductionCostAnomalyControl',
2480
+ }), statement({
2481
+ Action: exports.STAGE_AUTHORITY_COST_ANOMALY_CREATE_ACTIONS,
2482
+ Condition: {
2483
+ StringEquals: {
2484
+ 'aws:RequestTag/ebk:stage': options.stage,
2485
+ },
2486
+ StringLike: {
2487
+ 'aws:RequestTag/ebk:cost-scope': productionCostScope,
2488
+ },
2489
+ },
2490
+ Effect: 'Allow',
2491
+ Resource: ['*'],
2492
+ Sid: 'CreateProductionCostAnomalyControl',
2493
+ }), statement({
2494
+ Action: exports.STAGE_AUTHORITY_COST_ANOMALY_RESOURCE_WRITE_ACTIONS,
2495
+ Condition: {
2496
+ StringLike: {
2497
+ 'aws:ResourceTag/ebk:cost-scope': productionCostScope,
2498
+ },
2499
+ },
2500
+ Effect: 'Allow',
2501
+ Resource: costAnomalyResources,
2502
+ Sid: 'MutateProductionCostAnomalyControl',
2503
+ }), statement({
2504
+ Action: ['ce:TagResource'],
2505
+ Condition: {
2506
+ Null: { 'aws:RequestTag/ebk:cost-scope': 'false' },
2507
+ StringNotLike: {
2508
+ 'aws:RequestTag/ebk:cost-scope': productionCostScope,
2509
+ },
2510
+ },
2511
+ Effect: 'Deny',
2512
+ Resource: costAnomalyResources,
2513
+ Sid: 'DenyProductionCostScopeSubstitution',
2514
+ }), statement({
2515
+ Action: ['ce:UntagResource'],
2516
+ Condition: {
2517
+ 'ForAnyValue:StringEquals': {
2518
+ 'aws:TagKeys': 'ebk:cost-scope',
2519
+ },
2520
+ },
2521
+ Effect: 'Deny',
2522
+ Resource: costAnomalyResources,
2523
+ Sid: 'DenyProductionCostScopeRemoval',
2524
+ }), statement({
2525
+ Action: [
2526
+ 'backup:DeleteRecoveryPoint',
2527
+ 'rds:DeleteDBCluster',
2528
+ 'rds:DeleteDBClusterAutomatedBackup',
2529
+ 'rds:DeleteDBInstance',
2530
+ 'rds:DeleteDBSnapshot',
2531
+ ],
2532
+ Effect: 'Deny',
2533
+ Resource: productionDatabaseResources,
2534
+ Sid: 'DenyProtectedProductionDatabaseDeletion',
2535
+ }), statement({
2536
+ Action: [
2537
+ 'backup:DeleteBackupPlan',
2538
+ 'backup:DeleteBackupSelection',
2539
+ 'backup:DeleteBackupVault',
2540
+ 'backup:DeleteRecoveryPoint',
2541
+ ],
2542
+ Effect: 'Deny',
2543
+ Resource: productionRetentionResources,
2544
+ Sid: 'DenyProtectedProductionRetentionDeletion',
2545
+ }));
2546
+ }
2547
+ const groups = [
2548
+ {
2549
+ kind: 'read',
2550
+ logicalId: 'StageDeploymentReadPolicy',
2551
+ sids: [
2552
+ 'DenyGeneratedDatabaseValueAccessWithWrongStageTag',
2553
+ 'DenyNonPrivateServiceDiscoveryNamespaces',
2554
+ 'DenyRuntimeBoundaryRemoval',
2555
+ 'DenySecretsManagerBatchValueReads',
2556
+ 'DenySecretValueAccessOutsideGeneratedFoundationDatabase',
2557
+ 'DenySsmHistoryAndRecursiveValueReads',
2558
+ 'DenyWorkflowExecutionReadOutsideAccount',
2559
+ 'DenyUnapprovedSsmValueReads',
2560
+ 'CreateOnlyAwsResourceKmsGrants',
2561
+ 'ManageEdgeWafLogDeliveryControlPlane',
2562
+ 'ManageOnlyGeneratedEdgeWafLogGroup',
2563
+ 'PutOnlyStageRouterWafLogging',
2564
+ 'ReadFixedGlobalServiceFamilies',
2565
+ 'ReadFixedRegionalServiceFamilies',
2566
+ 'DenyWorkflowExecutionReadWithoutStageTag',
2567
+ 'DescribeOnlyTaggedStageWorkflowExecutions',
2568
+ ...(options.stage === 'production'
2569
+ ? ['ReadProductionCostAnomalyControl']
2570
+ : []),
2571
+ ],
2572
+ },
2573
+ {
2574
+ kind: 'regional-mutation',
2575
+ logicalId: 'StageDeploymentRegionalMutationPolicy',
2576
+ sids: [
2577
+ 'CreateOnlyCloudMapPrivateHostedZones',
2578
+ 'CreateBucketOnlyInStageRegion',
2579
+ 'DenyAcmMutationOutsideStageAndEdgeRegions',
2580
+ 'DenyEdgeControlMutationOutsideEdgeRegion',
2581
+ 'DenyMutationOutsideStageRegion',
2582
+ 'DenyS3MutationOutsideStageRegion',
2583
+ 'DenySecretMetadataUpdateOutsideStageContainers',
2584
+ 'DenyUnscopedWorkflowExecutionMutation',
2585
+ 'InvokeOnlyStageLambdaFunctions',
2586
+ 'MutateEdgeCertificates',
2587
+ 'MutateFixedRegionalServiceFamilies',
2588
+ 'MutateStageBucketsInStageRegion',
2589
+ ],
2590
+ },
2591
+ {
2592
+ kind: 'global-iam-mutation',
2593
+ logicalId: 'StageDeploymentGlobalIamMutationPolicy',
2594
+ sids: [
2595
+ 'AddOnlyReviewedStageLambdaIntegrations',
2596
+ 'ApplyExactStageRuntimeBoundary',
2597
+ 'AttachOnlyPinnedAwsRuntimePolicies',
2598
+ 'CreateOnlyBoundaryConstrainedStageWorkloadRoles',
2599
+ 'DenyFoundationNatRolePassOutsideEc2',
2600
+ 'DenyQueueAttributeMutationOutsideStage',
2601
+ 'DenyStageAuthorityMutation',
2602
+ 'DenyTopicAttributeMutationOutsideStage',
2603
+ 'DenyUnscopedLambdaPermissions',
2604
+ 'ManageStageWorkloadIdentities',
2605
+ 'PassOnlyStageWorkloadRoles',
2606
+ 'RemoveOnlyStageLambdaIntegrations',
2607
+ 'SetOnlyStageQueueAttributes',
2608
+ 'SetOnlyStageTopicAttributes',
2609
+ ],
2610
+ },
2611
+ {
2612
+ kind: 'global-resource-mutation',
2613
+ logicalId: 'StageDeploymentGlobalResourceMutationPolicy',
2614
+ sids: [
2615
+ 'DenyCrossAccountMutationWhenResourceAccountIsKnown',
2616
+ 'DenyCrossAccountRunInstancesTargets',
2617
+ 'DenyLogsMutationOutsideStageAndEdgeRegions',
2618
+ 'DenyRunInstancesFromUnapprovedReferenceOwners',
2619
+ 'DenyUnsupportedReplicationAndTopology',
2620
+ 'LaunchStageInfrastructureFromApprovedReferences',
2621
+ 'MutateAccountCloudFront',
2622
+ 'MutateCloudFrontScopeWafInEdgeRegion',
2623
+ 'MutateFixedGlobalServiceFamilies',
2624
+ 'CreateOnlyTaggedStageAccountBudget',
2625
+ 'DenyStageAccountBudgetStageTagRemoval',
2626
+ 'MutateOnlyTaggedStageAccountBudget',
2627
+ ],
2628
+ },
2629
+ {
2630
+ kind: 'generated-resource-mutation',
2631
+ logicalId: 'StageDeploymentGeneratedResourceMutationPolicy',
2632
+ sids: [
2633
+ 'AddOnlyPublicStageLambdaFunctionUrlAccess',
2634
+ 'AddOnlyPublicStageLambdaFunctionUrlInvoke',
2635
+ 'CreateOnlyRequiredServiceLinkedRoles',
2636
+ 'CreateArchiveFromTaggedFoundationEventBus',
2637
+ 'CreateNamedFoundationEventArchive',
2638
+ 'CreateTaggedMachineAuthorizationStacks',
2639
+ 'DenyPublishOutsideGeneratedFoundationAlerts',
2640
+ 'EnableRotationOnStageKmsKeys',
2641
+ 'InvalidateOnlyStageCloudFrontDistributions',
2642
+ 'MutateNamedFoundationEventArchive',
2643
+ 'MutateTaggedMachineAuthorizationStacks',
2644
+ 'DenyOwnerMutation',
2645
+ ...(options.stage !== 'production'
2646
+ ? ['RetireOnlyTaggedNonProductionKmsKeys']
2647
+ : []),
2648
+ 'DenyUntaggedWorkflowExecutionStart',
2649
+ 'StartOnlyTaggedStageWorkflowExecutions',
2650
+ ...(options.stage === 'production'
2651
+ ? [
2652
+ 'CreateProductionCostAnomalyControl',
2653
+ 'DenyProductionCostScopeRemoval',
2654
+ 'DenyProductionCostScopeSubstitution',
2655
+ 'MutateOnlyProductionDatabaseEventSubscriptionSources',
2656
+ 'MutateProductionCostAnomalyControl',
2657
+ ]
2658
+ : []),
2659
+ ],
2660
+ },
2661
+ {
2662
+ kind: 'stage-containment',
2663
+ logicalId: 'StageDeploymentStageContainmentPolicy',
2664
+ sids: [
2665
+ 'CreateOnlyGeneratedCloudFrontResources',
2666
+ 'DenyBucketPolicyMutationOutsideStageAndSstBootstrap',
2667
+ 'DenyEventArchiveMutationOutsideGeneratedFoundationArchive',
2668
+ 'DenyExplicitOtherStageRequestTag',
2669
+ 'DenyKnownOtherStageResourceMutation',
2670
+ ...(options.stage !== 'production'
2671
+ ? ['DenyNonProductionDatabaseEventSubscriptionSourceMutation']
2672
+ : []),
2673
+ 'DenyRuntimeTrustPolicyMutation',
2674
+ 'DenyUnscopedDatabaseEventSubscriptionSourceMutation',
2675
+ 'DenyUntaggedMutationOutsideStageAndSstResources',
2676
+ ],
2677
+ },
2678
+ {
2679
+ kind: 'permission-guardrails',
2680
+ logicalId: 'StageDeploymentPermissionGuardrailsPolicy',
2681
+ sids: [
2682
+ 'DenyNonDeploymentControlOperations',
2683
+ 'DenyUnboundedStageWorkloadIdentityPolicyMutation',
2684
+ 'DenyUnsupportedWorkflowExecutionMutation',
2685
+ 'DenyUnreviewableResourcePolicyAndTrustExpansion',
2686
+ ],
2687
+ },
2688
+ {
2689
+ kind: 'guardrails',
2690
+ logicalId: 'StageDeploymentGuardrailsPolicy',
2691
+ sids: [
2692
+ ...statements
2693
+ .filter((entry) => entry.Sid.startsWith('Deny') &&
2694
+ ![
2695
+ 'DenyCrossAccountMutationWhenResourceAccountIsKnown',
2696
+ 'DenyCrossAccountRunInstancesTargets',
2697
+ 'DenyEdgeControlMutationOutsideEdgeRegion',
2698
+ 'DenyAcmMutationOutsideStageAndEdgeRegions',
2699
+ 'DenyBucketPolicyMutationOutsideStageAndSstBootstrap',
2700
+ 'DenyEventArchiveMutationOutsideGeneratedFoundationArchive',
2701
+ 'DenyExplicitOtherStageRequestTag',
2702
+ 'DenyFoundationNatRolePassOutsideEc2',
2703
+ 'DenyGeneratedDatabaseValueAccessWithWrongStageTag',
2704
+ 'DenyKnownOtherStageResourceMutation',
2705
+ 'DenyLogsMutationOutsideStageAndEdgeRegions',
2706
+ 'DenyMutationOutsideStageRegion',
2707
+ 'DenyNonDeploymentControlOperations',
2708
+ 'DenyNonPrivateServiceDiscoveryNamespaces',
2709
+ 'DenyOwnerMutation',
2710
+ 'DenyNonProductionDatabaseEventSubscriptionSourceMutation',
2711
+ 'DenyProductionCostScopeRemoval',
2712
+ 'DenyProductionCostScopeSubstitution',
2713
+ 'DenyPublishOutsideGeneratedFoundationAlerts',
2714
+ 'DenyQueueAttributeMutationOutsideStage',
2715
+ 'DenyRuntimeBoundaryRemoval',
2716
+ 'DenyRuntimeTrustPolicyMutation',
2717
+ 'DenySecretsManagerBatchValueReads',
2718
+ 'DenySecretMetadataUpdateOutsideStageContainers',
2719
+ 'DenySecretValueAccessOutsideGeneratedFoundationDatabase',
2720
+ 'DenySsmHistoryAndRecursiveValueReads',
2721
+ 'DenyStageAccountBudgetStageTagRemoval',
2722
+ 'DenyStageAuthorityMutation',
2723
+ 'DenyTopicAttributeMutationOutsideStage',
2724
+ 'DenyUnapprovedSsmValueReads',
2725
+ 'DenyUnreviewableResourcePolicyAndTrustExpansion',
2726
+ 'DenyUnscopedDatabaseEventSubscriptionSourceMutation',
2727
+ 'DenyUnscopedLambdaPermissions',
2728
+ 'DenyUnboundedStageWorkloadIdentityPolicyMutation',
2729
+ 'DenyUnscopedWorkflowExecutionMutation',
2730
+ 'DenyUnsupportedWorkflowExecutionMutation',
2731
+ 'DenyUntaggedWorkflowExecutionStart',
2732
+ 'DenyUntaggedMutationOutsideStageAndSstResources',
2733
+ 'DenyWorkflowExecutionReadWithoutStageTag',
2734
+ 'DenyWorkflowExecutionReadOutsideAccount',
2735
+ 'DenyRunInstancesFromUnapprovedReferenceOwners',
2736
+ 'DenyUnsupportedReplicationAndTopology',
2737
+ 'DenyS3MutationOutsideStageRegion',
2738
+ ].includes(entry.Sid))
2739
+ .map((entry) => entry.Sid),
2740
+ 'ReadEdgeCertificateControlPlane',
2741
+ 'ReadEdgeObservabilityControlPlane',
2742
+ ],
2743
+ },
2744
+ ];
2745
+ const assigned = new Set(groups.flatMap((group) => group.sids));
2746
+ const assignmentCount = groups.reduce((total, group) => total + group.sids.length, 0);
2747
+ const unassigned = statements.filter((entry) => !assigned.has(entry.Sid));
2748
+ if (unassigned.length > 0 ||
2749
+ assigned.size !== statements.length ||
2750
+ assignmentCount !== statements.length) {
2751
+ throw new Error(`Stage authority policy groups must assign every statement exactly once; unassigned: ${unassigned.map((entry) => entry.Sid).join(', ') ||
2752
+ 'duplicate assignment'}.`);
2753
+ }
2754
+ return Object.freeze(groups.map((group, index) => {
2755
+ const document = policy(statements.filter((entry) => group.sids.includes(entry.Sid)));
2756
+ const characters = JSON.stringify(omitPolicyStatementSids(document)).length;
2757
+ if (characters >
2758
+ exports.STAGE_AUTHORITY_MANAGED_POLICY_CHARACTER_LIMIT -
2759
+ exports.STAGE_AUTHORITY_MANAGED_POLICY_CHARACTER_HEADROOM) {
2760
+ throw new Error(`Stage authority ${group.kind} policy has ${characters} characters; it must retain ${exports.STAGE_AUTHORITY_MANAGED_POLICY_CHARACTER_HEADROOM} characters below the IAM managed-policy limit of ${exports.STAGE_AUTHORITY_MANAGED_POLICY_CHARACTER_LIMIT}.`);
2761
+ }
2762
+ const name = options.identity.deploymentPolicyNames[index];
2763
+ const arn = options.identity.deploymentPolicyArns[index];
2764
+ if (!name || !arn) {
2765
+ throw new Error(`Stage authority ${group.kind} identity is incomplete.`);
2766
+ }
2767
+ return Object.freeze({
2768
+ arn,
2769
+ document,
2770
+ kind: group.kind,
2771
+ logicalId: group.logicalId,
2772
+ name,
2773
+ });
2774
+ }));
2775
+ }
2776
+ function canonicalStageAuthorityTemplate(template) {
2777
+ return `${JSON.stringify(canonicalize(template))}\n`;
2778
+ }
2779
+ function digestStageAuthorityTemplate(template) {
2780
+ return (0, node_crypto_1.createHash)('sha256')
2781
+ .update(canonicalStageAuthorityTemplate(template), 'utf8')
2782
+ .digest('hex');
2783
+ }
2784
+ function assertBoundStageAuthorityTemplateFitsBodyLimit(template) {
2785
+ const templateBytes = Buffer.byteLength(canonicalStageAuthorityTemplate(template), 'utf8');
2786
+ if (templateBytes > exports.STAGE_AUTHORITY_TEMPLATE_BODY_BYTE_LIMIT) {
2787
+ throw new Error(`Workspace-bound stage authority template has ${templateBytes} bytes; the direct CloudFormation TemplateBody limit is ${exports.STAGE_AUTHORITY_TEMPLATE_BODY_BYTE_LIMIT}.`);
2788
+ }
2789
+ return templateBytes;
2790
+ }
2791
+ function createStageAuthorityPlan(input) {
2792
+ const target = requireIdentity(input);
2793
+ const identity = createStageAuthorityIdentity(target);
2794
+ const assumeRolePolicy = requireTrust(input, target);
2795
+ const boundary = runtimeBoundaryPolicy({
2796
+ accountId: target.accountId,
2797
+ identity,
2798
+ region: target.region,
2799
+ stage: target.stage,
2800
+ });
2801
+ const boundaryCharacters = JSON.stringify(omitPolicyStatementSids(boundary)).length;
2802
+ if (boundaryCharacters > exports.STAGE_AUTHORITY_MANAGED_POLICY_CHARACTER_LIMIT) {
2803
+ throw new Error(`Stage runtime boundary has ${boundaryCharacters} characters; the IAM managed-policy limit is ${exports.STAGE_AUTHORITY_MANAGED_POLICY_CHARACTER_LIMIT}.`);
2804
+ }
2805
+ const deployment = deploymentPolicy({
2806
+ accountId: target.accountId,
2807
+ identity,
2808
+ partition: target.partition,
2809
+ region: target.region,
2810
+ stage: target.stage,
2811
+ });
2812
+ const renderedDeploymentPolicyDocuments = Object.fromEntries(deployment.map((entry) => {
2813
+ let document = replaceExactTemplateString(replaceExactTemplateString(omitPolicyStatementSids(entry.document), identity.boundaryArn, { Ref: 'StageRuntimeBoundary' }), identity.deploymentRoleArn, { 'Fn::GetAtt': ['StageDeploymentRole', 'Arn'] });
2814
+ if (entry.logicalId === 'StageDeploymentGlobalIamMutationPolicy') {
2815
+ for (const sibling of deployment) {
2816
+ if (sibling.logicalId === entry.logicalId)
2817
+ continue;
2818
+ document = replaceExactTemplateString(document, sibling.arn, {
2819
+ Ref: sibling.logicalId,
2820
+ });
2821
+ }
2822
+ }
2823
+ return [entry.logicalId, document];
2824
+ }));
2825
+ const edgeRegion = edgeControlRegion(target.partition, target.region);
2826
+ const templatePolicyDocuments = compactExactTemplatePolicyStrings({
2827
+ ...renderedDeploymentPolicyDocuments,
2828
+ StageRuntimeBoundary: omitPolicyStatementSids(boundary),
2829
+ }, [
2830
+ identity.workloadRoleArnPattern,
2831
+ `arn:${target.partition}:cloudformation:${target.region}:${target.accountId}:stack/*MachineAuthorization*/*`,
2832
+ `arn:${target.partition}:events:${target.region}:${target.accountId}:archive/ebk-${target.stage}-foundation-events`,
2833
+ `arn:${target.partition}:events:${target.region}:${target.accountId}:event-bus/foundation-${target.stage}-FoundationEventBusBus-*`,
2834
+ `arn:${target.partition}:lambda:${target.region}:${target.accountId}:function:*`,
2835
+ `arn:${target.partition}:logs:${edgeRegion}:${target.accountId}:log-group:aws-waf-logs-foundation-${target.stage}-FoundationRouter-*`,
2836
+ `arn:${target.partition}:secretsmanager:${target.region}:${target.accountId}:secret:foundation-${target.stage}-FoundationDatabaseProxySecret-*`,
2837
+ `arn:${target.partition}:states:${target.region}:${target.accountId}:execution:*:*`,
2838
+ ]);
2839
+ const deploymentPolicyResources = Object.fromEntries(deployment.map((entry) => [
2840
+ entry.logicalId,
2841
+ {
2842
+ Properties: {
2843
+ Description: `Stable reviewed ${target.stage} ${entry.kind} deployment authority.`,
2844
+ ManagedPolicyName: entry.name,
2845
+ Path: identity.deploymentRolePath,
2846
+ // IAM statement Sids are diagnostic labels, not authority. Keep them
2847
+ // in the executable policy model while omitting them from the direct
2848
+ // CloudFormation body so every supported partition retains bounded
2849
+ // TemplateBody headroom without changing effective permissions.
2850
+ // Ref on AWS::IAM::ManagedPolicy resolves to its exact ARN. Use the
2851
+ // runtime-boundary resource identity in rendered policy documents
2852
+ // instead of repeating its long partition/account/path ARN. The
2853
+ // executable policy model retains the literal ARN for simulation;
2854
+ // this is representation-only direct-TemplateBody compaction.
2855
+ PolicyDocument: templatePolicyDocuments.documents[entry.logicalId],
2856
+ Roles: [{ Ref: 'StageDeploymentRole' }],
2857
+ },
2858
+ Type: 'AWS::IAM::ManagedPolicy',
2859
+ },
2860
+ ]));
2861
+ const template = {
2862
+ AWSTemplateFormatVersion: '2010-09-09',
2863
+ ...(templatePolicyDocuments.mappings
2864
+ ? { Mappings: templatePolicyDocuments.mappings }
2865
+ : {}),
2866
+ Outputs: {
2867
+ DeploymentRoleArn: {
2868
+ Value: { 'Fn::GetAtt': ['StageDeploymentRole', 'Arn'] },
2869
+ },
2870
+ RuntimeBoundaryArn: { Value: { Ref: 'StageRuntimeBoundary' } },
2871
+ },
2872
+ Resources: {
2873
+ ...deploymentPolicyResources,
2874
+ StageDeploymentRole: {
2875
+ Properties: {
2876
+ AssumeRolePolicyDocument: omitPolicyStatementSids(assumeRolePolicy),
2877
+ Description: `Stable Empire Builder Kit ${target.stage} deployment role.`,
2878
+ MaxSessionDuration: 3_600,
2879
+ Path: identity.deploymentRolePath,
2880
+ RoleName: identity.deploymentRoleName,
2881
+ Tags: [
2882
+ { Key: 'ebk:managed-by', Value: 'empire-builder-kit' },
2883
+ { Key: 'ebk:stage', Value: target.stage },
2884
+ ],
2885
+ },
2886
+ Type: 'AWS::IAM::Role',
2887
+ },
2888
+ StageRuntimeBoundary: {
2889
+ Properties: {
2890
+ Description: `Stable maximum permissions for ${target.stage} workload roles.`,
2891
+ ManagedPolicyName: identity.boundaryName,
2892
+ Path: identity.boundaryPath,
2893
+ PolicyDocument: templatePolicyDocuments.documents['StageRuntimeBoundary'],
2894
+ },
2895
+ Type: 'AWS::IAM::ManagedPolicy',
2896
+ },
2897
+ },
2898
+ };
2899
+ const templateBytes = Buffer.byteLength(canonicalStageAuthorityTemplate(template), 'utf8');
2900
+ if (templateBytes > exports.STAGE_AUTHORITY_TEMPLATE_BODY_BYTE_LIMIT) {
2901
+ throw new Error(`Stage authority template has ${templateBytes} bytes; the direct CloudFormation TemplateBody limit is ${exports.STAGE_AUTHORITY_TEMPLATE_BODY_BYTE_LIMIT}.`);
2902
+ }
2903
+ return deepFreeze({
2904
+ deploymentPolicies: deployment,
2905
+ digest: digestStageAuthorityTemplate(template),
2906
+ identity,
2907
+ limitations: Object.freeze({
2908
+ protectedDatabaseConfiguration: 'iam-cannot-inspect-rds-modify-request-fields-lifecycle-preflight-required',
2909
+ resourcePolicyAttributes: 'sns-and-sqs-set-attributes-can-write-resource-policies-name-account-region-containment-applies',
2910
+ sameAccountStageDeploymentIsolation: 'deployment-role-is-account-scoped-use-production-isolated-for-hard-production-boundary',
2911
+ wafLoggingResourcePolicy: 'cloudwatch-logs-put-resource-policy-has-no-resource-or-action-condition-keys-reviewed-generated-source-is-control',
2912
+ workloadTrust: 'iam-cannot-inspect-create-role-trust-reviewed-generated-source-is-control',
2913
+ }),
2914
+ runtimeBoundaryPolicy: boundary,
2915
+ schemaVersion: exports.STAGE_AUTHORITY_SCHEMA_VERSION,
2916
+ template,
2917
+ });
2918
+ }
2919
+ function createStageIamBoundaryTransformContract(input) {
2920
+ const identity = createStageAuthorityIdentity({
2921
+ ...input,
2922
+ region: 'us-east-1',
2923
+ });
2924
+ return Object.freeze({
2925
+ boundaryArn: identity.boundaryArn,
2926
+ instanceProfilePath: identity.workloadInstanceProfilePath,
2927
+ rolePath: identity.workloadRolePath,
2928
+ });
2929
+ }
2930
+ function requireTransformArgs(value, label) {
2931
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
2932
+ throw new Error(`${label} transform arguments must be an object.`);
2933
+ }
2934
+ return value;
2935
+ }
2936
+ function refuseSubstitution(existing, expected, label) {
2937
+ if (existing !== undefined && existing !== expected) {
2938
+ throw new Error(`${label} attempted to substitute stable stage authority.`);
2939
+ }
2940
+ }
2941
+ /**
2942
+ * Installs ordinary Pulumi resource transforms before generated SST components
2943
+ * are constructed. SST remains responsible for creating its roles and NAT
2944
+ * instance profiles; this helper contributes only the stable stage path and
2945
+ * permissions boundary.
2946
+ */
2947
+ function installStageIamBoundaryTransforms(input) {
2948
+ if (typeof input.registerTransform !== 'function' ||
2949
+ input.roleResource === undefined ||
2950
+ input.instanceProfileResource === undefined) {
2951
+ throw new Error('Stage IAM boundary transforms require the ordinary Pulumi role and instance-profile resource types.');
2952
+ }
2953
+ const contract = createStageIamBoundaryTransformContract(input);
2954
+ input.registerTransform(input.roleResource, (value) => {
2955
+ const args = requireTransformArgs(value, 'IAM role');
2956
+ refuseSubstitution(args.permissionsBoundary, contract.boundaryArn, 'IAM role permissions boundary');
2957
+ refuseSubstitution(args.path, contract.rolePath, 'IAM role path');
2958
+ args.permissionsBoundary = contract.boundaryArn;
2959
+ args.path = contract.rolePath;
2960
+ });
2961
+ input.registerTransform(input.instanceProfileResource, (value) => {
2962
+ const args = requireTransformArgs(value, 'IAM instance profile');
2963
+ refuseSubstitution(args.path, contract.instanceProfilePath, 'IAM instance-profile path');
2964
+ args.path = contract.instanceProfilePath;
2965
+ });
2966
+ return contract;
2967
+ }
2968
+ //# sourceMappingURL=stage-authority.js.map