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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (310) hide show
  1. package/executors.json +8 -3
  2. package/package.json +14 -7
  3. package/src/aws/child-environment.d.ts +10 -0
  4. package/src/aws/child-environment.js +77 -0
  5. package/src/aws/child-environment.js.map +1 -0
  6. package/src/aws/identity.d.ts +13 -0
  7. package/src/aws/identity.js +41 -0
  8. package/src/aws/identity.js.map +1 -0
  9. package/src/aws-authority/index.d.ts +1 -0
  10. package/src/aws-authority/index.js +5 -0
  11. package/src/aws-authority/index.js.map +1 -0
  12. package/src/aws-authority/stage-authority.d.ts +240 -0
  13. package/src/aws-authority/stage-authority.js +2968 -0
  14. package/src/aws-authority/stage-authority.js.map +1 -0
  15. package/src/cli/main.d.ts +2 -0
  16. package/src/cli/main.js +11 -0
  17. package/src/cli/main.js.map +1 -1
  18. package/src/developer-agent/generator-planning.js +4 -4
  19. package/src/developer-agent/generator-planning.js.map +1 -1
  20. package/src/developer-agent/operation-registry.js +14 -6
  21. package/src/developer-agent/operation-registry.js.map +1 -1
  22. package/src/developer-agent/workspace-inspection.js +11 -2
  23. package/src/developer-agent/workspace-inspection.js.map +1 -1
  24. package/src/executors/deployment-plan/deployment-plan.js +1 -1
  25. package/src/executors/deployment-plan/deployment-plan.js.map +1 -1
  26. package/src/executors/foundation-preflight/foundation-preflight.d.ts +0 -1
  27. package/src/executors/foundation-preflight/foundation-preflight.js.map +1 -1
  28. package/src/executors/foundation-preflight/schema.json +0 -5
  29. package/src/executors/foundation-profile-preflight/foundation-profile-preflight.d.ts +12 -3
  30. package/src/executors/foundation-profile-preflight/foundation-profile-preflight.js +95 -32
  31. package/src/executors/foundation-profile-preflight/foundation-profile-preflight.js.map +1 -1
  32. package/src/executors/foundation-profile-preflight/production-database-safeguard.d.ts +64 -0
  33. package/src/executors/foundation-profile-preflight/production-database-safeguard.js +221 -0
  34. package/src/executors/foundation-profile-preflight/production-database-safeguard.js.map +1 -0
  35. package/src/executors/foundation-profile-preflight/schema.json +1 -6
  36. package/src/executors/foundation-remove-preflight/foundation-remove-preflight.d.ts +11 -1
  37. package/src/executors/foundation-remove-preflight/foundation-remove-preflight.js +44 -21
  38. package/src/executors/foundation-remove-preflight/foundation-remove-preflight.js.map +1 -1
  39. package/src/executors/foundation-remove-preflight/schema.json +6 -1
  40. package/src/executors/github-bootstrap/github-bootstrap.d.ts +1 -0
  41. package/src/executors/github-bootstrap/github-bootstrap.js +1 -0
  42. package/src/executors/github-bootstrap/github-bootstrap.js.map +1 -1
  43. package/src/executors/github-bootstrap/schema.json +7 -1
  44. package/src/executors/infrastructure-plan/infrastructure-plan.d.ts +27 -369
  45. package/src/executors/infrastructure-plan/infrastructure-plan.js +237 -5470
  46. package/src/executors/infrastructure-plan/infrastructure-plan.js.map +1 -1
  47. package/src/executors/infrastructure-plan/schema.json +8 -64
  48. package/src/executors/local-deploy/federated-credential-transport.d.ts +48 -0
  49. package/src/executors/local-deploy/federated-credential-transport.js +252 -0
  50. package/src/executors/local-deploy/federated-credential-transport.js.map +1 -0
  51. package/src/executors/local-deploy/local-approval.d.ts +4 -9
  52. package/src/executors/local-deploy/local-approval.js +3 -7
  53. package/src/executors/local-deploy/local-approval.js.map +1 -1
  54. package/src/executors/local-deploy/local-deploy.d.ts +60 -43
  55. package/src/executors/local-deploy/local-deploy.js +955 -804
  56. package/src/executors/local-deploy/local-deploy.js.map +1 -1
  57. package/src/executors/local-deploy/schema.json +3 -20
  58. package/src/executors/refresh/refresh.js +2 -2
  59. package/src/executors/refresh/refresh.js.map +1 -1
  60. package/src/executors/remove/remove.js +2 -2
  61. package/src/executors/remove/remove.js.map +1 -1
  62. package/src/executors/router-waf-rehearsal/router-waf-rehearsal.d.ts +27 -1
  63. package/src/executors/router-waf-rehearsal/router-waf-rehearsal.js +907 -22
  64. package/src/executors/router-waf-rehearsal/router-waf-rehearsal.js.map +1 -1
  65. package/src/executors/router-waf-rehearsal/schema.json +17 -2
  66. package/src/executors/secret-initialize/schema.json +42 -0
  67. package/src/executors/secret-initialize/secret-initialize.d.ts +32 -0
  68. package/src/executors/secret-initialize/secret-initialize.js +243 -0
  69. package/src/executors/secret-initialize/secret-initialize.js.map +1 -0
  70. package/src/executors/secret-metadata/secret-metadata.d.ts +4 -1
  71. package/src/executors/secret-metadata/secret-metadata.js +76 -29
  72. package/src/executors/secret-metadata/secret-metadata.js.map +1 -1
  73. package/src/executors/sst-lifecycle/generated-secret-retirement.d.ts +53 -0
  74. package/src/executors/sst-lifecycle/generated-secret-retirement.js +192 -0
  75. package/src/executors/sst-lifecycle/generated-secret-retirement.js.map +1 -0
  76. package/src/executors/sst-lifecycle/governed-native-operation.d.ts +46 -0
  77. package/src/executors/sst-lifecycle/governed-native-operation.js +153 -0
  78. package/src/executors/sst-lifecycle/governed-native-operation.js.map +1 -0
  79. package/src/executors/sst-lifecycle/run-sst-command.d.ts +27 -7
  80. package/src/executors/sst-lifecycle/run-sst-command.js +474 -89
  81. package/src/executors/sst-lifecycle/run-sst-command.js.map +1 -1
  82. package/src/executors/sst-lifecycle/schema.json +0 -5
  83. package/src/executors/sst-lifecycle/secret-preflight.d.ts +2 -0
  84. package/src/executors/sst-lifecycle/secret-preflight.js +13 -7
  85. package/src/executors/sst-lifecycle/secret-preflight.js.map +1 -1
  86. package/src/executors/unlock/unlock.js +2 -2
  87. package/src/executors/unlock/unlock.js.map +1 -1
  88. package/src/foundation/provision-logical-database.js +2 -0
  89. package/src/foundation/provision-logical-database.js.map +1 -1
  90. package/src/foundation/router-waf-rate-rehearsal.d.ts +97 -0
  91. package/src/foundation/router-waf-rate-rehearsal.js +210 -0
  92. package/src/foundation/router-waf-rate-rehearsal.js.map +1 -0
  93. package/src/foundation/router-waf.d.ts +59 -9
  94. package/src/foundation/router-waf.js +427 -32
  95. package/src/foundation/router-waf.js.map +1 -1
  96. package/src/foundation/stages.d.ts +1 -0
  97. package/src/foundation/stages.js +11 -0
  98. package/src/foundation/stages.js.map +1 -1
  99. package/src/generators/formatting.d.ts +8 -0
  100. package/src/generators/formatting.js +118 -0
  101. package/src/generators/formatting.js.map +1 -0
  102. package/src/generators/function/function.js +37 -17
  103. package/src/generators/function/function.js.map +1 -1
  104. package/src/generators/post-generation-install.js +2 -2
  105. package/src/generators/post-generation-install.js.map +1 -1
  106. package/src/generators/preset/generator.js +28 -8
  107. package/src/generators/preset/generator.js.map +1 -1
  108. package/src/generators/preset/schema.d.ts +3 -0
  109. package/src/generators/preset/schema.json +5 -5
  110. package/src/generators/preset/workspace-files-developer-agent.js +15 -7
  111. package/src/generators/preset/workspace-files-developer-agent.js.map +1 -1
  112. package/src/generators/preset/workspace-files-ecr-inspector.d.ts +18 -0
  113. package/src/generators/preset/workspace-files-ecr-inspector.js +298 -0
  114. package/src/generators/preset/workspace-files-ecr-inspector.js.map +1 -0
  115. package/src/generators/preset/workspace-files-performance-cost.d.ts +2 -2
  116. package/src/generators/preset/workspace-files-performance-cost.js +8 -8
  117. package/src/generators/preset/workspace-files-performance-cost.js.map +1 -1
  118. package/src/generators/preset/workspace-files-performance-load.d.ts +1 -1
  119. package/src/generators/preset/workspace-files-performance-load.js +29 -24
  120. package/src/generators/preset/workspace-files-performance-load.js.map +1 -1
  121. package/src/generators/preset/workspace-files-stage-authority.d.ts +6 -0
  122. package/src/generators/preset/workspace-files-stage-authority.js +479 -0
  123. package/src/generators/preset/workspace-files-stage-authority.js.map +1 -0
  124. package/src/generators/preset/workspace-files-thin-deploy.d.ts +40 -0
  125. package/src/generators/preset/workspace-files-thin-deploy.js +1500 -0
  126. package/src/generators/preset/workspace-files-thin-deploy.js.map +1 -0
  127. package/src/generators/preset/workspace-files-thin-docs.d.ts +5 -0
  128. package/src/generators/preset/workspace-files-thin-docs.js +240 -0
  129. package/src/generators/preset/workspace-files-thin-docs.js.map +1 -0
  130. package/src/generators/preset/workspace-files-thin-release.d.ts +14 -0
  131. package/src/generators/preset/workspace-files-thin-release.js +26 -0
  132. package/src/generators/preset/workspace-files-thin-release.js.map +1 -0
  133. package/src/generators/preset/workspace-files.d.ts +2 -1
  134. package/src/generators/preset/workspace-files.js +448 -680
  135. package/src/generators/preset/workspace-files.js.map +1 -1
  136. package/src/generators/repository-policy/repository-policy.js +149 -19
  137. package/src/generators/repository-policy/repository-policy.js.map +1 -1
  138. package/src/generators/repository-policy/schema.d.ts +2 -0
  139. package/src/generators/repository-policy/schema.json +11 -1
  140. package/src/generators/repository-policy/tree-policy.d.ts +3 -0
  141. package/src/generators/repository-policy/tree-policy.js +25 -0
  142. package/src/generators/repository-policy/tree-policy.js.map +1 -0
  143. package/src/generators/slice/built-in-plan-api-files.js +1 -0
  144. package/src/generators/slice/built-in-plan-api-files.js.map +1 -1
  145. package/src/generators/slice/built-in-plan-async-work-files.js +41 -0
  146. package/src/generators/slice/built-in-plan-async-work-files.js.map +1 -1
  147. package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js +6 -0
  148. package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js.map +1 -1
  149. package/src/generators/slice/built-in-plan-data-export-infrastructure-files.js +5 -31
  150. package/src/generators/slice/built-in-plan-data-export-infrastructure-files.js.map +1 -1
  151. package/src/generators/slice/built-in-plan-data-files.d.ts +1 -1
  152. package/src/generators/slice/built-in-plan-data-files.js +89 -30
  153. package/src/generators/slice/built-in-plan-data-files.js.map +1 -1
  154. package/src/generators/slice/built-in-plan-data-lifecycle-runtime-files.js +19 -0
  155. package/src/generators/slice/built-in-plan-data-lifecycle-runtime-files.js.map +1 -1
  156. package/src/generators/slice/built-in-plan-data-retention-launcher-files.js +21 -3
  157. package/src/generators/slice/built-in-plan-data-retention-launcher-files.js.map +1 -1
  158. package/src/generators/slice/built-in-plan-database-retirement-rehearsal-files.js +20 -1
  159. package/src/generators/slice/built-in-plan-database-retirement-rehearsal-files.js.map +1 -1
  160. package/src/generators/slice/built-in-plan-foundation-files.js +3 -5
  161. package/src/generators/slice/built-in-plan-foundation-files.js.map +1 -1
  162. package/src/generators/slice/built-in-plan-frontend-files.js +5 -1
  163. package/src/generators/slice/built-in-plan-frontend-files.js.map +1 -1
  164. package/src/generators/slice/built-in-plan-infrastructure-files.d.ts +1 -1
  165. package/src/generators/slice/built-in-plan-infrastructure-files.js +199 -188
  166. package/src/generators/slice/built-in-plan-infrastructure-files.js.map +1 -1
  167. package/src/generators/slice/built-in-plan-metadata-files.js +13 -0
  168. package/src/generators/slice/built-in-plan-metadata-files.js.map +1 -1
  169. package/src/generators/slice/built-in-plan-native-runtime-authority-files.d.ts +2 -0
  170. package/src/generators/slice/built-in-plan-native-runtime-authority-files.js +307 -0
  171. package/src/generators/slice/built-in-plan-native-runtime-authority-files.js.map +1 -0
  172. package/src/generators/slice/built-in-plan-operations-files.js +65 -51
  173. package/src/generators/slice/built-in-plan-operations-files.js.map +1 -1
  174. package/src/generators/slice/built-in-plan-performance-files.js +97 -19
  175. package/src/generators/slice/built-in-plan-performance-files.js.map +1 -1
  176. package/src/generators/slice/built-in-plan-project-files.js +9 -68
  177. package/src/generators/slice/built-in-plan-project-files.js.map +1 -1
  178. package/src/generators/slice/built-in-plan-secret-runtime-files.d.ts +1 -1
  179. package/src/generators/slice/built-in-plan-secret-runtime-files.js +87 -76
  180. package/src/generators/slice/built-in-plan-secret-runtime-files.js.map +1 -1
  181. package/src/generators/slice/built-in-plan-testing-files.js +94 -47
  182. package/src/generators/slice/built-in-plan-testing-files.js.map +1 -1
  183. package/src/generators/slice/built-in-plan-thin-release-files.d.ts +11 -0
  184. package/src/generators/slice/built-in-plan-thin-release-files.js +129 -0
  185. package/src/generators/slice/built-in-plan-thin-release-files.js.map +1 -0
  186. package/src/generators/slice/built-in-plan-verification-files.js +6 -2
  187. package/src/generators/slice/built-in-plan-verification-files.js.map +1 -1
  188. package/src/generators/slice/built-in-plan-web-files.js +4 -2
  189. package/src/generators/slice/built-in-plan-web-files.js.map +1 -1
  190. package/src/generators/slice/built-in-plan-workload-role-files.js +26 -4
  191. package/src/generators/slice/built-in-plan-workload-role-files.js.map +1 -1
  192. package/src/generators/slice/built-in-plan.d.ts +1 -0
  193. package/src/generators/slice/built-in-plan.js +28 -11
  194. package/src/generators/slice/built-in-plan.js.map +1 -1
  195. package/src/generators/slice/slice.js +30 -32
  196. package/src/generators/slice/slice.js.map +1 -1
  197. package/src/generators/upgrade/upgrade.d.ts +1 -1
  198. package/src/generators/upgrade/upgrade.js +200 -48
  199. package/src/generators/upgrade/upgrade.js.map +1 -1
  200. package/src/generators/workload/performance-registration.d.ts +1 -0
  201. package/src/generators/workload/performance-registration.js +9 -11
  202. package/src/generators/workload/performance-registration.js.map +1 -1
  203. package/src/generators/workload/service-performance-files.d.ts +1 -0
  204. package/src/generators/workload/service-performance-files.js +27 -7
  205. package/src/generators/workload/service-performance-files.js.map +1 -1
  206. package/src/generators/workload/workload.d.ts +1 -1
  207. package/src/generators/workload/workload.js +55 -28
  208. package/src/generators/workload/workload.js.map +1 -1
  209. package/src/git-policy/github-bootstrap.d.ts +28 -7
  210. package/src/git-policy/github-bootstrap.js +205 -163
  211. package/src/git-policy/github-bootstrap.js.map +1 -1
  212. package/src/index.d.ts +1 -0
  213. package/src/index.js +1 -0
  214. package/src/index.js.map +1 -1
  215. package/src/local-dev/private-state.d.ts +3 -0
  216. package/src/local-dev/private-state.js +17 -5
  217. package/src/local-dev/private-state.js.map +1 -1
  218. package/src/local-dev/tls.js +5 -1
  219. package/src/local-dev/tls.js.map +1 -1
  220. package/src/performance/cost-collection.d.ts +1 -1
  221. package/src/performance/cost-collection.js +4 -2
  222. package/src/performance/cost-collection.js.map +1 -1
  223. package/src/performance/cost-controls.d.ts +3 -1
  224. package/src/performance/cost-controls.js +9 -4
  225. package/src/performance/cost-controls.js.map +1 -1
  226. package/src/performance/evidence.d.ts +1 -1
  227. package/src/performance/evidence.js +5 -3
  228. package/src/performance/evidence.js.map +1 -1
  229. package/src/performance/load-safety.js +5 -3
  230. package/src/performance/load-safety.js.map +1 -1
  231. package/src/performance/model.d.ts +2 -2
  232. package/src/performance/model.js +4 -3
  233. package/src/performance/model.js.map +1 -1
  234. package/src/release/index.d.ts +0 -2
  235. package/src/release/index.js +0 -2
  236. package/src/release/index.js.map +1 -1
  237. package/src/release/source-authority.d.ts +4 -5
  238. package/src/release/source-authority.js +13 -123
  239. package/src/release/source-authority.js.map +1 -1
  240. package/src/repository-policy/aurora-preflight.d.ts +1 -1
  241. package/src/repository-policy/aurora-preflight.js +4 -1
  242. package/src/repository-policy/aurora-preflight.js.map +1 -1
  243. package/src/repository-policy/policy.d.ts +21 -2
  244. package/src/repository-policy/policy.js +290 -25
  245. package/src/repository-policy/policy.js.map +1 -1
  246. package/src/schemas/performance-policy-v1.json +3 -1
  247. package/src/schemas/repository-policy-v1.json +46 -2
  248. package/src/secrets/adapters.d.ts +7 -2
  249. package/src/secrets/adapters.js +34 -3
  250. package/src/secrets/adapters.js.map +1 -1
  251. package/src/secrets/aws-control-plane.d.ts +10 -1
  252. package/src/secrets/aws-control-plane.js +67 -0
  253. package/src/secrets/aws-control-plane.js.map +1 -1
  254. package/src/secrets/generated-aws-control-plane.d.ts +23 -0
  255. package/src/secrets/generated-aws-control-plane.js +259 -0
  256. package/src/secrets/generated-aws-control-plane.js.map +1 -0
  257. package/src/secrets/metadata.js +38 -10
  258. package/src/secrets/metadata.js.map +1 -1
  259. package/src/secrets/model.d.ts +8 -1
  260. package/src/secrets/model.js +24 -3
  261. package/src/secrets/model.js.map +1 -1
  262. package/src/secrets/operator.d.ts +89 -3
  263. package/src/secrets/operator.js +422 -9
  264. package/src/secrets/operator.js.map +1 -1
  265. package/src/secrets/registry.d.ts +2 -1
  266. package/src/secrets/registry.js +18 -1
  267. package/src/secrets/registry.js.map +1 -1
  268. package/src/secrets/workload-access.js +10 -2
  269. package/src/secrets/workload-access.js.map +1 -1
  270. package/src/supply-chain/action-pins.js +61 -7
  271. package/src/supply-chain/action-pins.js.map +1 -1
  272. package/src/supply-chain/build-output-bounds.d.ts +2 -0
  273. package/src/supply-chain/build-output-bounds.js +12 -0
  274. package/src/supply-chain/build-output-bounds.js.map +1 -0
  275. package/src/supply-chain/build-output.js +2 -4
  276. package/src/supply-chain/build-output.js.map +1 -1
  277. package/src/supply-chain/ecr-scanning.d.ts +5 -3
  278. package/src/supply-chain/ecr-scanning.js +11 -7
  279. package/src/supply-chain/ecr-scanning.js.map +1 -1
  280. package/src/supply-chain/ecs-deployed-image.d.ts +5 -3
  281. package/src/supply-chain/ecs-deployed-image.js +6 -4
  282. package/src/supply-chain/ecs-deployed-image.js.map +1 -1
  283. package/src/supply-chain/generated-container-release-command.js +849 -44
  284. package/src/supply-chain/generated-container-release-command.js.map +1 -1
  285. package/src/supply-chain/generated-ecr-collector-command.d.ts +0 -1
  286. package/src/supply-chain/generated-ecr-collector-command.js +188 -91
  287. package/src/supply-chain/generated-ecr-collector-command.js.map +1 -1
  288. package/src/supply-chain/generated-files.js +4 -3
  289. package/src/supply-chain/generated-files.js.map +1 -1
  290. package/src/supply-chain/index.d.ts +1 -1
  291. package/src/supply-chain/index.js +2 -1
  292. package/src/supply-chain/index.js.map +1 -1
  293. package/src/workload-roles/index.d.ts +10 -17
  294. package/src/workload-roles/index.js +5 -18
  295. package/src/workload-roles/index.js.map +1 -1
  296. package/src/workspace-deployment/identity.d.ts +3 -0
  297. package/src/workspace-deployment/identity.js +22 -0
  298. package/src/workspace-deployment/identity.js.map +1 -0
  299. package/src/workspace-deployment/index.d.ts +3 -0
  300. package/src/workspace-deployment/index.js +7 -0
  301. package/src/workspace-deployment/index.js.map +1 -0
  302. package/src/workspace-deployment/migration-evidence.d.ts +29 -0
  303. package/src/workspace-deployment/migration-evidence.js +83 -0
  304. package/src/workspace-deployment/migration-evidence.js.map +1 -0
  305. package/src/workspace-deployment/migration.d.ts +27 -0
  306. package/src/workspace-deployment/migration.js +109 -0
  307. package/src/workspace-deployment/migration.js.map +1 -0
  308. package/src/workspace-deployment/ownership.d.ts +81 -0
  309. package/src/workspace-deployment/ownership.js +381 -0
  310. package/src/workspace-deployment/ownership.js.map +1 -0
