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

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