@@ -1,14 +1,38 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createRouterWafRehearsalExecutor = createRouterWafRehearsalExecutor;
4
+ const client_cloudwatch_1 = require("@aws-sdk/client-cloudwatch");
5
+ const client_cloudwatch_logs_1 = require("@aws-sdk/client-cloudwatch-logs");
6
+ const client_cloudfront_1 = require("@aws-sdk/client-cloudfront");
7
+ const client_sts_1 = require("@aws-sdk/client-sts");
8
+ const client_wafv2_1 = require("@aws-sdk/client-wafv2");
9
+ const credential_providers_1 = require("@aws-sdk/credential-providers");
10
+ const node_crypto_1 = require("node:crypto");
3
11
  const node_fs_1 = require("node:fs");
4
12
  const node_path_1 = require("node:path");
5
13
  const devkit_1 = require("@nx/devkit");
6
14
  const index_js_1 = require("../../foundation/index.js");
15
+ const router_waf_rate_rehearsal_js_1 = require("../../foundation/router-waf-rate-rehearsal.js");
16
+ const index_js_2 = require("../../aws-authority/index.js");
17
+ const identity_js_1 = require("../../aws/identity.js");
18
+ const index_js_3 = require("../../repository-policy/index.js");
19
+ const deterministic_order_js_1 = require("../../deterministic-order.js");
20
+ const index_js_4 = require("../../workspace-deployment/index.js");
7
21
  const MAX_ROUTER_WAF_EVIDENCE_BYTES = 32 * 1024;
8
22
  const FOUNDATION_ROOT = (0, node_path_1.join)('packages', 'foundation');
9
23
  const POLICY_PATH = (0, node_path_1.join)('security', 'router-waf.json');
10
24
  const EVIDENCE_PATH = (0, node_path_1.join)('security', 'router-waf-live-evidence.json');
11
25
  const RECEIPT_PATH = (0, node_path_1.join)('security', 'router-waf-blocking-rehearsal.json');
26
+ const GLOBAL_WAF_REGION = 'us-east-1';
27
+ const REQUEST_TIMEOUT_MS = 15_000;
28
+ const RATE_MARKER_HEADER = 'x-ebk-waf-rate-rehearsal';
29
+ const OPERATOR_MARKER_HEADER = 'x-ebk-waf-rehearsal';
30
+ const RATE_MARKER_NONCE_PATTERN = /^[a-f0-9]{64}$/u;
31
+ const SLICE_HEALTH_PATH = /^\/[a-z0-9]+(?:-[a-z0-9]+)*\/api\/health$/u;
32
+ const WAF_ARN = /^arn:(aws|aws-us-gov):wafv2:us-east-1:(\d{12}):global\/webacl\/([A-Za-z0-9_-]{1,128})\/([a-f0-9-]{36})$/u;
33
+ const WAF_IP_SET_ARN = /^arn:(aws|aws-us-gov):wafv2:us-east-1:(\d{12}):global\/ipset\/([A-Za-z0-9_-]{1,128})\/([a-f0-9-]{36})$/u;
34
+ const WORKSPACE_PATTERN = /^[a-z0-9][a-z0-9-]{0,127}$/u;
35
+ const MAX_ROUTER_WAF_SEED_AGE_MS = 4 * 60 * 60 * 1_000;
12
36
  function readBoundedJson(path, label) {
13
37
  const before = (0, node_fs_1.lstatSync)(path, { bigint: true });
14
38
  if (!before.isFile() || before.nlink !== 1n) {
@@ -34,14 +58,14 @@ function readBoundedJson(path, label) {
34
58
  throw new Error(`${label} is not valid JSON.`);
35
59
  }
36
60
  }
37
- function writeReceipt(path, receipt) {
61
+ function writeBoundedJson(path, value, label) {
38
62
  const existing = (0, node_fs_1.lstatSync)(path, { bigint: true });
39
63
  if (!existing.isFile() || existing.nlink !== 1n) {
40
- throw new Error('Router WAF blocking rehearsal receipt must be one unaliased regular file.');
64
+ throw new Error(`${label} must be one unaliased regular file.`);
41
65
  }
42
- const bytes = `${JSON.stringify(receipt, null, 2)}\n`;
66
+ const bytes = `${JSON.stringify(value, null, 2)}\n`;
43
67
  if (Buffer.byteLength(bytes) > MAX_ROUTER_WAF_EVIDENCE_BYTES) {
44
- throw new Error('Router WAF blocking rehearsal receipt exceeds its bound.');
68
+ throw new Error(`${label} exceeds its bound.`);
45
69
  }
46
70
  const temporary = `${path}.tmp-${process.pid}`;
47
71
  let descriptor;
@@ -68,27 +92,888 @@ function writeReceipt(path, receipt) {
68
92
  if (cleanupError)
69
93
  throw cleanupError;
70
94
  }
71
- const executor = async (_, context) => {
95
+ function partitionForRegion(region) {
96
+ return region.startsWith('cn-')
97
+ ? 'aws-cn'
98
+ : region.startsWith('us-gov-')
99
+ ? 'aws-us-gov'
100
+ : 'aws';
101
+ }
102
+ function selectedProfile(options, environment) {
103
+ const explicit = options.profile?.trim();
104
+ const internal = environment.AWS_PROFILE?.trim();
105
+ if (explicit && internal && explicit !== internal) {
106
+ throw new Error('Router WAF rehearsal explicit profile differs from the invocation AWS_PROFILE.');
107
+ }
108
+ const profile = explicit || internal || undefined;
109
+ if (environment.GITHUB_ACTIONS === 'true') {
110
+ if (profile) {
111
+ throw new Error('Protected GitHub Router WAF rehearsal must use its configured OIDC credentials, not an AWS profile.');
112
+ }
113
+ return undefined;
114
+ }
115
+ if (!profile) {
116
+ throw new Error('Local Router WAF rehearsal requires the explicit or invocation-private AWS_PROFILE.');
117
+ }
118
+ return profile;
119
+ }
120
+ function markerDigest(value) {
121
+ return `sha256:${(0, node_crypto_1.createHash)('sha256').update(value, 'utf8').digest('hex')}`;
122
+ }
123
+ function rateMarker(nonce, sequence) {
124
+ return `${nonce}:${sequence}`;
125
+ }
126
+ function normalizedProbeUrl(value) {
127
+ let url;
72
128
  try {
73
- const root = (0, node_path_1.join)(context.root, FOUNDATION_ROOT);
74
- const policy = (0, index_js_1.normalizeRouterWafPolicy)(readBoundedJson((0, node_path_1.join)(root, POLICY_PATH), 'Router WAF policy'));
75
- const receipt = (0, index_js_1.createRouterWafBlockingRehearsal)({
76
- evidence: readBoundedJson((0, node_path_1.join)(root, EVIDENCE_PATH), 'Router WAF live evidence'),
77
- policy,
78
- });
79
- writeReceipt((0, node_path_1.join)(root, RECEIPT_PATH), receipt);
80
- devkit_1.logger.info(JSON.stringify({
81
- evidenceDigest: receipt.evidenceDigest,
82
- policyDigest: receipt.policyDigest,
83
- sealed: true,
84
- stage: receipt.stage,
85
- }));
86
- return { success: true };
129
+ url = new URL(value);
130
+ }
131
+ catch {
132
+ throw new Error('Router WAF rate probe URL must be one absolute URL.');
133
+ }
134
+ if (url.protocol !== 'https:' ||
135
+ url.username !== '' ||
136
+ url.password !== '' ||
137
+ url.hash !== '' ||
138
+ url.search !== '' ||
139
+ url.port !== '' ||
140
+ !SLICE_HEALTH_PATH.test(url.pathname)) {
141
+ throw new Error('Router WAF rate probe URL must be HTTPS on the default port, contain no credentials/query/fragment, and use /<kebab-slice>/api/health.');
142
+ }
143
+ return url;
144
+ }
145
+ function clients(profile, region, dependencies) {
146
+ const credentials = profile ? (0, credential_providers_1.fromIni)({ profile }) : undefined;
147
+ const common = credentials ? { credentials } : {};
148
+ return {
149
+ cloudFront: (dependencies.createCloudFrontClient ??
150
+ ((configuration) => new client_cloudfront_1.CloudFrontClient(configuration)))({
151
+ ...common,
152
+ region: GLOBAL_WAF_REGION,
153
+ }),
154
+ cloudWatch: (dependencies.createCloudWatchClient ??
155
+ ((configuration) => new client_cloudwatch_1.CloudWatchClient(configuration)))({
156
+ ...common,
157
+ region: GLOBAL_WAF_REGION,
158
+ }),
159
+ cloudWatchLogs: (dependencies.createCloudWatchLogsClient ??
160
+ ((configuration) => new client_cloudwatch_logs_1.CloudWatchLogsClient(configuration)))({
161
+ ...common,
162
+ region: GLOBAL_WAF_REGION,
163
+ }),
164
+ sts: (dependencies.createStsClient ??
165
+ ((configuration) => new client_sts_1.STSClient(configuration)))({
166
+ ...common,
167
+ endpoint: (0, identity_js_1.canonicalStsEndpoint)(region),
168
+ region,
169
+ }),
170
+ waf: (dependencies.createWafClient ??
171
+ ((configuration) => new client_wafv2_1.WAFV2Client(configuration)))({
172
+ ...common,
173
+ region: GLOBAL_WAF_REGION,
174
+ }),
175
+ };
176
+ }
177
+ async function attestCloudFrontDistribution(input) {
178
+ const expectedArn = `arn:${input.partition}:cloudfront::${input.accountId}:distribution/${input.distributionId}`;
179
+ const [result, tagResult] = await Promise.all([
180
+ input.cloudFront.send(new client_cloudfront_1.GetDistributionCommand({ Id: input.distributionId })),
181
+ input.cloudFront.send(new client_cloudfront_1.ListTagsForResourceCommand({ Resource: expectedArn })),
182
+ ]);
183
+ const distribution = result.Distribution;
184
+ const configuration = distribution?.DistributionConfig;
185
+ const aliases = configuration?.Aliases?.Items ?? [];
186
+ if (distribution?.ARN !== expectedArn ||
187
+ distribution?.Id !== input.distributionId ||
188
+ distribution.Status !== 'Deployed' ||
189
+ configuration?.Staging !== false ||
190
+ !distribution.DomainName ||
191
+ !configuration ||
192
+ configuration.Enabled !== true ||
193
+ configuration.WebACLId !== input.webAclArn ||
194
+ (input.probeHost !== distribution.DomainName &&
195
+ !aliases.includes(input.probeHost))) {
196
+ throw new Error('Router WAF rehearsal probe is not bound to the exact enabled deployed CloudFront distribution, Web ACL, DomainName, or Alias.');
197
+ }
198
+ const tags = new Map((tagResult.Tags?.Items ?? []).flatMap((tag) => tag.Key && tag.Value ? [[tag.Key, tag.Value]] : []));
199
+ if (tags.get('ebk:managed-by') !== 'empire-builder-kit' ||
200
+ tags.get('ebk:owner') !== 'foundation' ||
201
+ tags.get('ebk:stage') !== input.stage ||
202
+ tags.get('ebk:workspace') !== input.workspace) {
203
+ throw new Error('Live CloudFront distribution lacks the exact generated workspace, stage, owner, or managed identity tags.');
204
+ }
205
+ }
206
+ function liveRecord(value, label) {
207
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
208
+ throw new Error(`${label} is not one object.`);
209
+ }
210
+ return value;
211
+ }
212
+ function exactLiveKeys(value, expected, label) {
213
+ const observed = Object.entries(value)
214
+ .filter(([, item]) => item !== undefined)
215
+ .map(([key]) => key)
216
+ .sort();
217
+ const required = [...expected].sort();
218
+ if (JSON.stringify(observed) !== JSON.stringify(required)) {
219
+ throw new Error(`${label} has an unexpected live shape.`);
220
+ }
221
+ }
222
+ function emptyLiveObject(value, label) {
223
+ const candidate = liveRecord(value, label);
224
+ exactLiveKeys(candidate, [], label);
225
+ }
226
+ function canonicalLive(value) {
227
+ if (Array.isArray(value)) {
228
+ return `[${value.map(canonicalLive).join(',')}]`;
229
+ }
230
+ if (value && typeof value === 'object') {
231
+ return `{${Object.entries(value)
232
+ .sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0))
233
+ .map(([key, item]) => `${JSON.stringify(key)}:${canonicalLive(item)}`)
234
+ .join(',')}}`;
235
+ }
236
+ const encoded = JSON.stringify(value);
237
+ if (encoded === undefined) {
238
+ throw new Error('Router WAF live contract contains an unsupported value.');
239
+ }
240
+ return encoded;
241
+ }
242
+ function canonicalRulesForComparison(rules) {
243
+ const normalized = structuredClone(rules);
244
+ const allowlist = normalized.find(({ name }) => name === 'StageIpAllowlist');
245
+ const statement = allowlist?.statement;
246
+ const references = statement?.notStatement?.statement?.orStatement?.statements;
247
+ if (references) {
248
+ references.sort((left, right) => (0, deterministic_order_js_1.compareUtf16)(canonicalLive(left), canonicalLive(right)));
249
+ }
250
+ return canonicalLive(normalized);
251
+ }
252
+ function normalizedLiveVisibility(value, expectedMetricName, label) {
253
+ const candidate = liveRecord(value, `${label} visibility`);
254
+ exactLiveKeys(candidate, ['CloudWatchMetricsEnabled', 'MetricName', 'SampledRequestsEnabled'], `${label} visibility`);
255
+ if (candidate.CloudWatchMetricsEnabled !== true ||
256
+ candidate.SampledRequestsEnabled !== true ||
257
+ candidate.MetricName !== expectedMetricName) {
258
+ throw new Error(`${label} visibility differs from the generated plan.`);
259
+ }
260
+ return {
261
+ cloudwatchMetricsEnabled: true,
262
+ metricName: expectedMetricName,
263
+ sampledRequestsEnabled: true,
264
+ };
265
+ }
266
+ function normalizedIpSetReference(value, label) {
267
+ const candidate = liveRecord(value, label);
268
+ exactLiveKeys(candidate, ['ARN'], label);
269
+ if (typeof candidate.ARN !== 'string') {
270
+ throw new Error(`${label} ARN is invalid.`);
271
+ }
272
+ return {
273
+ arn: candidate.ARN,
274
+ normalized: { ipSetReferenceStatement: { arn: candidate.ARN } },
275
+ };
276
+ }
277
+ function normalizedLiveRules(input) {
278
+ const prefix = `Ebk${input.stage[0].toUpperCase()}${input.stage.slice(1)}`;
279
+ const expectedNames = [
280
+ 'StageIpAllowlist',
281
+ router_waf_rate_rehearsal_js_1.ROUTER_WAF_RATE_RULE_NAME,
282
+ 'AWSManagedRulesCommonRuleSet',
283
+ 'AWSManagedRulesKnownBadInputsRuleSet',
284
+ 'AWSManagedRulesAmazonIpReputationList',
285
+ ];
286
+ if (input.rules.length !== expectedNames.length ||
287
+ input.rules.some((rule, index) => liveRecord(rule, `Router WAF rule ${index}`).Name !==
288
+ expectedNames[index])) {
289
+ throw new Error('Live Router WAF ordered rule inventory differs from the generated blocking plan.');
290
+ }
291
+ const ipSetArns = [];
292
+ const rules = input.rules.map((value, index) => {
293
+ const name = expectedNames[index] ?? '';
294
+ const rule = liveRecord(value, `Router WAF rule ${name}`);
295
+ const managed = name.startsWith('AWSManagedRules');
296
+ exactLiveKeys(rule, managed
297
+ ? [
298
+ 'Name',
299
+ 'OverrideAction',
300
+ 'Priority',
301
+ 'Statement',
302
+ 'VisibilityConfig',
303
+ ]
304
+ : ['Action', 'Name', 'Priority', 'Statement', 'VisibilityConfig'], `Router WAF rule ${name}`);
305
+ if (rule.Name !== name || rule.Priority !== index) {
306
+ throw new Error(`Live Router WAF rule ${name} priority differs from the generated plan.`);
307
+ }
308
+ const visibilityConfig = normalizedLiveVisibility(rule.VisibilityConfig, `${prefix}${name}`, `Router WAF rule ${name}`);
309
+ const statement = liveRecord(rule.Statement, `Router WAF rule ${name} statement`);
310
+ if (name === 'StageIpAllowlist') {
311
+ const action = liveRecord(rule.Action, 'StageIpAllowlist action');
312
+ exactLiveKeys(action, ['Block'], 'StageIpAllowlist action');
313
+ emptyLiveObject(action.Block, 'StageIpAllowlist Block action');
314
+ exactLiveKeys(statement, ['NotStatement'], 'StageIpAllowlist statement');
315
+ const notStatement = liveRecord(statement.NotStatement, 'StageIpAllowlist not statement');
316
+ exactLiveKeys(notStatement, ['Statement'], 'StageIpAllowlist not statement');
317
+ const nested = liveRecord(notStatement.Statement, 'StageIpAllowlist nested statement');
318
+ let normalizedNested;
319
+ if ('IPSetReferenceStatement' in nested) {
320
+ exactLiveKeys(nested, ['IPSetReferenceStatement'], 'StageIpAllowlist nested statement');
321
+ const reference = normalizedIpSetReference(nested.IPSetReferenceStatement, 'StageIpAllowlist IP set reference');
322
+ ipSetArns.push(reference.arn);
323
+ normalizedNested = reference.normalized;
324
+ }
325
+ else {
326
+ exactLiveKeys(nested, ['OrStatement'], 'StageIpAllowlist nested statement');
327
+ const orStatement = liveRecord(nested.OrStatement, 'StageIpAllowlist OR statement');
328
+ exactLiveKeys(orStatement, ['Statements'], 'StageIpAllowlist OR statement');
329
+ if (!Array.isArray(orStatement.Statements) ||
330
+ orStatement.Statements.length !== 2) {
331
+ throw new Error('StageIpAllowlist OR statement must contain the exact generated IPv4 and IPv6 references.');
332
+ }
333
+ const references = orStatement.Statements.map((item, referenceIndex) => {
334
+ const wrapper = liveRecord(item, `StageIpAllowlist reference ${referenceIndex}`);
335
+ exactLiveKeys(wrapper, ['IPSetReferenceStatement'], `StageIpAllowlist reference ${referenceIndex}`);
336
+ const reference = normalizedIpSetReference(wrapper.IPSetReferenceStatement, `StageIpAllowlist reference ${referenceIndex}`);
337
+ ipSetArns.push(reference.arn);
338
+ return reference.normalized;
339
+ });
340
+ normalizedNested = { orStatement: { statements: references } };
341
+ }
342
+ return {
343
+ action: { block: {} },
344
+ name,
345
+ priority: index,
346
+ statement: {
347
+ notStatement: { statement: normalizedNested },
348
+ },
349
+ visibilityConfig,
350
+ };
351
+ }
352
+ if (name === router_waf_rate_rehearsal_js_1.ROUTER_WAF_RATE_RULE_NAME) {
353
+ const action = liveRecord(rule.Action, 'RateLimitPerIP action');
354
+ exactLiveKeys(action, ['Block'], 'RateLimitPerIP action');
355
+ emptyLiveObject(action.Block, 'RateLimitPerIP Block action');
356
+ exactLiveKeys(statement, ['RateBasedStatement'], 'RateLimitPerIP statement');
357
+ const rate = liveRecord(statement.RateBasedStatement, 'RateLimitPerIP rate statement');
358
+ const rateKeys = Object.entries(rate)
359
+ .filter(([, item]) => item !== undefined)
360
+ .map(([key]) => key)
361
+ .sort();
362
+ if (JSON.stringify(rateKeys) !==
363
+ JSON.stringify([
364
+ 'AggregateKeyType',
365
+ ...(rate.EvaluationWindowSec === undefined
366
+ ? []
367
+ : ['EvaluationWindowSec']),
368
+ 'Limit',
369
+ ].sort()) ||
370
+ rate.AggregateKeyType !== 'IP' ||
371
+ typeof rate.Limit !== 'number' ||
372
+ (rate.EvaluationWindowSec !== undefined &&
373
+ rate.EvaluationWindowSec !== 300)) {
374
+ throw new Error('Live RateLimitPerIP contains an unexpected scope-down, forwarded-IP, custom-key, evaluation-window, or limit shape.');
375
+ }
376
+ return {
377
+ action: { block: {} },
378
+ name,
379
+ priority: index,
380
+ statement: {
381
+ rateBasedStatement: {
382
+ aggregateKeyType: 'IP',
383
+ limit: rate.Limit,
384
+ },
385
+ },
386
+ visibilityConfig,
387
+ };
388
+ }
389
+ const overrideAction = liveRecord(rule.OverrideAction, `Router WAF rule ${name} override action`);
390
+ exactLiveKeys(overrideAction, ['None'], `Router WAF rule ${name} override action`);
391
+ emptyLiveObject(overrideAction.None, `Router WAF rule ${name} None action`);
392
+ exactLiveKeys(statement, ['ManagedRuleGroupStatement'], `Router WAF rule ${name} statement`);
393
+ const managedStatement = liveRecord(statement.ManagedRuleGroupStatement, `Router WAF rule ${name} managed statement`);
394
+ const common = name === 'AWSManagedRulesCommonRuleSet';
395
+ exactLiveKeys(managedStatement, common
396
+ ? ['Name', 'RuleActionOverrides', 'VendorName']
397
+ : ['Name', 'VendorName'], `Router WAF rule ${name} managed statement`);
398
+ if (managedStatement.Name !== name ||
399
+ managedStatement.VendorName !== 'AWS') {
400
+ throw new Error(`Live Router WAF rule ${name} managed group differs from the generated plan.`);
401
+ }
402
+ let ruleActionOverrides;
403
+ if (common) {
404
+ if (!Array.isArray(managedStatement.RuleActionOverrides) ||
405
+ managedStatement.RuleActionOverrides.length !== 1) {
406
+ throw new Error('AWSManagedRulesCommonRuleSet must retain the exact SizeRestrictions_BODY Count override.');
407
+ }
408
+ const override = liveRecord(managedStatement.RuleActionOverrides[0], 'SizeRestrictions_BODY override');
409
+ exactLiveKeys(override, ['ActionToUse', 'Name'], 'SizeRestrictions_BODY override');
410
+ const actionToUse = liveRecord(override.ActionToUse, 'SizeRestrictions_BODY action');
411
+ exactLiveKeys(actionToUse, ['Count'], 'SizeRestrictions_BODY action');
412
+ emptyLiveObject(actionToUse.Count, 'SizeRestrictions_BODY Count action');
413
+ if (override.Name !== 'SizeRestrictions_BODY') {
414
+ throw new Error('AWSManagedRulesCommonRuleSet override targets an unexpected rule.');
415
+ }
416
+ ruleActionOverrides = [
417
+ {
418
+ actionToUse: { count: {} },
419
+ name: 'SizeRestrictions_BODY',
420
+ },
421
+ ];
422
+ }
423
+ return {
424
+ name,
425
+ overrideAction: { none: {} },
426
+ priority: index,
427
+ statement: {
428
+ managedRuleGroupStatement: {
429
+ name,
430
+ ...(common ? { ruleActionOverrides } : {}),
431
+ vendorName: 'AWS',
432
+ },
433
+ },
434
+ visibilityConfig,
435
+ };
436
+ });
437
+ return { ipSetArns, rules };
438
+ }
439
+ function requiredIdentityTags(input) {
440
+ return {
441
+ 'ebk:managed-by': 'empire-builder-kit',
442
+ 'ebk:owner': 'foundation',
443
+ 'ebk:policy-digest': input.policyDigest,
444
+ 'ebk:stage': input.stage,
445
+ 'ebk:workspace': input.workspace,
446
+ };
447
+ }
448
+ function attestWafTags(tagsResult, resourceArn, expected, label) {
449
+ const result = liveRecord(tagsResult, `${label} tag response`);
450
+ const tagInfo = liveRecord(result.TagInfoForResource, `${label} tag information`);
451
+ if (tagInfo.ResourceARN !== resourceArn || !Array.isArray(tagInfo.TagList)) {
452
+ throw new Error(`${label} tag response does not identify the exact ARN.`);
453
+ }
454
+ const tags = new Map(tagInfo.TagList.flatMap((value) => {
455
+ const tag = liveRecord(value, `${label} tag`);
456
+ return typeof tag.Key === 'string' && typeof tag.Value === 'string'
457
+ ? [[tag.Key, tag.Value]]
458
+ : [];
459
+ }));
460
+ if (Object.entries(expected).some(([key, value]) => tags.get(key) !== value)) {
461
+ throw new Error(`${label} lacks the exact generated identity tags.`);
462
+ }
463
+ }
464
+ async function attestIpSet(input) {
465
+ const arn = WAF_IP_SET_ARN.exec(input.arn);
466
+ if (!arn || arn[2] !== input.accountId) {
467
+ throw new Error('Router WAF IP set ARN has the wrong account or scope.');
468
+ }
469
+ const name = arn[3] ?? '';
470
+ const id = arn[4] ?? '';
471
+ const [ipSetResult, tagsResult] = await Promise.all([
472
+ input.waf.send(new client_wafv2_1.GetIPSetCommand({ Id: id, Name: name, Scope: 'CLOUDFRONT' })),
473
+ input.waf.send(new client_wafv2_1.ListTagsForResourceCommand({ ResourceARN: input.arn })),
474
+ ]);
475
+ const ipSet = ipSetResult.IPSet;
476
+ const expectedCidrs = ipSet?.IPAddressVersion === 'IPV4'
477
+ ? input.expectedIpv4Cidrs
478
+ : input.expectedIpv6Cidrs;
479
+ if (ipSet?.ARN !== input.arn ||
480
+ ipSet.Id !== id ||
481
+ ipSet.Name !== name ||
482
+ (ipSet.IPAddressVersion !== 'IPV4' && ipSet.IPAddressVersion !== 'IPV6') ||
483
+ JSON.stringify([...(ipSet.Addresses ?? [])].sort()) !==
484
+ JSON.stringify([...expectedCidrs].sort())) {
485
+ throw new Error('Live Router WAF IP set identity, address family, or exact CIDRs differ from the generated plan.');
486
+ }
487
+ attestWafTags(tagsResult, input.arn, input.expectedTags, 'Router WAF IP set');
488
+ return ipSet.IPAddressVersion;
489
+ }
490
+ async function attestLoggingConfiguration(input) {
491
+ let configuration;
492
+ try {
493
+ const result = await input.waf.send(new client_wafv2_1.GetLoggingConfigurationCommand({ ResourceArn: input.webAclArn }));
494
+ configuration = result.LoggingConfiguration;
87
495
  }
88
496
  catch (error) {
89
- devkit_1.logger.error(error instanceof Error ? error.message : String(error));
90
- return { success: false };
497
+ if (input.plan.logging === false &&
498
+ error instanceof Error &&
499
+ error.name === 'WAFNonexistentItemException') {
500
+ return;
501
+ }
502
+ throw error;
503
+ }
504
+ if (input.plan.logging === false) {
505
+ if (configuration !== undefined) {
506
+ throw new Error('Router WAF logging exists even though committed policy disables it.');
507
+ }
508
+ return;
91
509
  }
92
- };
510
+ const live = liveRecord(configuration, 'Router WAF logging configuration');
511
+ if (live.ResourceArn !== input.webAclArn ||
512
+ !Array.isArray(live.LogDestinationConfigs) ||
513
+ live.LogDestinationConfigs.length !== 1 ||
514
+ typeof live.LogDestinationConfigs[0] !== 'string' ||
515
+ !new RegExp(`^arn:(?:aws|aws-us-gov):logs:us-east-1:${input.accountId}:log-group:(aws-waf-logs-foundation-${input.plan.stage}-FoundationRouter-[A-Za-z0-9]{1,32})$`).test(live.LogDestinationConfigs[0]) ||
516
+ live.ManagedByFirewallManager !== false ||
517
+ live.LogScope !== 'CUSTOMER' ||
518
+ live.LogType !== 'WAF_LOGS') {
519
+ throw new Error('Router WAF logging destination differs from the generated blocking log contract.');
520
+ }
521
+ const destinationArn = live.LogDestinationConfigs[0];
522
+ const logGroupName = destinationArn.slice(destinationArn.indexOf(':log-group:') + 11);
523
+ const logGroups = await input.cloudWatchLogs.send(new client_cloudwatch_logs_1.DescribeLogGroupsCommand({
524
+ includeLinkedAccounts: false,
525
+ logGroupNamePrefix: logGroupName,
526
+ }));
527
+ const exactLogGroups = (logGroups.logGroups ?? []).filter(({ logGroupName: candidate }) => candidate === logGroupName);
528
+ if (exactLogGroups.length !== 1 ||
529
+ exactLogGroups[0]?.logGroupArn !== destinationArn ||
530
+ exactLogGroups[0]?.arn !== `${destinationArn}:*` ||
531
+ exactLogGroups[0]?.logGroupClass !== 'STANDARD' ||
532
+ exactLogGroups[0]?.retentionInDays !==
533
+ ({ '1 day': 1, '3 days': 3, '5 days': 5, '1 week': 7, '2 weeks': 14 }[input.plan.logging.retention] ?? -1)) {
534
+ throw new Error('Router WAF CloudWatch Logs destination identity, class, or retention differs from committed policy.');
535
+ }
536
+ const expectedRedactions = [
537
+ { queryString: {} },
538
+ { singleHeader: { name: 'authorization' } },
539
+ { singleHeader: { name: 'cookie' } },
540
+ { singleHeader: { name: 'x-api-key' } },
541
+ ];
542
+ const redactions = Array.isArray(live.RedactedFields)
543
+ ? live.RedactedFields.map((value) => {
544
+ const field = liveRecord(value, 'Router WAF redacted field');
545
+ if ('QueryString' in field) {
546
+ exactLiveKeys(field, ['QueryString'], 'Router WAF query redaction');
547
+ emptyLiveObject(field.QueryString, 'Router WAF query redaction');
548
+ return { queryString: {} };
549
+ }
550
+ exactLiveKeys(field, ['SingleHeader'], 'Router WAF header redaction');
551
+ const header = liveRecord(field.SingleHeader, 'Router WAF header redaction');
552
+ exactLiveKeys(header, ['Name'], 'Router WAF header redaction');
553
+ return { singleHeader: { name: header.Name } };
554
+ })
555
+ : [];
556
+ const filter = liveRecord(live.LoggingFilter, 'Router WAF logging filter');
557
+ exactLiveKeys(filter, ['DefaultBehavior', 'Filters'], 'Router WAF logging filter');
558
+ if (canonicalLive([...redactions].sort((left, right) => (0, deterministic_order_js_1.compareUtf16)(canonicalLive(left), canonicalLive(right)))) !==
559
+ canonicalLive([...expectedRedactions].sort((left, right) => (0, deterministic_order_js_1.compareUtf16)(canonicalLive(left), canonicalLive(right)))) ||
560
+ filter.DefaultBehavior !== 'DROP' ||
561
+ !Array.isArray(filter.Filters) ||
562
+ filter.Filters.length !== 1) {
563
+ throw new Error('Router WAF logging redaction or blocked-only filter differs from committed policy.');
564
+ }
565
+ const retainedFilter = liveRecord(filter.Filters[0], 'Router WAF log filter');
566
+ exactLiveKeys(retainedFilter, ['Behavior', 'Conditions', 'Requirement'], 'Router WAF log filter');
567
+ if (retainedFilter.Behavior !== 'KEEP' ||
568
+ retainedFilter.Requirement !== 'MEETS_ANY' ||
569
+ !Array.isArray(retainedFilter.Conditions) ||
570
+ retainedFilter.Conditions.length !== 1 ||
571
+ canonicalLive(retainedFilter.Conditions) !==
572
+ canonicalLive([{ ActionCondition: { Action: 'BLOCK' } }])) {
573
+ throw new Error('Router WAF logging filter does not retain only blocked requests.');
574
+ }
575
+ }
576
+ async function attestRouterWafBaseline(input) {
577
+ const arn = WAF_ARN.exec(input.webAclArn);
578
+ if (!arn || arn[2] !== input.accountId) {
579
+ throw new Error('Router WAF rehearsal Web ACL ARN does not match the committed account and global scope.');
580
+ }
581
+ const name = arn[3] ?? '';
582
+ const id = arn[4] ?? '';
583
+ const [webAclResult, tagsResult] = await Promise.all([
584
+ input.waf.send(new client_wafv2_1.GetWebACLCommand({ Id: id, Name: name, Scope: 'CLOUDFRONT' })),
585
+ input.waf.send(new client_wafv2_1.ListTagsForResourceCommand({ ResourceARN: input.webAclArn })),
586
+ ]);
587
+ const webAcl = webAclResult.WebACL;
588
+ if (webAcl?.ARN !== input.webAclArn ||
589
+ webAcl.Id !== id ||
590
+ webAcl.Name !== name ||
591
+ !webAcl.DefaultAction?.Allow ||
592
+ Object.keys(webAcl.DefaultAction).some((key) => key !== 'Allow') ||
593
+ webAcl.VisibilityConfig?.CloudWatchMetricsEnabled !== true ||
594
+ webAcl.VisibilityConfig.SampledRequestsEnabled !== true ||
595
+ webAcl.VisibilityConfig.MetricName !== input.webAclMetricName) {
596
+ throw new Error('Live Router WAF identity, default action, or Web ACL visibility differs from the generated plan.');
597
+ }
598
+ emptyLiveObject(webAcl.DefaultAction.Allow, 'Router WAF Web ACL default Allow action');
599
+ const expectedTags = requiredIdentityTags(input);
600
+ attestWafTags(tagsResult, input.webAclArn, expectedTags, 'Router WAF Web ACL');
601
+ const normalized = normalizedLiveRules({
602
+ rules: webAcl.Rules ?? [],
603
+ stage: input.stage,
604
+ });
605
+ const expectedIpSetCount = Number(input.plan.ipv4Cidrs.length > 0) +
606
+ Number(input.plan.ipv6Cidrs.length > 0);
607
+ if (normalized.ipSetArns.length !== expectedIpSetCount) {
608
+ throw new Error('Live StageIpAllowlist has an unexpected number of generated IP set references.');
609
+ }
610
+ if (new Set(normalized.ipSetArns).size !== normalized.ipSetArns.length) {
611
+ throw new Error('Live StageIpAllowlist reuses an IP set instead of the exact generated address-family inventory.');
612
+ }
613
+ const ipSetResults = await Promise.all(normalized.ipSetArns.map(async (ipSetArn) => {
614
+ const version = await attestIpSet({
615
+ accountId: input.accountId,
616
+ arn: ipSetArn,
617
+ expectedIpv4Cidrs: input.plan.ipv4Cidrs,
618
+ expectedIpv6Cidrs: input.plan.ipv6Cidrs,
619
+ expectedTags,
620
+ waf: input.waf,
621
+ });
622
+ return { arn: ipSetArn, version };
623
+ }));
624
+ const ipv4Arn = ipSetResults.find(({ version }) => version === 'IPV4')?.arn;
625
+ const ipv6Arn = ipSetResults.find(({ version }) => version === 'IPV6')?.arn;
626
+ if (Boolean(ipv4Arn) !== Boolean(input.plan.ipv4Cidrs.length) ||
627
+ Boolean(ipv6Arn) !== Boolean(input.plan.ipv6Cidrs.length)) {
628
+ throw new Error('Live Router WAF IP set address families differ from the generated plan.');
629
+ }
630
+ const expectedRules = (0, index_js_1.createRouterWafRules)(input.plan, {
631
+ ...(ipv4Arn ? { ipv4Arn } : {}),
632
+ ...(ipv6Arn ? { ipv6Arn } : {}),
633
+ });
634
+ if (canonicalRulesForComparison(normalized.rules) !==
635
+ canonicalRulesForComparison(expectedRules)) {
636
+ throw new Error('Live Router WAF ordered rules differ from the exact generated blocking plan.');
637
+ }
638
+ await attestLoggingConfiguration({
639
+ accountId: input.accountId,
640
+ cloudWatchLogs: input.cloudWatchLogs,
641
+ plan: input.plan,
642
+ waf: input.waf,
643
+ webAclArn: input.webAclArn,
644
+ });
645
+ return name;
646
+ }
647
+ function matchesManagedSampledRule(input) {
648
+ if (!input.group || !input.subrule)
649
+ return false;
650
+ return (input.observed === input.subrule ||
651
+ input.observed === `AWS#${input.group}#${input.subrule}`);
652
+ }
653
+ async function attestOperatorBlock(input) {
654
+ const [managedGroup, managedSubrule] = input.evidence.ruleName.split('#');
655
+ const metricName = input.type === 'nonallowlisted'
656
+ ? `Ebk${input.stage[0].toUpperCase()}${input.stage.slice(1)}StageIpAllowlist`
657
+ : `Ebk${input.stage[0].toUpperCase()}${input.stage.slice(1)}${managedGroup}`;
658
+ const blockedAt = new Date(input.evidence.blockedAt);
659
+ const sampled = await input.waf.send(new client_wafv2_1.GetSampledRequestsCommand({
660
+ MaxItems: 500,
661
+ RuleMetricName: metricName,
662
+ Scope: 'CLOUDFRONT',
663
+ TimeWindow: {
664
+ EndTime: new Date(blockedAt.getTime() + 60_000),
665
+ StartTime: new Date(blockedAt.getTime() - 60_000),
666
+ },
667
+ WebAclArn: input.webAclArn,
668
+ }));
669
+ const matches = (sampled.SampledRequests ?? []).filter((sample) => {
670
+ const headers = sample.Request?.Headers ?? [];
671
+ const marker = headers.find(({ Name }) => Name?.toLowerCase() === OPERATOR_MARKER_HEADER)?.Value;
672
+ const timestamp = sample.Timestamp?.getTime();
673
+ return (sample.Action === 'BLOCK' &&
674
+ sample.Request?.Method === input.evidence.method &&
675
+ sample.Request.URI === input.evidence.path &&
676
+ marker === input.evidence.marker &&
677
+ timestamp !== undefined &&
678
+ Math.abs(timestamp - blockedAt.getTime()) <= 60_000 &&
679
+ (input.type === 'nonallowlisted'
680
+ ? sample.RuleNameWithinRuleGroup === undefined
681
+ : matchesManagedSampledRule({
682
+ group: managedGroup,
683
+ observed: sample.RuleNameWithinRuleGroup,
684
+ subrule: managedSubrule,
685
+ })));
686
+ });
687
+ if (matches.length !== 1) {
688
+ throw new Error(`Router WAF ${input.type} operator evidence does not correlate to one exact sampled BLOCK request.`);
689
+ }
690
+ }
691
+ async function observeRateLimit(input) {
692
+ const [sampled, metric] = await Promise.all([
693
+ input.waf.send(new client_wafv2_1.GetSampledRequestsCommand({
694
+ MaxItems: 500,
695
+ RuleMetricName: input.ruleMetricName,
696
+ Scope: 'CLOUDFRONT',
697
+ TimeWindow: {
698
+ EndTime: input.endTime,
699
+ StartTime: input.startTime,
700
+ },
701
+ WebAclArn: input.webAclArn,
702
+ })),
703
+ input.cloudWatch.send(new client_cloudwatch_1.GetMetricDataCommand({
704
+ EndTime: input.endTime,
705
+ MetricDataQueries: [
706
+ {
707
+ Id: 'rateblocked',
708
+ MetricStat: {
709
+ Metric: {
710
+ Dimensions: [
711
+ { Name: 'Rule', Value: input.ruleMetricName },
712
+ { Name: 'WebACL', Value: input.webAclMetricName },
713
+ ],
714
+ MetricName: 'BlockedRequests',
715
+ Namespace: 'AWS/WAFV2',
716
+ },
717
+ Period: 60,
718
+ Stat: 'Sum',
719
+ },
720
+ ReturnData: true,
721
+ },
722
+ ],
723
+ ScanBy: 'TimestampDescending',
724
+ StartTime: input.startTime,
725
+ })),
726
+ ]);
727
+ const result = metric.MetricDataResults?.find((candidate) => candidate.Id === 'rateblocked');
728
+ const metrics = (result?.Timestamps ?? []).flatMap((timestamp, index) => {
729
+ const value = result?.Values?.[index];
730
+ return timestamp && typeof value === 'number'
731
+ ? [
732
+ {
733
+ name: 'BlockedRequests',
734
+ namespace: 'AWS/WAFV2',
735
+ ruleMetricName: input.ruleMetricName,
736
+ timestamp,
737
+ value,
738
+ webAclMetricName: input.webAclMetricName,
739
+ },
740
+ ]
741
+ : [];
742
+ });
743
+ const samples = (sampled.SampledRequests ?? []).flatMap((sample) => {
744
+ const markerHeader = sample.Request?.Headers?.find((header) => header.Name?.toLowerCase() === RATE_MARKER_HEADER &&
745
+ header.Value === input.marker);
746
+ return sample.Timestamp &&
747
+ typeof sample.Action === 'string' &&
748
+ sample.Request?.Method === 'GET' &&
749
+ sample.Request.URI === input.probePath &&
750
+ markerHeader
751
+ ? [
752
+ {
753
+ action: sample.Action,
754
+ markerDigest: input.markerDigest,
755
+ method: 'GET',
756
+ ruleMetricName: input.ruleMetricName,
757
+ ruleName: router_waf_rate_rehearsal_js_1.ROUTER_WAF_RATE_RULE_NAME,
758
+ timestamp: sample.Timestamp,
759
+ uri: input.probePath,
760
+ },
761
+ ]
762
+ : [];
763
+ });
764
+ return { metrics, samples };
765
+ }
766
+ function mergeRateEvidence(evidence, allowedRequestStatus, rateLimit, completedAt) {
767
+ const candidate = evidence;
768
+ const probes = candidate.probes;
769
+ return {
770
+ ...candidate,
771
+ completedAt,
772
+ probes: { ...probes, allowedRequestStatus, rateLimit },
773
+ };
774
+ }
775
+ function createRouterWafRehearsalExecutor(dependencies = {}) {
776
+ return async (options, context) => {
777
+ try {
778
+ const environment = dependencies.environment ?? process.env;
779
+ const root = (0, node_path_1.join)(context.root, FOUNDATION_ROOT);
780
+ const policy = (0, index_js_1.normalizeRouterWafPolicy)(readBoundedJson((0, node_path_1.join)(root, POLICY_PATH), 'Router WAF policy'));
781
+ const workspaceManifest = liveRecord(readBoundedJson((0, node_path_1.join)(context.root, 'package.json'), 'Workspace manifest'), 'Workspace manifest');
782
+ const workspace = workspaceManifest.name;
783
+ if (typeof workspace !== 'string' || !WORKSPACE_PATTERN.test(workspace)) {
784
+ throw new Error('Router WAF rehearsal workspace must match the exact generated root package name.');
785
+ }
786
+ const operatingPolicy = (0, index_js_3.readRepositoryOperatingPolicy)(context.root);
787
+ if (operatingPolicy.deploymentProfile !== policy.deploymentProfile) {
788
+ throw new Error('Router WAF policy deployment profile differs from the committed repository policy.');
789
+ }
790
+ const stage = (0, index_js_3.productionRehearsalStage)(operatingPolicy.deploymentProfile);
791
+ const accountId = (0, index_js_3.configuredDeploymentAccountForStage)(operatingPolicy.deploymentProfile, operatingPolicy.deploymentAccounts, stage);
792
+ const region = (0, index_js_3.configuredDeploymentRegion)(operatingPolicy.deploymentRegion);
793
+ for (const candidate of [
794
+ environment.AWS_REGION?.trim(),
795
+ environment.AWS_DEFAULT_REGION?.trim(),
796
+ ]) {
797
+ if (candidate && candidate !== region) {
798
+ throw new Error(`Router WAF rehearsal ambient Region ${candidate} differs from committed deployment Region ${region}.`);
799
+ }
800
+ }
801
+ const partition = partitionForRegion(region);
802
+ const expectedRoleArn = (0, index_js_2.createStageAuthorityIdentity)({
803
+ accountId,
804
+ partition,
805
+ region,
806
+ stage,
807
+ }).deploymentRoleArn;
808
+ const wafContractDigest = (0, index_js_1.routerWafContractDigest)(policy);
809
+ const plan = (0, index_js_1.createRouterWafStagePlan)({
810
+ policy,
811
+ rehearsal: null,
812
+ stage,
813
+ });
814
+ const evidencePath = (0, node_path_1.join)(root, EVIDENCE_PATH);
815
+ const retainedEvidence = readBoundedJson(evidencePath, 'Router WAF live evidence');
816
+ const seed = (0, index_js_1.validateRouterWafLiveEvidenceSeed)({
817
+ evidence: retainedEvidence,
818
+ policy,
819
+ });
820
+ if (seed.stage !== stage ||
821
+ seed.accountId !== accountId ||
822
+ seed.region !== region ||
823
+ seed.deploymentRoleArn !== expectedRoleArn ||
824
+ seed.workspace !== workspace ||
825
+ seed.wafContractDigest !== wafContractDigest) {
826
+ throw new Error('Router WAF live evidence stage, account, Region, deployment role, workspace, or WAF contract differs from committed authority.');
827
+ }
828
+ const invocationStartedAt = (dependencies.now ?? (() => new Date()))();
829
+ if (!Number.isFinite(invocationStartedAt.getTime()) ||
830
+ seed.startedAt > invocationStartedAt.getTime() ||
831
+ seed.completedAt > invocationStartedAt.getTime() ||
832
+ invocationStartedAt.getTime() - seed.completedAt >
833
+ MAX_ROUTER_WAF_SEED_AGE_MS) {
834
+ throw new Error('Router WAF live evidence seed is stale or future-dated; no live request was made.');
835
+ }
836
+ const receiptPath = (0, node_path_1.join)(root, RECEIPT_PATH);
837
+ const retainedReceipt = (0, index_js_1.normalizeRouterWafBlockingRehearsal)(readBoundedJson(receiptPath, 'Router WAF blocking rehearsal receipt'));
838
+ if (retainedReceipt) {
839
+ throw new Error('Router WAF blocking rehearsal receipt must be reset to its exact pending seed before another live probe.');
840
+ }
841
+ const url = normalizedProbeUrl(options.probeUrl);
842
+ const profile = selectedProfile(options, environment);
843
+ const aws = clients(profile, region, dependencies);
844
+ const identity = await aws.sts.send(new client_sts_1.GetCallerIdentityCommand({}));
845
+ if (identity.Account !== accountId ||
846
+ typeof identity.Arn !== 'string' ||
847
+ !(0, identity_js_1.matchesExpectedAssumedRoleArn)(accountId, identity.Arn, expectedRoleArn)) {
848
+ throw new Error('Router WAF rehearsal caller does not match the committed account and exact stage deployment role.');
849
+ }
850
+ await (dependencies.ownership ?? index_js_4.assertWorkspaceStageOwnershipForWorkspace)(context.root, stage, profile ? { profile } : {});
851
+ const webAclVisibilityMetricName = (0, router_waf_rate_rehearsal_js_1.routerWafWebAclMetricName)(stage);
852
+ const [, webAclMetricName] = await Promise.all([
853
+ attestCloudFrontDistribution({
854
+ accountId,
855
+ cloudFront: aws.cloudFront,
856
+ distributionId: seed.distributionId,
857
+ partition,
858
+ probeHost: url.hostname,
859
+ stage,
860
+ webAclArn: seed.webAclArn,
861
+ workspace,
862
+ }),
863
+ attestRouterWafBaseline({
864
+ accountId,
865
+ cloudWatchLogs: aws.cloudWatchLogs,
866
+ plan,
867
+ policyDigest: (0, index_js_1.routerWafPolicyDigest)(policy),
868
+ stage,
869
+ waf: aws.waf,
870
+ webAclArn: seed.webAclArn,
871
+ webAclMetricName: webAclVisibilityMetricName,
872
+ workspace,
873
+ }),
874
+ ]);
875
+ await Promise.all([
876
+ attestOperatorBlock({
877
+ evidence: seed.probes.managedRule,
878
+ stage,
879
+ type: 'managed',
880
+ waf: aws.waf,
881
+ webAclArn: seed.webAclArn,
882
+ }),
883
+ attestOperatorBlock({
884
+ evidence: seed.probes
885
+ .nonAllowlisted,
886
+ stage,
887
+ type: 'nonallowlisted',
888
+ waf: aws.waf,
889
+ webAclArn: seed.webAclArn,
890
+ }),
891
+ ]);
892
+ const fetchRequest = dependencies.fetch ?? fetch;
893
+ const baselineResponse = await fetchRequest(url, {
894
+ cache: 'no-store',
895
+ headers: { 'cache-control': 'no-cache' },
896
+ redirect: 'manual',
897
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
898
+ });
899
+ const allowedRequestStatus = baselineResponse.status;
900
+ await baselineResponse.body?.cancel();
901
+ if (allowedRequestStatus < 200 || allowedRequestStatus > 399) {
902
+ throw new Error(`Router WAF rehearsal health preflight returned unexpected HTTP ${allowedRequestStatus}; rate traffic was not sent.`);
903
+ }
904
+ const nonce = (dependencies.createNonce ?? (() => (0, node_crypto_1.randomBytes)(32).toString('hex')))();
905
+ if (!RATE_MARKER_NONCE_PATTERN.test(nonce)) {
906
+ throw new Error('Router WAF rehearsal invocation nonce is not one opaque 256-bit lowercase hexadecimal value.');
907
+ }
908
+ const rateLimit = await (dependencies.runRateRehearsal ?? router_waf_rate_rehearsal_js_1.runRouterWafRateRehearsal)({
909
+ dependencies: {
910
+ ...(dependencies.now ? { now: dependencies.now } : {}),
911
+ observe: ({ endTime, markerDigest: expectedMarkerDigest, ruleMetricName, sequence, startTime, webAclMetricName: expectedWebAclMetricName, }) => {
912
+ const marker = rateMarker(nonce, sequence);
913
+ if (expectedWebAclMetricName !== webAclMetricName) {
914
+ throw new Error('Router WAF rate observation metric dimension differs from the attested physical Web ACL name.');
915
+ }
916
+ if (markerDigest(marker) !== expectedMarkerDigest) {
917
+ throw new Error('Router WAF rate observation marker differs from the exact blocked request.');
918
+ }
919
+ return observeRateLimit({
920
+ cloudWatch: aws.cloudWatch,
921
+ endTime,
922
+ marker,
923
+ markerDigest: expectedMarkerDigest,
924
+ probePath: url.pathname,
925
+ ruleMetricName,
926
+ startTime,
927
+ waf: aws.waf,
928
+ webAclArn: seed.webAclArn,
929
+ webAclMetricName: expectedWebAclMetricName,
930
+ });
931
+ },
932
+ request: async (sequence) => {
933
+ const marker = rateMarker(nonce, sequence);
934
+ const response = await fetchRequest(url, {
935
+ cache: 'no-store',
936
+ headers: {
937
+ 'cache-control': 'no-cache',
938
+ [RATE_MARKER_HEADER]: marker,
939
+ },
940
+ redirect: 'manual',
941
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
942
+ });
943
+ await response.body?.cancel();
944
+ return {
945
+ markerDigest: markerDigest(marker),
946
+ status: response.status,
947
+ };
948
+ },
949
+ ...(dependencies.sleep ? { sleep: dependencies.sleep } : {}),
950
+ },
951
+ rateLimitPerIp: policy.rateLimitPerIp,
952
+ stage,
953
+ webAclMetricName,
954
+ });
955
+ const completedAt = (dependencies.now ?? (() => new Date()))().toISOString();
956
+ const evidence = mergeRateEvidence(retainedEvidence, allowedRequestStatus, rateLimit, completedAt);
957
+ const receipt = (0, index_js_1.createRouterWafBlockingRehearsal)({ evidence, policy });
958
+ writeBoundedJson(evidencePath, evidence, 'Router WAF live evidence');
959
+ writeBoundedJson(receiptPath, receipt, 'Router WAF blocking rehearsal receipt');
960
+ devkit_1.logger.info(JSON.stringify({
961
+ evidenceDigest: receipt.evidenceDigest,
962
+ accountId: receipt.accountId,
963
+ policyDigest: receipt.policyDigest,
964
+ rateRequests: rateLimit.requestsSent,
965
+ region: receipt.region,
966
+ sealed: true,
967
+ stage: receipt.stage,
968
+ }));
969
+ return { success: true };
970
+ }
971
+ catch (error) {
972
+ devkit_1.logger.error(error instanceof Error ? error.message : String(error));
973
+ return { success: false };
974
+ }
975
+ };
976
+ }
977
+ const executor = createRouterWafRehearsalExecutor();
93
978
  exports.default = executor;
94
979
  //# sourceMappingURL=router-waf-rehearsal.js.map