@empire-builder-kit/nx 1.0.0-rc.4 → 1.0.0-rc.41
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.
- package/README.md +44 -10
- package/executors.json +21 -1
- package/generators.json +10 -0
- package/package.json +31 -7
- package/src/aws/child-environment.d.ts +10 -0
- package/src/aws/child-environment.js +77 -0
- package/src/aws/identity.d.ts +13 -0
- package/src/aws/identity.js +41 -0
- package/src/aws-authority/index.d.ts +2 -0
- package/src/aws-authority/index.js +6 -0
- package/src/aws-authority/lambda-policy-ordering.d.ts +38 -0
- package/src/aws-authority/lambda-policy-ordering.js +56 -0
- package/src/aws-authority/stage-authority.d.ts +248 -0
- package/src/aws-authority/stage-authority.js +3558 -0
- package/src/blueprints/external-sandbox.d.ts +1 -1
- package/src/blueprints/external-sandbox.js +21 -9
- package/src/blueprints/manifest.d.ts +1 -1
- package/src/blueprints/resolver.js +28 -15
- package/src/cli/bootstrap.d.ts +1 -1
- package/src/cli/deploy-runtime.d.ts +30 -0
- package/src/cli/deploy-runtime.js +174 -0
- package/src/cli/dev-command.d.ts +18 -4
- package/src/cli/dev-command.js +71 -0
- package/src/cli/main.d.ts +9 -1
- package/src/cli/main.js +157 -24
- package/src/cli/output.d.ts +1 -1
- package/src/cli/output.js +1 -1
- package/src/cli/personal-secret-runtime.d.ts +12 -0
- package/src/cli/personal-secret-runtime.js +18 -3
- package/src/cli/secret-terminal.js +1 -1
- package/src/container-images.d.ts +1 -1
- package/src/container-images.js +1 -1
- package/src/developer-agent/context.js +41 -9
- package/src/developer-agent/generator-planning.js +20 -6
- package/src/developer-agent/git-inspection.d.ts +6 -2
- package/src/developer-agent/git-inspection.js +78 -6
- package/src/developer-agent/model.d.ts +5 -3
- package/src/developer-agent/model.js +4 -4
- package/src/developer-agent/operation-registry.d.ts +15 -0
- package/src/developer-agent/operation-registry.js +342 -49
- package/src/developer-agent/planning.d.ts +8 -1
- package/src/developer-agent/planning.js +39 -16
- package/src/developer-agent/preview-changes.d.ts +2 -0
- package/src/developer-agent/preview-changes.js +14 -2
- package/src/developer-agent/preview-source.js +3 -0
- package/src/developer-agent/validation.js +25 -9
- package/src/developer-agent/workspace-inspection.js +60 -5
- package/src/executors/blueprint-trust/blueprint-trust.js +6 -0
- package/src/executors/deployment-plan/deployment-plan.d.ts +2 -0
- package/src/executors/deployment-plan/deployment-plan.js +57 -2
- package/src/executors/dev-doctor/dev-doctor.js +13 -6
- package/src/executors/dev-task-cleanup/dev-task-cleanup.d.ts +23 -0
- package/src/executors/dev-task-cleanup/dev-task-cleanup.js +145 -0
- package/src/executors/dev-task-cleanup/schema.json +9 -0
- package/src/executors/dev-task-cleanup/task-stack-absence.d.ts +27 -0
- package/src/executors/dev-task-cleanup/task-stack-absence.js +281 -0
- package/src/executors/dev-task-cleanup/task-stack-checkpoint.d.ts +12 -0
- package/src/executors/dev-task-cleanup/task-stack-checkpoint.js +92 -0
- package/src/executors/fleet/fleet.js +1 -1
- package/src/executors/foundation-preflight/foundation-preflight.d.ts +1 -0
- package/src/executors/foundation-preflight/foundation-preflight.js +6 -2
- package/src/executors/foundation-preflight/schema.json +5 -0
- package/src/executors/foundation-profile-preflight/foundation-profile-preflight.d.ts +13 -2
- package/src/executors/foundation-profile-preflight/foundation-profile-preflight.js +95 -31
- package/src/executors/foundation-profile-preflight/production-database-safeguard.d.ts +65 -0
- package/src/executors/foundation-profile-preflight/production-database-safeguard.js +223 -0
- package/src/executors/foundation-profile-preflight/schema.json +15 -4
- package/src/executors/foundation-remove-preflight/foundation-remove-preflight.d.ts +11 -1
- package/src/executors/foundation-remove-preflight/foundation-remove-preflight.js +70 -20
- package/src/executors/foundation-remove-preflight/schema.json +6 -1
- package/src/executors/github-bootstrap/github-bootstrap.d.ts +1 -0
- package/src/executors/github-bootstrap/github-bootstrap.js +3 -0
- package/src/executors/github-bootstrap/schema.json +7 -1
- package/src/executors/infrastructure-plan/infrastructure-plan.d.ts +29 -136
- package/src/executors/infrastructure-plan/infrastructure-plan.js +230 -3282
- package/src/executors/infrastructure-plan/schema.json +12 -52
- package/src/executors/local-deploy/federated-credential-transport.d.ts +48 -0
- package/src/executors/local-deploy/federated-credential-transport.js +252 -0
- package/src/executors/local-deploy/local-approval.d.ts +18 -0
- package/src/executors/local-deploy/local-approval.js +39 -0
- package/src/executors/local-deploy/local-deploy.d.ts +112 -0
- package/src/executors/local-deploy/local-deploy.js +1404 -0
- package/src/executors/local-deploy/schema.json +70 -0
- package/src/executors/refresh/refresh.js +2 -2
- package/src/executors/remove/remove.js +2 -2
- package/src/executors/router-waf-rehearsal/router-waf-rehearsal.d.ts +42 -0
- package/src/executors/router-waf-rehearsal/router-waf-rehearsal.js +1229 -0
- package/src/executors/router-waf-rehearsal/schema.json +29 -0
- package/src/executors/secret-initialize/schema.json +42 -0
- package/src/executors/secret-initialize/secret-initialize.d.ts +34 -0
- package/src/executors/secret-initialize/secret-initialize.js +297 -0
- package/src/executors/secret-metadata/schema.json +5 -0
- package/src/executors/secret-metadata/secret-metadata.d.ts +16 -2
- package/src/executors/secret-metadata/secret-metadata.js +129 -30
- package/src/executors/sst-lifecycle/generated-secret-retirement.d.ts +53 -0
- package/src/executors/sst-lifecycle/generated-secret-retirement.js +192 -0
- package/src/executors/sst-lifecycle/governed-native-operation.d.ts +48 -0
- package/src/executors/sst-lifecycle/governed-native-operation.js +163 -0
- package/src/executors/sst-lifecycle/logging-preflight.d.ts +5 -0
- package/src/executors/sst-lifecycle/logging-preflight.js +23 -13
- package/src/executors/sst-lifecycle/run-sst-command.d.ts +46 -6
- package/src/executors/sst-lifecycle/run-sst-command.js +750 -71
- package/src/executors/sst-lifecycle/schema.json +8 -1
- package/src/executors/sst-lifecycle/secret-preflight.d.ts +2 -0
- package/src/executors/sst-lifecycle/secret-preflight.js +13 -7
- package/src/executors/sst-lifecycle/sst-environment-source.d.ts +33 -0
- package/src/executors/sst-lifecycle/sst-environment-source.js +267 -0
- package/src/executors/sst-lifecycle/task-unlock.d.ts +15 -0
- package/src/executors/sst-lifecycle/task-unlock.js +126 -0
- package/src/executors/unlock/unlock.js +2 -2
- package/src/foundation/capability-plan.d.ts +43 -0
- package/src/foundation/capability-plan.js +168 -0
- package/src/foundation/cost-estimate.d.ts +170 -0
- package/src/foundation/cost-estimate.js +683 -0
- package/src/foundation/discovery.d.ts +14 -11
- package/src/foundation/discovery.js +32 -6
- package/src/foundation/index.d.ts +4 -0
- package/src/foundation/index.js +11 -0
- package/src/foundation/local-database-contract.d.ts +20 -0
- package/src/foundation/local-database-contract.js +262 -13
- package/src/foundation/local-private-smoke-access.d.ts +20 -0
- package/src/foundation/local-private-smoke-access.js +197 -0
- package/src/foundation/logging-preflight.js +1 -1
- package/src/foundation/logging.d.ts +3 -1
- package/src/foundation/logging.js +1 -1
- package/src/foundation/partial-state.d.ts +10 -0
- package/src/foundation/partial-state.js +21 -0
- package/src/foundation/preflight.js +37 -18
- package/src/foundation/private-smoke-access.d.ts +25 -0
- package/src/foundation/private-smoke-access.js +135 -0
- package/src/foundation/private-smoke-admission.d.ts +45 -0
- package/src/foundation/private-smoke-admission.js +180 -0
- package/src/foundation/private-smoke-control-plane.d.ts +18 -0
- package/src/foundation/private-smoke-control-plane.js +184 -0
- package/src/foundation/provision-database-bindings.d.ts +5 -0
- package/src/foundation/provision-database-bindings.js +41 -8
- package/src/foundation/provision-logical-database.js +15 -8
- package/src/foundation/requirements.d.ts +3 -3
- package/src/foundation/requirements.js +24 -12
- package/src/foundation/router-waf-rate-rehearsal.d.ts +97 -0
- package/src/foundation/router-waf-rate-rehearsal.js +210 -0
- package/src/foundation/router-waf.d.ts +149 -0
- package/src/foundation/router-waf.js +809 -0
- package/src/foundation/stages.d.ts +1 -0
- package/src/foundation/stages.js +11 -0
- package/src/generators/blueprint/blueprint.d.ts +5 -0
- package/src/generators/blueprint/blueprint.js +102 -0
- package/src/generators/blueprint/files.d.ts +6 -0
- package/src/generators/blueprint/files.js +217 -0
- package/src/generators/blueprint/schema.d.ts +4 -0
- package/src/generators/blueprint/schema.json +18 -0
- package/src/generators/formatting.d.ts +8 -0
- package/src/generators/formatting.js +141 -0
- package/src/generators/function/function.js +38 -17
- package/src/generators/logging-registry.d.ts +2 -0
- package/src/generators/logging-registry.js +22 -0
- package/src/generators/post-generation-install.js +2 -2
- package/src/generators/preset/dependency-overrides.d.ts +8 -0
- package/src/generators/preset/dependency-overrides.js +55 -0
- package/src/generators/preset/generator.js +43 -3
- package/src/generators/preset/schema.d.ts +10 -0
- package/src/generators/preset/schema.json +47 -0
- package/src/generators/preset/workspace-files-adopter-skills.d.ts +7 -0
- package/src/generators/preset/workspace-files-adopter-skills.js +324 -0
- package/src/generators/preset/workspace-files-ci-acceptance.d.ts +1 -0
- package/src/generators/preset/workspace-files-ci-acceptance.js +791 -0
- package/src/generators/preset/workspace-files-ci-install.d.ts +2 -0
- package/src/generators/preset/workspace-files-ci-install.js +196 -0
- package/src/generators/preset/workspace-files-developer-agent.d.ts +2 -0
- package/src/generators/preset/workspace-files-developer-agent.js +162 -44
- package/src/generators/preset/workspace-files-ecr-inspector.d.ts +18 -0
- package/src/generators/preset/workspace-files-ecr-inspector.js +298 -0
- package/src/generators/preset/workspace-files-foundation-workload-roles.js +5 -2
- package/src/generators/preset/workspace-files-performance-cost.d.ts +2 -2
- package/src/generators/preset/workspace-files-performance-cost.js +8 -8
- package/src/generators/preset/workspace-files-performance-load.d.ts +1 -1
- package/src/generators/preset/workspace-files-performance-load.js +39 -25
- package/src/generators/preset/workspace-files-shared-operation-claims.d.ts +10 -0
- package/src/generators/preset/workspace-files-shared-operation-claims.js +59 -0
- package/src/generators/preset/workspace-files-stage-authority.d.ts +6 -0
- package/src/generators/preset/workspace-files-stage-authority.js +529 -0
- package/src/generators/preset/workspace-files-thin-deploy.d.ts +65 -0
- package/src/generators/preset/workspace-files-thin-deploy.js +2507 -0
- package/src/generators/preset/workspace-files-thin-docs.d.ts +7 -0
- package/src/generators/preset/workspace-files-thin-docs.js +370 -0
- package/src/generators/preset/workspace-files-thin-release.d.ts +14 -0
- package/src/generators/preset/workspace-files-thin-release.js +26 -0
- package/src/generators/preset/workspace-files-workload-role-baseline.js +254 -42
- package/src/generators/preset/workspace-files.d.ts +18 -2
- package/src/generators/preset/workspace-files.js +1525 -609
- package/src/generators/repository-policy/repository-policy.js +235 -5
- package/src/generators/repository-policy/schema.d.ts +10 -0
- package/src/generators/repository-policy/schema.json +80 -4
- package/src/generators/repository-policy/tree-policy.d.ts +3 -0
- package/src/generators/repository-policy/tree-policy.js +25 -0
- package/src/generators/slice/built-in-plan-api-contract-files.js +49 -11
- package/src/generators/slice/built-in-plan-api-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-api-files.js +130 -414
- package/src/generators/slice/built-in-plan-async-work-files.js +166 -1969
- package/src/generators/slice/built-in-plan-authorization-files.js +102 -334
- package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js +975 -233
- package/src/generators/slice/built-in-plan-authorization-storage.d.ts +3 -0
- package/src/generators/slice/built-in-plan-authorization-storage.js +119 -0
- package/src/generators/slice/built-in-plan-background-work-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-background-work-files.js +99 -0
- package/src/generators/slice/built-in-plan-data-deletion-files.js +2 -830
- package/src/generators/slice/built-in-plan-data-export-infrastructure-files.js +5 -31
- package/src/generators/slice/built-in-plan-data-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-data-files.js +176 -1200
- package/src/generators/slice/built-in-plan-data-lifecycle-files.js +53 -28
- package/src/generators/slice/built-in-plan-data-lifecycle-runtime-files.js +37 -2
- package/src/generators/slice/built-in-plan-data-retention-job-files.js +7 -2443
- package/src/generators/slice/built-in-plan-data-retention-launcher-files.js +50 -8
- package/src/generators/slice/built-in-plan-data-rights-files.js +6 -4482
- package/src/generators/slice/built-in-plan-database-lifecycle-files.js +44 -25
- package/src/generators/slice/built-in-plan-database-retirement-rehearsal-files.js +25 -2
- package/src/generators/slice/built-in-plan-foundation-files.d.ts +2 -1
- package/src/generators/slice/built-in-plan-foundation-files.js +39 -18
- package/src/generators/slice/built-in-plan-frontend-files.js +14 -3
- package/src/generators/slice/built-in-plan-infrastructure-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-infrastructure-files.js +933 -730
- package/src/generators/slice/built-in-plan-infrastructure-safety-files.js +75 -14
- package/src/generators/slice/built-in-plan-key-value-cleanup.d.ts +4 -0
- package/src/generators/slice/built-in-plan-key-value-cleanup.js +100 -0
- package/src/generators/slice/built-in-plan-key-value-consumer.d.ts +4 -0
- package/src/generators/slice/built-in-plan-key-value-consumer.js +151 -0
- package/src/generators/slice/built-in-plan-key-value-deletion.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-deletion.js +377 -0
- package/src/generators/slice/built-in-plan-key-value-delivery.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-delivery.js +137 -0
- package/src/generators/slice/built-in-plan-key-value-export.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-export.js +595 -0
- package/src/generators/slice/built-in-plan-key-value-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-files.js +31 -0
- package/src/generators/slice/built-in-plan-key-value-holds.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-holds.js +189 -0
- package/src/generators/slice/built-in-plan-key-value-infrastructure.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-infrastructure.js +102 -0
- package/src/generators/slice/built-in-plan-key-value-performance.d.ts +5 -0
- package/src/generators/slice/built-in-plan-key-value-performance.js +530 -0
- package/src/generators/slice/built-in-plan-key-value-provider.d.ts +4 -0
- package/src/generators/slice/built-in-plan-key-value-provider.js +366 -0
- package/src/generators/slice/built-in-plan-key-value-records.d.ts +3 -0
- package/src/generators/slice/built-in-plan-key-value-records.js +232 -0
- package/src/generators/slice/built-in-plan-key-value-retention-infrastructure.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-retention-infrastructure.js +103 -0
- package/src/generators/slice/built-in-plan-key-value-retention-job-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-retention-job-files.js +160 -0
- package/src/generators/slice/built-in-plan-key-value-retention-maintenance.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-retention-maintenance.js +329 -0
- package/src/generators/slice/built-in-plan-key-value-retention-worker.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-retention-worker.js +477 -0
- package/src/generators/slice/built-in-plan-key-value-retention.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-retention.js +184 -0
- package/src/generators/slice/built-in-plan-metadata-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-metadata-files.js +159 -29
- package/src/generators/slice/built-in-plan-native-runtime-authority-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-native-runtime-authority-files.js +324 -0
- package/src/generators/slice/built-in-plan-operations-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-operations-files.js +585 -97
- package/src/generators/slice/built-in-plan-performance-files.d.ts +2 -1
- package/src/generators/slice/built-in-plan-performance-files.js +220 -1074
- package/src/generators/slice/built-in-plan-project-files.js +141 -135
- package/src/generators/slice/built-in-plan-release-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-release-files.js +310 -70
- package/src/generators/slice/built-in-plan-reliability-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-reliability-files.js +283 -44
- package/src/generators/slice/built-in-plan-secret-runtime-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-secret-runtime-files.js +87 -76
- package/src/generators/slice/built-in-plan-shared.d.ts +4 -2
- package/src/generators/slice/built-in-plan-shared.js +41 -19
- package/src/generators/slice/built-in-plan-sql-events.d.ts +2 -0
- package/src/generators/slice/built-in-plan-sql-events.js +276 -0
- package/src/generators/slice/built-in-plan-testing-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-testing-files.js +304 -172
- package/src/generators/slice/built-in-plan-testing-persistence-event-files.d.ts +7 -0
- package/src/generators/slice/built-in-plan-testing-persistence-event-files.js +487 -0
- package/src/generators/slice/built-in-plan-testing-persistence-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-testing-persistence-files.js +180 -0
- package/src/generators/slice/built-in-plan-thin-release-files.d.ts +11 -0
- package/src/generators/slice/built-in-plan-thin-release-files.js +162 -0
- package/src/generators/slice/built-in-plan-verification-files.js +76 -2
- package/src/generators/slice/built-in-plan-web-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-web-files.js +223 -16
- package/src/generators/slice/built-in-plan-workload-role-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-workload-role-files.js +143 -279
- package/src/generators/slice/built-in-plan.d.ts +11 -1
- package/src/generators/slice/built-in-plan.js +83 -30
- package/src/generators/slice/data-policy-review-seed.d.ts +2 -0
- package/src/generators/slice/data-policy-review-seed.js +22 -0
- package/src/generators/slice/deployed-smoke-readiness.d.ts +2 -0
- package/src/generators/slice/deployed-smoke-readiness.js +214 -0
- package/src/generators/slice/local-source-identity.d.ts +2 -0
- package/src/generators/slice/local-source-identity.js +31 -0
- package/src/generators/slice/persistence-contract.d.ts +14 -0
- package/src/generators/slice/persistence-contract.js +32 -0
- package/src/generators/slice/slice.js +61 -50
- package/src/generators/sql/schema.d.ts +7 -0
- package/src/generators/sql/schema.json +38 -0
- package/src/generators/sql/sql.d.ts +6 -0
- package/src/generators/sql/sql.js +53 -0
- package/src/generators/upgrade/sql-seeded-reconciliation.d.ts +2 -0
- package/src/generators/upgrade/sql-seeded-reconciliation.js +170 -0
- package/src/generators/upgrade/upgrade.d.ts +15 -1
- package/src/generators/upgrade/upgrade.js +435 -44
- package/src/generators/workload/frozen-container.d.ts +1 -1
- package/src/generators/workload/performance-registration.d.ts +1 -0
- package/src/generators/workload/performance-registration.js +9 -11
- package/src/generators/workload/service-performance-files.d.ts +1 -0
- package/src/generators/workload/service-performance-files.js +27 -7
- package/src/generators/workload/workload.d.ts +1 -1
- package/src/generators/workload/workload.js +105 -29
- package/src/git-policy/action-pins.d.ts +5 -0
- package/src/git-policy/action-pins.js +5 -0
- package/src/git-policy/github-bootstrap.d.ts +48 -8
- package/src/git-policy/github-bootstrap.js +344 -176
- package/src/git-policy/routing.d.ts +7 -1
- package/src/git-policy/routing.js +62 -1
- package/src/index.d.ts +1 -0
- package/src/index.js +1 -0
- package/src/infrastructure-change/local-capability.d.ts +73 -0
- package/src/infrastructure-change/local-capability.js +114 -0
- package/src/infrastructure-change/model.d.ts +25 -4
- package/src/infrastructure-change/model.js +1 -0
- package/src/infrastructure-change/normalize.d.ts +4 -0
- package/src/infrastructure-change/normalize.js +47 -7
- package/src/infrastructure-change/plan.d.ts +30 -3
- package/src/infrastructure-change/plan.js +323 -13
- package/src/infrastructure-change/risk.d.ts +3 -1
- package/src/infrastructure-change/risk.js +96 -16
- package/src/local-dev/browser-verification.d.ts +37 -0
- package/src/local-dev/browser-verification.js +281 -0
- package/src/local-dev/contracts.d.ts +10 -1
- package/src/local-dev/contracts.js +114 -13
- package/src/local-dev/dev-command-core.js +24 -5
- package/src/local-dev/docker-credential-health.d.ts +14 -0
- package/src/local-dev/docker-credential-health.js +94 -0
- package/src/local-dev/environment-inspector.d.ts +7 -0
- package/src/local-dev/environment-inspector.js +42 -15
- package/src/local-dev/environment-state.js +2 -2
- package/src/local-dev/errors.d.ts +1 -1
- package/src/local-dev/errors.js +2 -0
- package/src/local-dev/fleet-client.js +2 -2
- package/src/local-dev/fleet-configuration.js +57 -25
- package/src/local-dev/fleet-inventory.js +58 -22
- package/src/local-dev/fleet-ipc.d.ts +7 -0
- package/src/local-dev/fleet-ipc.js +14 -2
- package/src/local-dev/fleet-launcher.d.ts +6 -0
- package/src/local-dev/fleet-launcher.js +16 -4
- package/src/local-dev/fleet-logs.js +50 -22
- package/src/local-dev/fleet-process.d.ts +20 -1
- package/src/local-dev/fleet-process.js +197 -41
- package/src/local-dev/fleet-runner-evidence.d.ts +5 -0
- package/src/local-dev/fleet-runner-evidence.js +56 -0
- package/src/local-dev/fleet-runtime.js +268 -229
- package/src/local-dev/fleet-session.d.ts +2 -0
- package/src/local-dev/fleet-session.js +28 -11
- package/src/local-dev/fleet-slice-runner-daemon.js +4 -0
- package/src/local-dev/fleet-state.d.ts +5 -2
- package/src/local-dev/fleet-state.js +25 -4
- package/src/local-dev/fleet-supervisor.d.ts +3 -0
- package/src/local-dev/fleet-supervisor.js +66 -11
- package/src/local-dev/fleet-web-start.d.ts +12 -0
- package/src/local-dev/fleet-web-start.js +48 -0
- package/src/local-dev/identity.js +3 -14
- package/src/local-dev/index.d.ts +1 -0
- package/src/local-dev/index.js +1 -0
- package/src/local-dev/native-completion.d.ts +9 -0
- package/src/local-dev/native-completion.js +130 -0
- package/src/local-dev/nx-workspace.d.ts +4 -2
- package/src/local-dev/nx-workspace.js +11 -6
- package/src/local-dev/personal-stage.d.ts +4 -5
- package/src/local-dev/personal-stage.js +6 -54
- package/src/local-dev/port-claims.d.ts +13 -0
- package/src/local-dev/port-claims.js +46 -0
- package/src/local-dev/ports.d.ts +14 -2
- package/src/local-dev/ports.js +78 -3
- package/src/local-dev/private-state.d.ts +11 -0
- package/src/local-dev/private-state.js +146 -40
- package/src/local-dev/process-group.d.ts +4 -0
- package/src/local-dev/process-group.js +29 -0
- package/src/local-dev/process-identity.d.ts +8 -0
- package/src/local-dev/process-identity.js +31 -1
- package/src/local-dev/proxy.js +11 -12
- package/src/local-dev/runtime-paths.js +1 -1
- package/src/local-dev/slice-claim.js +1 -1
- package/src/local-dev/slice-runner.d.ts +15 -6
- package/src/local-dev/slice-runner.js +189 -54
- package/src/local-dev/task-attachment.d.ts +17 -0
- package/src/local-dev/task-attachment.js +73 -0
- package/src/local-dev/task-authority.d.ts +10 -0
- package/src/local-dev/task-authority.js +27 -0
- package/src/local-dev/task-identity.d.ts +33 -0
- package/src/local-dev/task-identity.js +418 -0
- package/src/local-dev/task-native-writer.d.ts +18 -0
- package/src/local-dev/task-native-writer.js +329 -0
- package/src/local-dev/tls.js +8 -2
- package/src/local-dev/web-launch.d.ts +9 -0
- package/src/local-dev/web-launch.js +130 -0
- package/src/ownership/composition.d.ts +2 -1
- package/src/ownership/composition.js +1 -0
- package/src/performance/cost-collection.d.ts +1 -1
- package/src/performance/cost-collection.js +4 -2
- package/src/performance/cost-controls.d.ts +8 -4
- package/src/performance/cost-controls.js +39 -28
- package/src/performance/evidence.d.ts +1 -1
- package/src/performance/evidence.js +5 -3
- package/src/performance/load-safety.js +5 -3
- package/src/performance/model.d.ts +4 -4
- package/src/performance/model.js +4 -5
- package/src/performance/scenario.d.ts +22 -0
- package/src/performance/scenario.js +100 -92
- package/src/release/index.d.ts +0 -2
- package/src/release/index.js +0 -2
- package/src/release/source-authority.d.ts +5 -5
- package/src/release/source-authority.js +141 -124
- package/src/repository-policy/aurora-preflight.d.ts +1 -1
- package/src/repository-policy/aurora-preflight.js +4 -1
- package/src/repository-policy/capacity-migration.d.ts +3 -0
- package/src/repository-policy/capacity-migration.js +21 -0
- package/src/repository-policy/deployment-plan.d.ts +4 -1
- package/src/repository-policy/deployment-plan.js +9 -0
- package/src/repository-policy/foundation-profile.js +10 -1
- package/src/repository-policy/policy.d.ts +57 -3
- package/src/repository-policy/policy.js +497 -11
- package/src/schemas/blueprint-v1.json +5 -4
- package/src/schemas/foundation-requirement-v1.json +5 -4
- package/src/schemas/performance-policy-v1.json +3 -3
- package/src/schemas/repository-policy-v1.json +130 -6
- package/src/schemas/testing-evidence-result-v1.json +1 -1
- package/src/secrets/adapters.d.ts +7 -2
- package/src/secrets/adapters.js +34 -3
- package/src/secrets/aws-control-plane.d.ts +10 -1
- package/src/secrets/aws-control-plane.js +67 -0
- package/src/secrets/generated-aws-control-plane.d.ts +23 -0
- package/src/secrets/generated-aws-control-plane.js +259 -0
- package/src/secrets/metadata.js +38 -10
- package/src/secrets/model.d.ts +8 -1
- package/src/secrets/model.js +24 -3
- package/src/secrets/operator.d.ts +91 -3
- package/src/secrets/operator.js +463 -13
- package/src/secrets/personal-command.d.ts +18 -2
- package/src/secrets/personal-command.js +66 -12
- package/src/secrets/registry.d.ts +2 -1
- package/src/secrets/registry.js +18 -1
- package/src/secrets/verification.js +15 -2
- package/src/secrets/workload-access.js +10 -2
- package/src/sst-environment-source-format.d.ts +6 -0
- package/src/sst-environment-source-format.js +199 -0
- package/src/supply-chain/action-pins.js +61 -7
- package/src/supply-chain/blueprint-trust-git.js +85 -24
- package/src/supply-chain/blueprint-verification-authority.js +55 -3
- package/src/supply-chain/build-output-bounds.d.ts +2 -0
- package/src/supply-chain/build-output-bounds.js +12 -0
- package/src/supply-chain/build-output.d.ts +7 -1
- package/src/supply-chain/build-output.js +177 -24
- package/src/supply-chain/ecr-scanning.d.ts +5 -3
- package/src/supply-chain/ecr-scanning.js +11 -7
- package/src/supply-chain/ecs-deployed-image.d.ts +5 -3
- package/src/supply-chain/ecs-deployed-image.js +6 -4
- package/src/supply-chain/generated-blueprint-trust-command.js +18 -1
- package/src/supply-chain/generated-command.js +1 -0
- package/src/supply-chain/generated-container-release-command.js +1285 -124
- package/src/supply-chain/generated-ecr-collector-command.d.ts +0 -1
- package/src/supply-chain/generated-ecr-collector-command.js +200 -93
- package/src/supply-chain/generated-files.js +25 -14
- package/src/supply-chain/generated-scanner-command.js +2 -1
- package/src/supply-chain/index.d.ts +1 -1
- package/src/supply-chain/index.js +2 -1
- package/src/supply-chain/sbom.d.ts +1 -1
- package/src/supply-chain/sbom.js +30 -5
- package/src/supply-chain/scanner.js +3 -0
- package/src/testing-evidence/fixture-scope.d.ts +2 -1
- package/src/testing-evidence/fixture-scope.js +4 -2
- package/src/testing-evidence/result.d.ts +2 -1
- package/src/testing-evidence/result.js +18 -4
- package/src/workload-roles/account-baseline.d.ts +11 -0
- package/src/workload-roles/account-baseline.js +253 -16
- package/src/workload-roles/authority.d.ts +3 -1
- package/src/workload-roles/authority.js +48 -2
- package/src/workload-roles/aws-s3-state.js +27 -0
- package/src/workload-roles/boundary.d.ts +2 -1
- package/src/workload-roles/boundary.js +9 -2
- package/src/workload-roles/handler.js +52 -24
- package/src/workload-roles/index.d.ts +10 -17
- package/src/workload-roles/index.js +5 -18
- package/src/workload-roles/injection.js +1 -1
- package/src/workload-roles/provisioner.js +12 -4
- package/src/workload-roles/registration.d.ts +1 -0
- package/src/workload-roles/registration.js +5 -0
- package/src/workload-roles/stage-boundary.js +1 -1
- package/src/workload-roles/standard-slice.js +3 -3
- package/src/workspace-deployment/affected-lifecycle.d.ts +53 -0
- package/src/workspace-deployment/affected-lifecycle.js +84 -0
- package/src/workspace-deployment/identity.d.ts +3 -0
- package/src/workspace-deployment/identity.js +22 -0
- package/src/workspace-deployment/index.d.ts +4 -0
- package/src/workspace-deployment/index.js +8 -0
- package/src/workspace-deployment/migration-evidence.d.ts +29 -0
- package/src/workspace-deployment/migration-evidence.js +83 -0
- package/src/workspace-deployment/migration.d.ts +27 -0
- package/src/workspace-deployment/migration.js +109 -0
- package/src/workspace-deployment/ownership.d.ts +81 -0
- package/src/workspace-deployment/ownership.js +381 -0
- package/src/api-contract/baseline-update.js.map +0 -1
- package/src/api-contract/compatibility.js.map +0 -1
- package/src/api-contract/contract-graph.js.map +0 -1
- package/src/api-contract/evidence.js.map +0 -1
- package/src/api-contract/index.js.map +0 -1
- package/src/api-contract/model.fixtures.d.ts +0 -2
- package/src/api-contract/model.fixtures.js +0 -81
- package/src/api-contract/model.fixtures.js.map +0 -1
- package/src/api-contract/model.js.map +0 -1
- package/src/api-contract/rollout.js.map +0 -1
- package/src/authorization/compatibility.js.map +0 -1
- package/src/authorization/index.js.map +0 -1
- package/src/blueprints/execution.js.map +0 -1
- package/src/blueprints/external-sandbox.js.map +0 -1
- package/src/blueprints/index.js.map +0 -1
- package/src/blueprints/inputs.js.map +0 -1
- package/src/blueprints/manifest.js.map +0 -1
- package/src/blueprints/resolver.js.map +0 -1
- package/src/blueprints/transitions.js.map +0 -1
- package/src/cli/agent-command.js.map +0 -1
- package/src/cli/agent-runtime.js.map +0 -1
- package/src/cli/blueprint-authority.js.map +0 -1
- package/src/cli/blueprint-command.js.map +0 -1
- package/src/cli/bootstrap.js.map +0 -1
- package/src/cli/dev-command.js.map +0 -1
- package/src/cli/main.js.map +0 -1
- package/src/cli/output.js.map +0 -1
- package/src/cli/personal-secret-runtime.js.map +0 -1
- package/src/cli/secret-terminal.js.map +0 -1
- package/src/container-images.js.map +0 -1
- package/src/data-lifecycle/index.js.map +0 -1
- package/src/data-lifecycle/model.fixtures.d.ts +0 -2
- package/src/data-lifecycle/model.fixtures.js +0 -244
- package/src/data-lifecycle/model.fixtures.js.map +0 -1
- package/src/data-lifecycle/model.js.map +0 -1
- package/src/data-lifecycle/sensitive-artifacts.js.map +0 -1
- package/src/data-lifecycle/validation.js.map +0 -1
- package/src/database-retirement/contract.js.map +0 -1
- package/src/database-retirement/index.js.map +0 -1
- package/src/deterministic-order.js.map +0 -1
- package/src/developer-agent/context.js.map +0 -1
- package/src/developer-agent/generator-planning.js.map +0 -1
- package/src/developer-agent/generator-preview.js.map +0 -1
- package/src/developer-agent/git-inspection.js.map +0 -1
- package/src/developer-agent/handoff.js.map +0 -1
- package/src/developer-agent/index.js.map +0 -1
- package/src/developer-agent/model.js.map +0 -1
- package/src/developer-agent/operation-registry.js.map +0 -1
- package/src/developer-agent/ordering.js.map +0 -1
- package/src/developer-agent/planning.js.map +0 -1
- package/src/developer-agent/preview-changes.js.map +0 -1
- package/src/developer-agent/preview-source.js.map +0 -1
- package/src/developer-agent/validation.js.map +0 -1
- package/src/developer-agent/workspace-inspection.js.map +0 -1
- package/src/developer-agent/workspace-path.js.map +0 -1
- package/src/executors/blueprint-trust/blueprint-trust.js.map +0 -1
- package/src/executors/blueprint-verification/blueprint-verification.js.map +0 -1
- package/src/executors/deploy/deploy.js.map +0 -1
- package/src/executors/deployment-plan/deployment-plan.js.map +0 -1
- package/src/executors/dev/dev.js.map +0 -1
- package/src/executors/dev-doctor/dev-doctor.js.map +0 -1
- package/src/executors/dev-setup/dev-setup.js.map +0 -1
- package/src/executors/developer-context/developer-context.js.map +0 -1
- package/src/executors/fleet/fleet.js.map +0 -1
- package/src/executors/foundation-preflight/foundation-preflight.js.map +0 -1
- package/src/executors/foundation-profile-preflight/foundation-profile-preflight.js.map +0 -1
- package/src/executors/foundation-remove-preflight/foundation-remove-preflight.js.map +0 -1
- package/src/executors/github-bootstrap/github-bootstrap.js.map +0 -1
- package/src/executors/infrastructure-plan/infrastructure-plan.js.map +0 -1
- package/src/executors/refresh/refresh.js.map +0 -1
- package/src/executors/remove/remove.js.map +0 -1
- package/src/executors/secret-guardrails/secret-guardrails.js.map +0 -1
- package/src/executors/secret-metadata/secret-metadata.js.map +0 -1
- package/src/executors/sst-lifecycle/logging-preflight.js.map +0 -1
- package/src/executors/sst-lifecycle/run-sst-command.js.map +0 -1
- package/src/executors/sst-lifecycle/secret-preflight.js.map +0 -1
- package/src/executors/unlock/unlock.js.map +0 -1
- package/src/executors/work-markers/work-markers.js.map +0 -1
- package/src/foundation/discovery.js.map +0 -1
- package/src/foundation/index.js.map +0 -1
- package/src/foundation/local-database-contract.js.map +0 -1
- package/src/foundation/logging-preflight.js.map +0 -1
- package/src/foundation/logging.js.map +0 -1
- package/src/foundation/preflight.js.map +0 -1
- package/src/foundation/provision-database-bindings.js.map +0 -1
- package/src/foundation/provision-logical-database.js.map +0 -1
- package/src/foundation/requirements.js.map +0 -1
- package/src/foundation/router-origin-proof.js.map +0 -1
- package/src/foundation/stages.js.map +0 -1
- package/src/frontend-security/index.js.map +0 -1
- package/src/frontend-security/security-headers.js.map +0 -1
- package/src/generators/feature-registry.js.map +0 -1
- package/src/generators/function/function.js.map +0 -1
- package/src/generators/job/job.js.map +0 -1
- package/src/generators/logging-registry.js.map +0 -1
- package/src/generators/post-generation-install.js.map +0 -1
- package/src/generators/preset/generator.js.map +0 -1
- package/src/generators/preset/tool-versions.js.map +0 -1
- package/src/generators/preset/workspace-files-developer-agent.js.map +0 -1
- package/src/generators/preset/workspace-files-foundation-workload-roles.js.map +0 -1
- package/src/generators/preset/workspace-files-performance-cost.js.map +0 -1
- package/src/generators/preset/workspace-files-performance-load.js.map +0 -1
- package/src/generators/preset/workspace-files-workload-role-baseline.js.map +0 -1
- package/src/generators/preset/workspace-files.js.map +0 -1
- package/src/generators/repository-policy/repository-policy.js.map +0 -1
- package/src/generators/service/service.js.map +0 -1
- package/src/generators/slice/built-in-plan-api-contract-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-api-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-async-work-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-authorization-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-deletion-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-export-infrastructure-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-lifecycle-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-lifecycle-runtime-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-retention-job-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-retention-launcher-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-rights-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-database-lifecycle-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-database-retirement-rehearsal-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-foundation-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-frontend-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-infrastructure-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-infrastructure-safety-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-metadata-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-operations-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-performance-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-project-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-release-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-reliability-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-secret-runtime-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-shared.js.map +0 -1
- package/src/generators/slice/built-in-plan-testing-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-verification-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-web-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-workload-role-files.js.map +0 -1
- package/src/generators/slice/built-in-plan.js.map +0 -1
- package/src/generators/slice/slice.js.map +0 -1
- package/src/generators/upgrade/upgrade.js.map +0 -1
- package/src/generators/workload/frozen-container.js.map +0 -1
- package/src/generators/workload/performance-registration.js.map +0 -1
- package/src/generators/workload/reliability-registration.js.map +0 -1
- package/src/generators/workload/service-performance-files.js.map +0 -1
- package/src/generators/workload/workload.js.map +0 -1
- package/src/git-policy/action-pins.js.map +0 -1
- package/src/git-policy/github-bootstrap.js.map +0 -1
- package/src/git-policy/index.js.map +0 -1
- package/src/git-policy/routing.js.map +0 -1
- package/src/index.js.map +0 -1
- package/src/infrastructure-change/convergence.js.map +0 -1
- package/src/infrastructure-change/drift.js.map +0 -1
- package/src/infrastructure-change/index.js.map +0 -1
- package/src/infrastructure-change/model.js.map +0 -1
- package/src/infrastructure-change/normalize.js.map +0 -1
- package/src/infrastructure-change/plan.js.map +0 -1
- package/src/infrastructure-change/risk.js.map +0 -1
- package/src/infrastructure-change/summary.js.map +0 -1
- package/src/local-dev/context-discovery.js.map +0 -1
- package/src/local-dev/contract-validation.js.map +0 -1
- package/src/local-dev/contracts.js.map +0 -1
- package/src/local-dev/control-contract.js.map +0 -1
- package/src/local-dev/dev-command-core.js.map +0 -1
- package/src/local-dev/effects.js.map +0 -1
- package/src/local-dev/environment-inspector.js.map +0 -1
- package/src/local-dev/environment-state.js.map +0 -1
- package/src/local-dev/errors.js.map +0 -1
- package/src/local-dev/fleet-client.js.map +0 -1
- package/src/local-dev/fleet-configuration.js.map +0 -1
- package/src/local-dev/fleet-inventory.js.map +0 -1
- package/src/local-dev/fleet-ipc.js.map +0 -1
- package/src/local-dev/fleet-launcher.js.map +0 -1
- package/src/local-dev/fleet-lock.js.map +0 -1
- package/src/local-dev/fleet-logs.js.map +0 -1
- package/src/local-dev/fleet-process.js.map +0 -1
- package/src/local-dev/fleet-runtime.js.map +0 -1
- package/src/local-dev/fleet-session.js.map +0 -1
- package/src/local-dev/fleet-slice-runner-daemon.js.map +0 -1
- package/src/local-dev/fleet-startup.js.map +0 -1
- package/src/local-dev/fleet-state.js.map +0 -1
- package/src/local-dev/fleet-supervisor-daemon.js.map +0 -1
- package/src/local-dev/fleet-supervisor.js.map +0 -1
- package/src/local-dev/gateway-daemon.js.map +0 -1
- package/src/local-dev/gateway.js.map +0 -1
- package/src/local-dev/identity.js.map +0 -1
- package/src/local-dev/index.js.map +0 -1
- package/src/local-dev/local-context.js.map +0 -1
- package/src/local-dev/nx-workspace.js.map +0 -1
- package/src/local-dev/personal-secret-ledger.js.map +0 -1
- package/src/local-dev/personal-stage.js.map +0 -1
- package/src/local-dev/portless.js.map +0 -1
- package/src/local-dev/ports.js.map +0 -1
- package/src/local-dev/private-state.js.map +0 -1
- package/src/local-dev/process-identity.js.map +0 -1
- package/src/local-dev/proxy.js.map +0 -1
- package/src/local-dev/runtime-paths.js.map +0 -1
- package/src/local-dev/secret-slice-claim.js.map +0 -1
- package/src/local-dev/slice-claim.js.map +0 -1
- package/src/local-dev/slice-runner.js.map +0 -1
- package/src/local-dev/tls.js.map +0 -1
- package/src/ownership/composition.js.map +0 -1
- package/src/ownership/foundation-record.js.map +0 -1
- package/src/ownership/hash.js.map +0 -1
- package/src/ownership/index.js.map +0 -1
- package/src/ownership/json-ownership.js.map +0 -1
- package/src/ownership/record.js.map +0 -1
- package/src/ownership/workspace-record.js.map +0 -1
- package/src/performance/cost-collection.js.map +0 -1
- package/src/performance/cost-controls.js.map +0 -1
- package/src/performance/evidence.js.map +0 -1
- package/src/performance/index.js.map +0 -1
- package/src/performance/load-safety.js.map +0 -1
- package/src/performance/model.js.map +0 -1
- package/src/performance/pr-artifact-budget.js.map +0 -1
- package/src/performance/runner.js.map +0 -1
- package/src/performance/scenario-endpoint.js.map +0 -1
- package/src/performance/scenario.js.map +0 -1
- package/src/performance/test-fixtures.d.ts +0 -7
- package/src/performance/test-fixtures.js +0 -169
- package/src/performance/test-fixtures.js.map +0 -1
- package/src/performance/unit-cost.js.map +0 -1
- package/src/release/index.js.map +0 -1
- package/src/release/lifecycle.js.map +0 -1
- package/src/release/manifest.js.map +0 -1
- package/src/release/rollout.js.map +0 -1
- package/src/release/source-authority.js.map +0 -1
- package/src/repository-policy/aurora-preflight.js.map +0 -1
- package/src/repository-policy/deployment-plan.js.map +0 -1
- package/src/repository-policy/foundation-profile.js.map +0 -1
- package/src/repository-policy/index.js.map +0 -1
- package/src/repository-policy/policy.js.map +0 -1
- package/src/secrets/adapters.js.map +0 -1
- package/src/secrets/aws-control-plane.js.map +0 -1
- package/src/secrets/declaration-verification.js.map +0 -1
- package/src/secrets/guardrails.js.map +0 -1
- package/src/secrets/index.js.map +0 -1
- package/src/secrets/metadata.js.map +0 -1
- package/src/secrets/model.js.map +0 -1
- package/src/secrets/operator.js.map +0 -1
- package/src/secrets/permissions.js.map +0 -1
- package/src/secrets/personal-command.js.map +0 -1
- package/src/secrets/personal-ledger.js.map +0 -1
- package/src/secrets/registry.js.map +0 -1
- package/src/secrets/verification.js.map +0 -1
- package/src/secrets/workload-access.js.map +0 -1
- package/src/supply-chain/action-pins.js.map +0 -1
- package/src/supply-chain/blueprint-trust-git.js.map +0 -1
- package/src/supply-chain/blueprint-trust-process.js.map +0 -1
- package/src/supply-chain/blueprint-trust.js.map +0 -1
- package/src/supply-chain/blueprint-verification-authority.js.map +0 -1
- package/src/supply-chain/blueprint-verification-execution.js.map +0 -1
- package/src/supply-chain/build-output.js.map +0 -1
- package/src/supply-chain/common.js.map +0 -1
- package/src/supply-chain/container-artifact-contract.js.map +0 -1
- package/src/supply-chain/container-release.js.map +0 -1
- package/src/supply-chain/ecr-applicability.js.map +0 -1
- package/src/supply-chain/ecr-scanning.js.map +0 -1
- package/src/supply-chain/ecs-deployed-image.js.map +0 -1
- package/src/supply-chain/errors.js.map +0 -1
- package/src/supply-chain/generated-blueprint-trust-command.js.map +0 -1
- package/src/supply-chain/generated-command.js.map +0 -1
- package/src/supply-chain/generated-container-release-command.js.map +0 -1
- package/src/supply-chain/generated-ecr-collector-command.js.map +0 -1
- package/src/supply-chain/generated-files.js.map +0 -1
- package/src/supply-chain/generated-scanner-command.js.map +0 -1
- package/src/supply-chain/index.js.map +0 -1
- package/src/supply-chain/inventory.js.map +0 -1
- package/src/supply-chain/license-expression.js.map +0 -1
- package/src/supply-chain/pnpm-toolchain-authority.js.map +0 -1
- package/src/supply-chain/policy.js.map +0 -1
- package/src/supply-chain/publication.js.map +0 -1
- package/src/supply-chain/sbom.js.map +0 -1
- package/src/supply-chain/scanner.js.map +0 -1
- package/src/supply-chain/vendored-source.js.map +0 -1
- package/src/testing-evidence/fixture-scope.js.map +0 -1
- package/src/testing-evidence/index.js.map +0 -1
- package/src/testing-evidence/quarantine.js.map +0 -1
- package/src/testing-evidence/result.js.map +0 -1
- package/src/testing-evidence/runner-adapter.js.map +0 -1
- package/src/testing-evidence/sensitive-artifacts.js.map +0 -1
- package/src/validation.js.map +0 -1
- package/src/work-markers/index.js.map +0 -1
- package/src/work-markers/work-markers.js.map +0 -1
- package/src/workload-roles/account-baseline.js.map +0 -1
- package/src/workload-roles/authority.js.map +0 -1
- package/src/workload-roles/aws-adapter.js.map +0 -1
- package/src/workload-roles/aws-internal-handler.js.map +0 -1
- package/src/workload-roles/aws-s3-state.js.map +0 -1
- package/src/workload-roles/aws-sdk-control-plane.js.map +0 -1
- package/src/workload-roles/boundary.js.map +0 -1
- package/src/workload-roles/convergence.js.map +0 -1
- package/src/workload-roles/effective-policy.js.map +0 -1
- package/src/workload-roles/handler.js.map +0 -1
- package/src/workload-roles/history.js.map +0 -1
- package/src/workload-roles/index.js.map +0 -1
- package/src/workload-roles/injection.js.map +0 -1
- package/src/workload-roles/manifest.js.map +0 -1
- package/src/workload-roles/provisioner.js.map +0 -1
- package/src/workload-roles/registration.js.map +0 -1
- package/src/workload-roles/stage-boundary.js.map +0 -1
- package/src/workload-roles/standard-slice.js.map +0 -1
|
@@ -0,0 +1,1229 @@
|
|
|
1
|
+
"use strict";
|
|
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 node_https_1 = require("node:https");
|
|
9
|
+
const client_wafv2_1 = require("@aws-sdk/client-wafv2");
|
|
10
|
+
const credential_providers_1 = require("@aws-sdk/credential-providers");
|
|
11
|
+
const node_crypto_1 = require("node:crypto");
|
|
12
|
+
const node_fs_1 = require("node:fs");
|
|
13
|
+
const node_path_1 = require("node:path");
|
|
14
|
+
const devkit_1 = require("@nx/devkit");
|
|
15
|
+
const index_js_1 = require("../../foundation/index.js");
|
|
16
|
+
const router_waf_rate_rehearsal_js_1 = require("../../foundation/router-waf-rate-rehearsal.js");
|
|
17
|
+
const index_js_2 = require("../../aws-authority/index.js");
|
|
18
|
+
const private_smoke_access_js_1 = require("../../foundation/private-smoke-access.js");
|
|
19
|
+
const identity_js_1 = require("../../aws/identity.js");
|
|
20
|
+
const index_js_3 = require("../../repository-policy/index.js");
|
|
21
|
+
const deterministic_order_js_1 = require("../../deterministic-order.js");
|
|
22
|
+
const index_js_4 = require("../../workspace-deployment/index.js");
|
|
23
|
+
const MAX_ROUTER_WAF_EVIDENCE_BYTES = 32 * 1024;
|
|
24
|
+
const FOUNDATION_ROOT = (0, node_path_1.join)('packages', 'foundation');
|
|
25
|
+
const POLICY_PATH = (0, node_path_1.join)('security', 'router-waf.json');
|
|
26
|
+
const EVIDENCE_PATH = (0, node_path_1.join)('security', 'router-waf-live-evidence.json');
|
|
27
|
+
const RECEIPT_PATH = (0, node_path_1.join)('security', 'router-waf-blocking-rehearsal.json');
|
|
28
|
+
const GLOBAL_WAF_REGION = 'us-east-1';
|
|
29
|
+
const REQUEST_TIMEOUT_MS = 15_000;
|
|
30
|
+
const RATE_MARKER_HEADER = 'x-ebk-waf-rate-rehearsal';
|
|
31
|
+
const OPERATOR_MARKER_HEADER = 'x-ebk-waf-rehearsal';
|
|
32
|
+
const RATE_MARKER_NONCE_PATTERN = /^[a-f0-9]{64}$/u;
|
|
33
|
+
const SLICE_HEALTH_PATH = /^\/[a-z0-9]+(?:-[a-z0-9]+)*\/api\/health$/u;
|
|
34
|
+
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;
|
|
35
|
+
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;
|
|
36
|
+
const WORKSPACE_PATTERN = /^[a-z0-9][a-z0-9-]{0,127}$/u;
|
|
37
|
+
const MAX_ROUTER_WAF_SEED_AGE_MS = 4 * 60 * 60 * 1_000;
|
|
38
|
+
function readBoundedJson(path, label) {
|
|
39
|
+
const before = (0, node_fs_1.lstatSync)(path, { bigint: true });
|
|
40
|
+
if (!before.isFile() || before.nlink !== 1n) {
|
|
41
|
+
throw new Error(`${label} must be one unaliased regular file.`);
|
|
42
|
+
}
|
|
43
|
+
if (before.size > BigInt(MAX_ROUTER_WAF_EVIDENCE_BYTES)) {
|
|
44
|
+
throw new Error(`${label} exceeds the bounded evidence size.`);
|
|
45
|
+
}
|
|
46
|
+
const bytes = (0, node_fs_1.readFileSync)(path);
|
|
47
|
+
const after = (0, node_fs_1.lstatSync)(path, { bigint: true });
|
|
48
|
+
if (before.dev !== after.dev ||
|
|
49
|
+
before.ino !== after.ino ||
|
|
50
|
+
before.mode !== after.mode ||
|
|
51
|
+
before.size !== after.size ||
|
|
52
|
+
before.mtimeNs !== after.mtimeNs ||
|
|
53
|
+
before.ctimeNs !== after.ctimeNs) {
|
|
54
|
+
throw new Error(`${label} changed while it was being read.`);
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
return JSON.parse(bytes.toString('utf8'));
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
throw new Error(`${label} is not valid JSON.`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function writeBoundedJson(path, value, label) {
|
|
64
|
+
const existing = (0, node_fs_1.lstatSync)(path, { bigint: true });
|
|
65
|
+
if (!existing.isFile() || existing.nlink !== 1n) {
|
|
66
|
+
throw new Error(`${label} must be one unaliased regular file.`);
|
|
67
|
+
}
|
|
68
|
+
const bytes = `${JSON.stringify(value, null, 2)}\n`;
|
|
69
|
+
if (Buffer.byteLength(bytes) > MAX_ROUTER_WAF_EVIDENCE_BYTES) {
|
|
70
|
+
throw new Error(`${label} exceeds its bound.`);
|
|
71
|
+
}
|
|
72
|
+
const temporary = `${path}.tmp-${process.pid}`;
|
|
73
|
+
let descriptor;
|
|
74
|
+
let cleanupError;
|
|
75
|
+
try {
|
|
76
|
+
descriptor = (0, node_fs_1.openSync)(temporary, 'wx', Number(existing.mode & 511n));
|
|
77
|
+
(0, node_fs_1.writeFileSync)(descriptor, bytes, 'utf8');
|
|
78
|
+
(0, node_fs_1.closeSync)(descriptor);
|
|
79
|
+
descriptor = undefined;
|
|
80
|
+
(0, node_fs_1.renameSync)(temporary, path);
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
if (descriptor !== undefined)
|
|
84
|
+
(0, node_fs_1.closeSync)(descriptor);
|
|
85
|
+
try {
|
|
86
|
+
(0, node_fs_1.unlinkSync)(temporary);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
if (error.code !== 'ENOENT') {
|
|
90
|
+
cleanupError = error;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (cleanupError)
|
|
95
|
+
throw cleanupError;
|
|
96
|
+
}
|
|
97
|
+
function partitionForRegion(region) {
|
|
98
|
+
return region.startsWith('cn-')
|
|
99
|
+
? 'aws-cn'
|
|
100
|
+
: region.startsWith('us-gov-')
|
|
101
|
+
? 'aws-us-gov'
|
|
102
|
+
: 'aws';
|
|
103
|
+
}
|
|
104
|
+
function selectedProfile(options, environment) {
|
|
105
|
+
const explicit = options.profile?.trim();
|
|
106
|
+
const internal = environment.AWS_PROFILE?.trim();
|
|
107
|
+
if (explicit && internal && explicit !== internal) {
|
|
108
|
+
throw new Error('Router WAF rehearsal explicit profile differs from the invocation AWS_PROFILE.');
|
|
109
|
+
}
|
|
110
|
+
const profile = explicit || internal || undefined;
|
|
111
|
+
if (environment.GITHUB_ACTIONS === 'true') {
|
|
112
|
+
if (profile) {
|
|
113
|
+
throw new Error('Protected GitHub Router WAF rehearsal must use its configured OIDC credentials, not an AWS profile.');
|
|
114
|
+
}
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
if (!profile) {
|
|
118
|
+
throw new Error('Local Router WAF rehearsal requires the explicit or invocation-private AWS_PROFILE.');
|
|
119
|
+
}
|
|
120
|
+
return profile;
|
|
121
|
+
}
|
|
122
|
+
function markerDigest(value) {
|
|
123
|
+
return `sha256:${(0, node_crypto_1.createHash)('sha256').update(value, 'utf8').digest('hex')}`;
|
|
124
|
+
}
|
|
125
|
+
function rateMarker(nonce, sequence) {
|
|
126
|
+
return `${nonce}:${sequence}`;
|
|
127
|
+
}
|
|
128
|
+
function normalizedProbeUrl(value) {
|
|
129
|
+
let url;
|
|
130
|
+
try {
|
|
131
|
+
url = new URL(value);
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
throw new Error('Router WAF rate probe URL must be one absolute URL.');
|
|
135
|
+
}
|
|
136
|
+
if (url.protocol !== 'https:' ||
|
|
137
|
+
url.username !== '' ||
|
|
138
|
+
url.password !== '' ||
|
|
139
|
+
url.hash !== '' ||
|
|
140
|
+
url.search !== '' ||
|
|
141
|
+
url.port !== '' ||
|
|
142
|
+
!SLICE_HEALTH_PATH.test(url.pathname)) {
|
|
143
|
+
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.');
|
|
144
|
+
}
|
|
145
|
+
return url;
|
|
146
|
+
}
|
|
147
|
+
function clients(profile, region, dependencies) {
|
|
148
|
+
const credentials = profile ? (0, credential_providers_1.fromIni)({ profile }) : undefined;
|
|
149
|
+
const common = credentials ? { credentials } : {};
|
|
150
|
+
return {
|
|
151
|
+
cloudFront: (dependencies.createCloudFrontClient ??
|
|
152
|
+
((configuration) => new client_cloudfront_1.CloudFrontClient(configuration)))({
|
|
153
|
+
...common,
|
|
154
|
+
region: GLOBAL_WAF_REGION,
|
|
155
|
+
}),
|
|
156
|
+
cloudWatch: (dependencies.createCloudWatchClient ??
|
|
157
|
+
((configuration) => new client_cloudwatch_1.CloudWatchClient(configuration)))({
|
|
158
|
+
...common,
|
|
159
|
+
region: GLOBAL_WAF_REGION,
|
|
160
|
+
}),
|
|
161
|
+
cloudWatchLogs: (dependencies.createCloudWatchLogsClient ??
|
|
162
|
+
((configuration) => new client_cloudwatch_logs_1.CloudWatchLogsClient(configuration)))({
|
|
163
|
+
...common,
|
|
164
|
+
region: GLOBAL_WAF_REGION,
|
|
165
|
+
}),
|
|
166
|
+
sts: (dependencies.createStsClient ??
|
|
167
|
+
((configuration) => new client_sts_1.STSClient(configuration)))({
|
|
168
|
+
...common,
|
|
169
|
+
endpoint: (0, identity_js_1.canonicalStsEndpoint)(region),
|
|
170
|
+
region,
|
|
171
|
+
}),
|
|
172
|
+
waf: (dependencies.createWafClient ??
|
|
173
|
+
((configuration) => new client_wafv2_1.WAFV2Client(configuration)))({
|
|
174
|
+
...common,
|
|
175
|
+
region: GLOBAL_WAF_REGION,
|
|
176
|
+
}),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
async function attestCloudFrontDistribution(input) {
|
|
180
|
+
const expectedArn = `arn:${input.partition}:cloudfront::${input.accountId}:distribution/${input.distributionId}`;
|
|
181
|
+
const [result, tagResult] = await Promise.all([
|
|
182
|
+
input.cloudFront.send(new client_cloudfront_1.GetDistributionCommand({ Id: input.distributionId })),
|
|
183
|
+
input.cloudFront.send(new client_cloudfront_1.ListTagsForResourceCommand({ Resource: expectedArn })),
|
|
184
|
+
]);
|
|
185
|
+
const distribution = result.Distribution;
|
|
186
|
+
const configuration = distribution?.DistributionConfig;
|
|
187
|
+
const aliases = configuration?.Aliases?.Items ?? [];
|
|
188
|
+
if (distribution?.ARN !== expectedArn ||
|
|
189
|
+
distribution?.Id !== input.distributionId ||
|
|
190
|
+
distribution.Status !== 'Deployed' ||
|
|
191
|
+
configuration?.Staging !== false ||
|
|
192
|
+
!distribution.DomainName ||
|
|
193
|
+
!configuration ||
|
|
194
|
+
configuration.Enabled !== true ||
|
|
195
|
+
configuration.WebACLId !== input.webAclArn ||
|
|
196
|
+
(input.probeHost !== distribution.DomainName &&
|
|
197
|
+
!aliases.includes(input.probeHost))) {
|
|
198
|
+
throw new Error('Router WAF rehearsal probe is not bound to the exact enabled deployed CloudFront distribution, Web ACL, DomainName, or Alias.');
|
|
199
|
+
}
|
|
200
|
+
const tags = new Map((tagResult.Tags?.Items ?? []).flatMap((tag) => tag.Key && tag.Value ? [[tag.Key, tag.Value]] : []));
|
|
201
|
+
if (tags.get('ebk:managed-by') !== 'empire-builder-kit' ||
|
|
202
|
+
tags.get('ebk:owner') !== 'foundation' ||
|
|
203
|
+
tags.get('ebk:stage') !== input.stage ||
|
|
204
|
+
tags.get('ebk:workspace') !== input.workspace) {
|
|
205
|
+
throw new Error('Live CloudFront distribution lacks the exact generated workspace, stage, owner, or managed identity tags.');
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
function liveRecord(value, label) {
|
|
209
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
210
|
+
throw new Error(`${label} is not one object.`);
|
|
211
|
+
}
|
|
212
|
+
return value;
|
|
213
|
+
}
|
|
214
|
+
function exactLiveKeys(value, expected, label) {
|
|
215
|
+
const observed = Object.entries(value)
|
|
216
|
+
.filter(([, item]) => item !== undefined)
|
|
217
|
+
.map(([key]) => key)
|
|
218
|
+
.sort();
|
|
219
|
+
const required = [...expected].sort();
|
|
220
|
+
if (JSON.stringify(observed) !== JSON.stringify(required)) {
|
|
221
|
+
throw new Error(`${label} has an unexpected live shape.`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
function emptyLiveObject(value, label) {
|
|
225
|
+
const candidate = liveRecord(value, label);
|
|
226
|
+
exactLiveKeys(candidate, [], label);
|
|
227
|
+
}
|
|
228
|
+
function canonicalLive(value) {
|
|
229
|
+
if (Array.isArray(value)) {
|
|
230
|
+
return `[${value.map(canonicalLive).join(',')}]`;
|
|
231
|
+
}
|
|
232
|
+
if (value && typeof value === 'object') {
|
|
233
|
+
return `{${Object.entries(value)
|
|
234
|
+
.sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0))
|
|
235
|
+
.map(([key, item]) => `${JSON.stringify(key)}:${canonicalLive(item)}`)
|
|
236
|
+
.join(',')}}`;
|
|
237
|
+
}
|
|
238
|
+
const encoded = JSON.stringify(value);
|
|
239
|
+
if (encoded === undefined) {
|
|
240
|
+
throw new Error('Router WAF live contract contains an unsupported value.');
|
|
241
|
+
}
|
|
242
|
+
return encoded;
|
|
243
|
+
}
|
|
244
|
+
function canonicalRulesForComparison(rules) {
|
|
245
|
+
const normalized = structuredClone(rules);
|
|
246
|
+
const allowlist = normalized.find(({ name }) => name === 'StageIpAllowlist');
|
|
247
|
+
const statement = allowlist?.statement;
|
|
248
|
+
const references = statement?.notStatement?.statement?.orStatement?.statements;
|
|
249
|
+
if (references) {
|
|
250
|
+
references.sort((left, right) => (0, deterministic_order_js_1.compareUtf16)(canonicalLive(left), canonicalLive(right)));
|
|
251
|
+
}
|
|
252
|
+
return canonicalLive(normalized);
|
|
253
|
+
}
|
|
254
|
+
function normalizedLiveVisibility(value, expectedMetricName, label) {
|
|
255
|
+
const candidate = liveRecord(value, `${label} visibility`);
|
|
256
|
+
exactLiveKeys(candidate, ['CloudWatchMetricsEnabled', 'MetricName', 'SampledRequestsEnabled'], `${label} visibility`);
|
|
257
|
+
if (candidate.CloudWatchMetricsEnabled !== true ||
|
|
258
|
+
candidate.SampledRequestsEnabled !== true ||
|
|
259
|
+
candidate.MetricName !== expectedMetricName) {
|
|
260
|
+
throw new Error(`${label} visibility differs from the generated plan.`);
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
cloudwatchMetricsEnabled: true,
|
|
264
|
+
metricName: expectedMetricName,
|
|
265
|
+
sampledRequestsEnabled: true,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
function normalizedIpSetReference(value, label) {
|
|
269
|
+
const candidate = liveRecord(value, label);
|
|
270
|
+
exactLiveKeys(candidate, ['ARN'], label);
|
|
271
|
+
if (typeof candidate.ARN !== 'string') {
|
|
272
|
+
throw new Error(`${label} ARN is invalid.`);
|
|
273
|
+
}
|
|
274
|
+
return {
|
|
275
|
+
arn: candidate.ARN,
|
|
276
|
+
normalized: { ipSetReferenceStatement: { arn: candidate.ARN } },
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
function normalizedLiveRules(input) {
|
|
280
|
+
const prefix = `Ebk${input.stage[0].toUpperCase()}${input.stage.slice(1)}`;
|
|
281
|
+
const expectedNames = [
|
|
282
|
+
'StageIpAllowlist',
|
|
283
|
+
router_waf_rate_rehearsal_js_1.ROUTER_WAF_RATE_RULE_NAME,
|
|
284
|
+
'AWSManagedRulesCommonRuleSet',
|
|
285
|
+
'AWSManagedRulesKnownBadInputsRuleSet',
|
|
286
|
+
'AWSManagedRulesAmazonIpReputationList',
|
|
287
|
+
];
|
|
288
|
+
if (input.rules.length !== expectedNames.length ||
|
|
289
|
+
input.rules.some((rule, index) => liveRecord(rule, `Router WAF rule ${index}`).Name !==
|
|
290
|
+
expectedNames[index])) {
|
|
291
|
+
throw new Error('Live Router WAF ordered rule inventory differs from the generated blocking plan.');
|
|
292
|
+
}
|
|
293
|
+
const ipSetArns = [];
|
|
294
|
+
const rules = input.rules.map((value, index) => {
|
|
295
|
+
const name = expectedNames[index] ?? '';
|
|
296
|
+
const rule = liveRecord(value, `Router WAF rule ${name}`);
|
|
297
|
+
const managed = name.startsWith('AWSManagedRules');
|
|
298
|
+
exactLiveKeys(rule, managed
|
|
299
|
+
? [
|
|
300
|
+
'Name',
|
|
301
|
+
'OverrideAction',
|
|
302
|
+
'Priority',
|
|
303
|
+
'Statement',
|
|
304
|
+
'VisibilityConfig',
|
|
305
|
+
]
|
|
306
|
+
: ['Action', 'Name', 'Priority', 'Statement', 'VisibilityConfig'], `Router WAF rule ${name}`);
|
|
307
|
+
if (rule.Name !== name || rule.Priority !== index) {
|
|
308
|
+
throw new Error(`Live Router WAF rule ${name} priority differs from the generated plan.`);
|
|
309
|
+
}
|
|
310
|
+
const visibilityConfig = normalizedLiveVisibility(rule.VisibilityConfig, `${prefix}${name}`, `Router WAF rule ${name}`);
|
|
311
|
+
const statement = liveRecord(rule.Statement, `Router WAF rule ${name} statement`);
|
|
312
|
+
if (name === 'StageIpAllowlist') {
|
|
313
|
+
const action = liveRecord(rule.Action, 'StageIpAllowlist action');
|
|
314
|
+
exactLiveKeys(action, ['Block'], 'StageIpAllowlist action');
|
|
315
|
+
emptyLiveObject(action.Block, 'StageIpAllowlist Block action');
|
|
316
|
+
exactLiveKeys(statement, ['NotStatement'], 'StageIpAllowlist statement');
|
|
317
|
+
const notStatement = liveRecord(statement.NotStatement, 'StageIpAllowlist not statement');
|
|
318
|
+
exactLiveKeys(notStatement, ['Statement'], 'StageIpAllowlist not statement');
|
|
319
|
+
const nested = liveRecord(notStatement.Statement, 'StageIpAllowlist nested statement');
|
|
320
|
+
let normalizedNested;
|
|
321
|
+
if ('IPSetReferenceStatement' in nested) {
|
|
322
|
+
exactLiveKeys(nested, ['IPSetReferenceStatement'], 'StageIpAllowlist nested statement');
|
|
323
|
+
const reference = normalizedIpSetReference(nested.IPSetReferenceStatement, 'StageIpAllowlist IP set reference');
|
|
324
|
+
ipSetArns.push(reference.arn);
|
|
325
|
+
normalizedNested = reference.normalized;
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
exactLiveKeys(nested, ['OrStatement'], 'StageIpAllowlist nested statement');
|
|
329
|
+
const orStatement = liveRecord(nested.OrStatement, 'StageIpAllowlist OR statement');
|
|
330
|
+
exactLiveKeys(orStatement, ['Statements'], 'StageIpAllowlist OR statement');
|
|
331
|
+
if (!Array.isArray(orStatement.Statements) ||
|
|
332
|
+
orStatement.Statements.length < 2 ||
|
|
333
|
+
orStatement.Statements.length > 3) {
|
|
334
|
+
throw new Error('StageIpAllowlist OR statement must contain the exact generated baseline and smoke references.');
|
|
335
|
+
}
|
|
336
|
+
const references = orStatement.Statements.map((item, referenceIndex) => {
|
|
337
|
+
const wrapper = liveRecord(item, `StageIpAllowlist reference ${referenceIndex}`);
|
|
338
|
+
exactLiveKeys(wrapper, ['IPSetReferenceStatement'], `StageIpAllowlist reference ${referenceIndex}`);
|
|
339
|
+
const reference = normalizedIpSetReference(wrapper.IPSetReferenceStatement, `StageIpAllowlist reference ${referenceIndex}`);
|
|
340
|
+
ipSetArns.push(reference.arn);
|
|
341
|
+
return reference.normalized;
|
|
342
|
+
});
|
|
343
|
+
normalizedNested = { orStatement: { statements: references } };
|
|
344
|
+
}
|
|
345
|
+
return {
|
|
346
|
+
action: { block: {} },
|
|
347
|
+
name,
|
|
348
|
+
priority: index,
|
|
349
|
+
statement: {
|
|
350
|
+
notStatement: { statement: normalizedNested },
|
|
351
|
+
},
|
|
352
|
+
visibilityConfig,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
if (name === router_waf_rate_rehearsal_js_1.ROUTER_WAF_RATE_RULE_NAME) {
|
|
356
|
+
const action = liveRecord(rule.Action, 'RateLimitPerIP action');
|
|
357
|
+
exactLiveKeys(action, ['Block'], 'RateLimitPerIP action');
|
|
358
|
+
emptyLiveObject(action.Block, 'RateLimitPerIP Block action');
|
|
359
|
+
exactLiveKeys(statement, ['RateBasedStatement'], 'RateLimitPerIP statement');
|
|
360
|
+
const rate = liveRecord(statement.RateBasedStatement, 'RateLimitPerIP rate statement');
|
|
361
|
+
const rateKeys = Object.entries(rate)
|
|
362
|
+
.filter(([, item]) => item !== undefined)
|
|
363
|
+
.map(([key]) => key)
|
|
364
|
+
.sort();
|
|
365
|
+
if (JSON.stringify(rateKeys) !==
|
|
366
|
+
JSON.stringify([
|
|
367
|
+
'AggregateKeyType',
|
|
368
|
+
...(rate.EvaluationWindowSec === undefined
|
|
369
|
+
? []
|
|
370
|
+
: ['EvaluationWindowSec']),
|
|
371
|
+
'Limit',
|
|
372
|
+
].sort()) ||
|
|
373
|
+
rate.AggregateKeyType !== 'IP' ||
|
|
374
|
+
typeof rate.Limit !== 'number' ||
|
|
375
|
+
(rate.EvaluationWindowSec !== undefined &&
|
|
376
|
+
rate.EvaluationWindowSec !== 300)) {
|
|
377
|
+
throw new Error('Live RateLimitPerIP contains an unexpected scope-down, forwarded-IP, custom-key, evaluation-window, or limit shape.');
|
|
378
|
+
}
|
|
379
|
+
return {
|
|
380
|
+
action: { block: {} },
|
|
381
|
+
name,
|
|
382
|
+
priority: index,
|
|
383
|
+
statement: {
|
|
384
|
+
rateBasedStatement: {
|
|
385
|
+
aggregateKeyType: 'IP',
|
|
386
|
+
limit: rate.Limit,
|
|
387
|
+
},
|
|
388
|
+
},
|
|
389
|
+
visibilityConfig,
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
const overrideAction = liveRecord(rule.OverrideAction, `Router WAF rule ${name} override action`);
|
|
393
|
+
exactLiveKeys(overrideAction, ['None'], `Router WAF rule ${name} override action`);
|
|
394
|
+
emptyLiveObject(overrideAction.None, `Router WAF rule ${name} None action`);
|
|
395
|
+
exactLiveKeys(statement, ['ManagedRuleGroupStatement'], `Router WAF rule ${name} statement`);
|
|
396
|
+
const managedStatement = liveRecord(statement.ManagedRuleGroupStatement, `Router WAF rule ${name} managed statement`);
|
|
397
|
+
const common = name === 'AWSManagedRulesCommonRuleSet';
|
|
398
|
+
exactLiveKeys(managedStatement, common
|
|
399
|
+
? ['Name', 'RuleActionOverrides', 'VendorName']
|
|
400
|
+
: ['Name', 'VendorName'], `Router WAF rule ${name} managed statement`);
|
|
401
|
+
if (managedStatement.Name !== name ||
|
|
402
|
+
managedStatement.VendorName !== 'AWS') {
|
|
403
|
+
throw new Error(`Live Router WAF rule ${name} managed group differs from the generated plan.`);
|
|
404
|
+
}
|
|
405
|
+
let ruleActionOverrides;
|
|
406
|
+
if (common) {
|
|
407
|
+
if (!Array.isArray(managedStatement.RuleActionOverrides) ||
|
|
408
|
+
managedStatement.RuleActionOverrides.length !== 1) {
|
|
409
|
+
throw new Error('AWSManagedRulesCommonRuleSet must retain the exact SizeRestrictions_BODY Count override.');
|
|
410
|
+
}
|
|
411
|
+
const override = liveRecord(managedStatement.RuleActionOverrides[0], 'SizeRestrictions_BODY override');
|
|
412
|
+
exactLiveKeys(override, ['ActionToUse', 'Name'], 'SizeRestrictions_BODY override');
|
|
413
|
+
const actionToUse = liveRecord(override.ActionToUse, 'SizeRestrictions_BODY action');
|
|
414
|
+
exactLiveKeys(actionToUse, ['Count'], 'SizeRestrictions_BODY action');
|
|
415
|
+
emptyLiveObject(actionToUse.Count, 'SizeRestrictions_BODY Count action');
|
|
416
|
+
if (override.Name !== 'SizeRestrictions_BODY') {
|
|
417
|
+
throw new Error('AWSManagedRulesCommonRuleSet override targets an unexpected rule.');
|
|
418
|
+
}
|
|
419
|
+
ruleActionOverrides = [
|
|
420
|
+
{
|
|
421
|
+
actionToUse: { count: {} },
|
|
422
|
+
name: 'SizeRestrictions_BODY',
|
|
423
|
+
},
|
|
424
|
+
];
|
|
425
|
+
}
|
|
426
|
+
return {
|
|
427
|
+
name,
|
|
428
|
+
overrideAction: { none: {} },
|
|
429
|
+
priority: index,
|
|
430
|
+
statement: {
|
|
431
|
+
managedRuleGroupStatement: {
|
|
432
|
+
name,
|
|
433
|
+
...(common ? { ruleActionOverrides } : {}),
|
|
434
|
+
vendorName: 'AWS',
|
|
435
|
+
},
|
|
436
|
+
},
|
|
437
|
+
visibilityConfig,
|
|
438
|
+
};
|
|
439
|
+
});
|
|
440
|
+
return { ipSetArns, rules };
|
|
441
|
+
}
|
|
442
|
+
function requiredIdentityTags(input) {
|
|
443
|
+
return {
|
|
444
|
+
'ebk:managed-by': 'empire-builder-kit',
|
|
445
|
+
'ebk:owner': 'foundation',
|
|
446
|
+
'ebk:policy-digest': input.policyDigest,
|
|
447
|
+
'ebk:stage': input.stage,
|
|
448
|
+
'ebk:workspace': input.workspace,
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
function attestWafTags(tagsResult, resourceArn, expected, label) {
|
|
452
|
+
const result = liveRecord(tagsResult, `${label} tag response`);
|
|
453
|
+
const tagInfo = liveRecord(result.TagInfoForResource, `${label} tag information`);
|
|
454
|
+
if (tagInfo.ResourceARN !== resourceArn || !Array.isArray(tagInfo.TagList)) {
|
|
455
|
+
throw new Error(`${label} tag response does not identify the exact ARN.`);
|
|
456
|
+
}
|
|
457
|
+
const tags = new Map(tagInfo.TagList.flatMap((value) => {
|
|
458
|
+
const tag = liveRecord(value, `${label} tag`);
|
|
459
|
+
return typeof tag.Key === 'string' && typeof tag.Value === 'string'
|
|
460
|
+
? [[tag.Key, tag.Value]]
|
|
461
|
+
: [];
|
|
462
|
+
}));
|
|
463
|
+
if (Object.entries(expected).some(([key, value]) => tags.get(key) !== value)) {
|
|
464
|
+
throw new Error(`${label} lacks the exact generated identity tags.`);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
async function attestIpSet(input) {
|
|
468
|
+
const arn = WAF_IP_SET_ARN.exec(input.arn);
|
|
469
|
+
if (!arn || arn[2] !== input.accountId) {
|
|
470
|
+
throw new Error('Router WAF IP set ARN has the wrong account or scope.');
|
|
471
|
+
}
|
|
472
|
+
const name = arn[3] ?? '';
|
|
473
|
+
const id = arn[4] ?? '';
|
|
474
|
+
const smoke = name ===
|
|
475
|
+
`foundation-${input.expectedTags['ebk:stage']}-router-smoke-access`;
|
|
476
|
+
const [ipSetResult, tagsResult] = await Promise.all([
|
|
477
|
+
input.waf.send(new client_wafv2_1.GetIPSetCommand({ Id: id, Name: name, Scope: 'CLOUDFRONT' })),
|
|
478
|
+
input.waf.send(new client_wafv2_1.ListTagsForResourceCommand({ ResourceARN: input.arn })),
|
|
479
|
+
]);
|
|
480
|
+
const ipSet = ipSetResult.IPSet;
|
|
481
|
+
const expectedCidrs = smoke
|
|
482
|
+
? input.admittedCidr
|
|
483
|
+
? [input.admittedCidr]
|
|
484
|
+
: []
|
|
485
|
+
: ipSet?.IPAddressVersion === 'IPV4'
|
|
486
|
+
? input.expectedIpv4Cidrs
|
|
487
|
+
: input.expectedIpv6Cidrs;
|
|
488
|
+
if (ipSet?.ARN !== input.arn ||
|
|
489
|
+
ipSet.Id !== id ||
|
|
490
|
+
ipSet.Name !== name ||
|
|
491
|
+
(smoke && ipSet.IPAddressVersion !== 'IPV4') ||
|
|
492
|
+
(ipSet.IPAddressVersion !== 'IPV4' && ipSet.IPAddressVersion !== 'IPV6') ||
|
|
493
|
+
JSON.stringify([...(ipSet.Addresses ?? [])].sort()) !==
|
|
494
|
+
JSON.stringify([...expectedCidrs].sort())) {
|
|
495
|
+
throw new Error('Live Router WAF IP set identity, address family, or exact CIDRs differ from the generated plan.');
|
|
496
|
+
}
|
|
497
|
+
const expectedTags = smoke
|
|
498
|
+
? {
|
|
499
|
+
...Object.fromEntries(Object.entries(input.expectedTags).filter(([key]) => key !== 'ebk:policy-digest')),
|
|
500
|
+
'ebk:purpose': 'private-smoke-access',
|
|
501
|
+
}
|
|
502
|
+
: input.expectedTags;
|
|
503
|
+
attestWafTags(tagsResult, input.arn, expectedTags, 'Router WAF IP set');
|
|
504
|
+
return smoke ? 'SMOKE' : ipSet.IPAddressVersion;
|
|
505
|
+
}
|
|
506
|
+
async function attestLoggingConfiguration(input) {
|
|
507
|
+
let configuration;
|
|
508
|
+
try {
|
|
509
|
+
const result = await input.waf.send(new client_wafv2_1.GetLoggingConfigurationCommand({ ResourceArn: input.webAclArn }));
|
|
510
|
+
configuration = result.LoggingConfiguration;
|
|
511
|
+
}
|
|
512
|
+
catch (error) {
|
|
513
|
+
if (input.plan.logging === false &&
|
|
514
|
+
error instanceof Error &&
|
|
515
|
+
error.name === 'WAFNonexistentItemException') {
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
throw error;
|
|
519
|
+
}
|
|
520
|
+
if (input.plan.logging === false) {
|
|
521
|
+
if (configuration !== undefined) {
|
|
522
|
+
throw new Error('Router WAF logging exists even though committed policy disables it.');
|
|
523
|
+
}
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
const live = liveRecord(configuration, 'Router WAF logging configuration');
|
|
527
|
+
if (live.ResourceArn !== input.webAclArn ||
|
|
528
|
+
!Array.isArray(live.LogDestinationConfigs) ||
|
|
529
|
+
live.LogDestinationConfigs.length !== 1 ||
|
|
530
|
+
typeof live.LogDestinationConfigs[0] !== 'string' ||
|
|
531
|
+
!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]) ||
|
|
532
|
+
live.ManagedByFirewallManager !== false ||
|
|
533
|
+
live.LogScope !== 'CUSTOMER' ||
|
|
534
|
+
live.LogType !== 'WAF_LOGS') {
|
|
535
|
+
throw new Error('Router WAF logging destination differs from the generated blocking log contract.');
|
|
536
|
+
}
|
|
537
|
+
const destinationArn = live.LogDestinationConfigs[0];
|
|
538
|
+
const logGroupName = destinationArn.slice(destinationArn.indexOf(':log-group:') + 11);
|
|
539
|
+
const logGroups = await input.cloudWatchLogs.send(new client_cloudwatch_logs_1.DescribeLogGroupsCommand({
|
|
540
|
+
includeLinkedAccounts: false,
|
|
541
|
+
logGroupNamePrefix: logGroupName,
|
|
542
|
+
}));
|
|
543
|
+
const exactLogGroups = (logGroups.logGroups ?? []).filter(({ logGroupName: candidate }) => candidate === logGroupName);
|
|
544
|
+
if (exactLogGroups.length !== 1 ||
|
|
545
|
+
exactLogGroups[0]?.logGroupArn !== destinationArn ||
|
|
546
|
+
exactLogGroups[0]?.arn !== `${destinationArn}:*` ||
|
|
547
|
+
exactLogGroups[0]?.logGroupClass !== 'STANDARD' ||
|
|
548
|
+
exactLogGroups[0]?.retentionInDays !==
|
|
549
|
+
({ '1 day': 1, '3 days': 3, '5 days': 5, '1 week': 7, '2 weeks': 14 }[input.plan.logging.retention] ?? -1)) {
|
|
550
|
+
throw new Error('Router WAF CloudWatch Logs destination identity, class, or retention differs from committed policy.');
|
|
551
|
+
}
|
|
552
|
+
const expectedRedactions = [
|
|
553
|
+
{ queryString: {} },
|
|
554
|
+
{ singleHeader: { name: 'authorization' } },
|
|
555
|
+
{ singleHeader: { name: 'cookie' } },
|
|
556
|
+
{ singleHeader: { name: 'x-api-key' } },
|
|
557
|
+
];
|
|
558
|
+
const redactions = Array.isArray(live.RedactedFields)
|
|
559
|
+
? live.RedactedFields.map((value) => {
|
|
560
|
+
const field = liveRecord(value, 'Router WAF redacted field');
|
|
561
|
+
if ('QueryString' in field) {
|
|
562
|
+
exactLiveKeys(field, ['QueryString'], 'Router WAF query redaction');
|
|
563
|
+
emptyLiveObject(field.QueryString, 'Router WAF query redaction');
|
|
564
|
+
return { queryString: {} };
|
|
565
|
+
}
|
|
566
|
+
exactLiveKeys(field, ['SingleHeader'], 'Router WAF header redaction');
|
|
567
|
+
const header = liveRecord(field.SingleHeader, 'Router WAF header redaction');
|
|
568
|
+
exactLiveKeys(header, ['Name'], 'Router WAF header redaction');
|
|
569
|
+
return { singleHeader: { name: header.Name } };
|
|
570
|
+
})
|
|
571
|
+
: [];
|
|
572
|
+
const filter = liveRecord(live.LoggingFilter, 'Router WAF logging filter');
|
|
573
|
+
exactLiveKeys(filter, ['DefaultBehavior', 'Filters'], 'Router WAF logging filter');
|
|
574
|
+
if (canonicalLive([...redactions].sort((left, right) => (0, deterministic_order_js_1.compareUtf16)(canonicalLive(left), canonicalLive(right)))) !==
|
|
575
|
+
canonicalLive([...expectedRedactions].sort((left, right) => (0, deterministic_order_js_1.compareUtf16)(canonicalLive(left), canonicalLive(right)))) ||
|
|
576
|
+
filter.DefaultBehavior !== 'DROP' ||
|
|
577
|
+
!Array.isArray(filter.Filters) ||
|
|
578
|
+
filter.Filters.length !== 1) {
|
|
579
|
+
throw new Error('Router WAF logging redaction or blocked-only filter differs from committed policy.');
|
|
580
|
+
}
|
|
581
|
+
const retainedFilter = liveRecord(filter.Filters[0], 'Router WAF log filter');
|
|
582
|
+
exactLiveKeys(retainedFilter, ['Behavior', 'Conditions', 'Requirement'], 'Router WAF log filter');
|
|
583
|
+
if (retainedFilter.Behavior !== 'KEEP' ||
|
|
584
|
+
retainedFilter.Requirement !== 'MEETS_ANY' ||
|
|
585
|
+
!Array.isArray(retainedFilter.Conditions) ||
|
|
586
|
+
retainedFilter.Conditions.length !== 1 ||
|
|
587
|
+
canonicalLive(retainedFilter.Conditions) !==
|
|
588
|
+
canonicalLive([{ ActionCondition: { Action: 'BLOCK' } }])) {
|
|
589
|
+
throw new Error('Router WAF logging filter does not retain only blocked requests.');
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
async function attestRouterWafBaseline(input) {
|
|
593
|
+
const arn = WAF_ARN.exec(input.webAclArn);
|
|
594
|
+
if (!arn || arn[2] !== input.accountId) {
|
|
595
|
+
throw new Error('Router WAF rehearsal Web ACL ARN does not match the committed account and global scope.');
|
|
596
|
+
}
|
|
597
|
+
const name = arn[3] ?? '';
|
|
598
|
+
const id = arn[4] ?? '';
|
|
599
|
+
const [webAclResult, tagsResult] = await Promise.all([
|
|
600
|
+
input.waf.send(new client_wafv2_1.GetWebACLCommand({ Id: id, Name: name, Scope: 'CLOUDFRONT' })),
|
|
601
|
+
input.waf.send(new client_wafv2_1.ListTagsForResourceCommand({ ResourceARN: input.webAclArn })),
|
|
602
|
+
]);
|
|
603
|
+
const webAcl = webAclResult.WebACL;
|
|
604
|
+
if (webAcl?.ARN !== input.webAclArn ||
|
|
605
|
+
webAcl.Id !== id ||
|
|
606
|
+
webAcl.Name !== name ||
|
|
607
|
+
!webAcl.DefaultAction?.Allow ||
|
|
608
|
+
Object.keys(webAcl.DefaultAction).some((key) => key !== 'Allow') ||
|
|
609
|
+
webAcl.VisibilityConfig?.CloudWatchMetricsEnabled !== true ||
|
|
610
|
+
webAcl.VisibilityConfig.SampledRequestsEnabled !== true ||
|
|
611
|
+
webAcl.VisibilityConfig.MetricName !== input.webAclMetricName) {
|
|
612
|
+
throw new Error('Live Router WAF identity, default action, or Web ACL visibility differs from the generated plan.');
|
|
613
|
+
}
|
|
614
|
+
emptyLiveObject(webAcl.DefaultAction.Allow, 'Router WAF Web ACL default Allow action');
|
|
615
|
+
const expectedTags = requiredIdentityTags(input);
|
|
616
|
+
attestWafTags(tagsResult, input.webAclArn, expectedTags, 'Router WAF Web ACL');
|
|
617
|
+
const normalized = normalizedLiveRules({
|
|
618
|
+
rules: webAcl.Rules ?? [],
|
|
619
|
+
stage: input.stage,
|
|
620
|
+
});
|
|
621
|
+
const expectedIpSetCount = Number(input.plan.ipv4Cidrs.length > 0) +
|
|
622
|
+
Number(input.plan.ipv6Cidrs.length > 0) +
|
|
623
|
+
1;
|
|
624
|
+
if (normalized.ipSetArns.length !== expectedIpSetCount) {
|
|
625
|
+
throw new Error('Live StageIpAllowlist has an unexpected number of generated IP set references.');
|
|
626
|
+
}
|
|
627
|
+
if (new Set(normalized.ipSetArns).size !== normalized.ipSetArns.length) {
|
|
628
|
+
throw new Error('Live StageIpAllowlist reuses an IP set instead of the exact generated address-family inventory.');
|
|
629
|
+
}
|
|
630
|
+
const ipSetResults = await Promise.all(normalized.ipSetArns.map(async (ipSetArn) => {
|
|
631
|
+
const version = await attestIpSet({
|
|
632
|
+
accountId: input.accountId,
|
|
633
|
+
arn: ipSetArn,
|
|
634
|
+
...(input.admittedCidr ? { admittedCidr: input.admittedCidr } : {}),
|
|
635
|
+
// Admission owns only the separate smoke set. Baselines stay exact.
|
|
636
|
+
expectedIpv4Cidrs: input.plan.ipv4Cidrs,
|
|
637
|
+
expectedIpv6Cidrs: input.plan.ipv6Cidrs,
|
|
638
|
+
expectedTags,
|
|
639
|
+
waf: input.waf,
|
|
640
|
+
});
|
|
641
|
+
return { arn: ipSetArn, version };
|
|
642
|
+
}));
|
|
643
|
+
const ipv4Arn = ipSetResults.find(({ version }) => version === 'IPV4')?.arn;
|
|
644
|
+
const ipv6Arn = ipSetResults.find(({ version }) => version === 'IPV6')?.arn;
|
|
645
|
+
const smokeArn = ipSetResults.find(({ version }) => version === 'SMOKE')?.arn;
|
|
646
|
+
if (!smokeArn ||
|
|
647
|
+
Boolean(ipv4Arn) !== Boolean(input.plan.ipv4Cidrs.length) ||
|
|
648
|
+
Boolean(ipv6Arn) !== Boolean(input.plan.ipv6Cidrs.length)) {
|
|
649
|
+
throw new Error('Live Router WAF IP set address families differ from the generated plan.');
|
|
650
|
+
}
|
|
651
|
+
const expectedRules = (0, index_js_1.createRouterWafRules)(input.plan, {
|
|
652
|
+
smokeArn,
|
|
653
|
+
...(ipv4Arn ? { ipv4Arn } : {}),
|
|
654
|
+
...(ipv6Arn ? { ipv6Arn } : {}),
|
|
655
|
+
});
|
|
656
|
+
if (canonicalRulesForComparison(normalized.rules) !==
|
|
657
|
+
canonicalRulesForComparison(expectedRules)) {
|
|
658
|
+
throw new Error('Live Router WAF ordered rules differ from the exact generated blocking plan.');
|
|
659
|
+
}
|
|
660
|
+
await attestLoggingConfiguration({
|
|
661
|
+
accountId: input.accountId,
|
|
662
|
+
cloudWatchLogs: input.cloudWatchLogs,
|
|
663
|
+
plan: input.plan,
|
|
664
|
+
waf: input.waf,
|
|
665
|
+
webAclArn: input.webAclArn,
|
|
666
|
+
});
|
|
667
|
+
return { ipSetArns: normalized.ipSetArns, name };
|
|
668
|
+
}
|
|
669
|
+
function matchesManagedSampledRule(input) {
|
|
670
|
+
if (!input.group || !input.subrule)
|
|
671
|
+
return false;
|
|
672
|
+
return (input.observed === input.subrule ||
|
|
673
|
+
input.observed === `AWS#${input.group}#${input.subrule}`);
|
|
674
|
+
}
|
|
675
|
+
async function attestOperatorBlock(input) {
|
|
676
|
+
const [managedGroup, managedSubrule] = input.evidence.ruleName.split('#');
|
|
677
|
+
const metricName = input.type === 'nonallowlisted'
|
|
678
|
+
? `Ebk${input.stage[0].toUpperCase()}${input.stage.slice(1)}StageIpAllowlist`
|
|
679
|
+
: `Ebk${input.stage[0].toUpperCase()}${input.stage.slice(1)}${managedGroup}`;
|
|
680
|
+
const blockedAt = new Date(input.evidence.blockedAt);
|
|
681
|
+
const sampled = await input.waf.send(new client_wafv2_1.GetSampledRequestsCommand({
|
|
682
|
+
MaxItems: 500,
|
|
683
|
+
RuleMetricName: metricName,
|
|
684
|
+
Scope: 'CLOUDFRONT',
|
|
685
|
+
TimeWindow: {
|
|
686
|
+
EndTime: new Date(blockedAt.getTime() + 60_000),
|
|
687
|
+
StartTime: new Date(blockedAt.getTime() - 60_000),
|
|
688
|
+
},
|
|
689
|
+
WebAclArn: input.webAclArn,
|
|
690
|
+
}));
|
|
691
|
+
const matches = (sampled.SampledRequests ?? []).filter((sample) => {
|
|
692
|
+
const headers = sample.Request?.Headers ?? [];
|
|
693
|
+
const marker = headers.find(({ Name }) => Name?.toLowerCase() === OPERATOR_MARKER_HEADER)?.Value;
|
|
694
|
+
const timestamp = sample.Timestamp?.getTime();
|
|
695
|
+
return (sample.Action === 'BLOCK' &&
|
|
696
|
+
sample.Request?.Method === input.evidence.method &&
|
|
697
|
+
sample.Request.URI === input.evidence.path &&
|
|
698
|
+
marker === input.evidence.marker &&
|
|
699
|
+
timestamp !== undefined &&
|
|
700
|
+
Math.abs(timestamp - blockedAt.getTime()) <= 60_000 &&
|
|
701
|
+
(input.type === 'nonallowlisted'
|
|
702
|
+
? sample.RuleNameWithinRuleGroup === undefined
|
|
703
|
+
: matchesManagedSampledRule({
|
|
704
|
+
group: managedGroup,
|
|
705
|
+
observed: sample.RuleNameWithinRuleGroup,
|
|
706
|
+
subrule: managedSubrule,
|
|
707
|
+
})));
|
|
708
|
+
});
|
|
709
|
+
if (matches.length !== 1) {
|
|
710
|
+
throw new Error(`Router WAF ${input.type} operator evidence does not correlate to one exact sampled BLOCK request.`);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
async function observeRateLimit(input) {
|
|
714
|
+
const [sampled, metric] = await Promise.all([
|
|
715
|
+
input.waf.send(new client_wafv2_1.GetSampledRequestsCommand({
|
|
716
|
+
MaxItems: 500,
|
|
717
|
+
RuleMetricName: input.ruleMetricName,
|
|
718
|
+
Scope: 'CLOUDFRONT',
|
|
719
|
+
TimeWindow: {
|
|
720
|
+
EndTime: input.endTime,
|
|
721
|
+
StartTime: input.startTime,
|
|
722
|
+
},
|
|
723
|
+
WebAclArn: input.webAclArn,
|
|
724
|
+
})),
|
|
725
|
+
input.cloudWatch.send(new client_cloudwatch_1.GetMetricDataCommand({
|
|
726
|
+
EndTime: input.endTime,
|
|
727
|
+
MetricDataQueries: [
|
|
728
|
+
{
|
|
729
|
+
Id: 'rateblocked',
|
|
730
|
+
MetricStat: {
|
|
731
|
+
Metric: {
|
|
732
|
+
Dimensions: [
|
|
733
|
+
{ Name: 'Rule', Value: input.ruleMetricName },
|
|
734
|
+
{ Name: 'WebACL', Value: input.webAclMetricName },
|
|
735
|
+
],
|
|
736
|
+
MetricName: 'BlockedRequests',
|
|
737
|
+
Namespace: 'AWS/WAFV2',
|
|
738
|
+
},
|
|
739
|
+
Period: 60,
|
|
740
|
+
Stat: 'Sum',
|
|
741
|
+
},
|
|
742
|
+
ReturnData: true,
|
|
743
|
+
},
|
|
744
|
+
],
|
|
745
|
+
ScanBy: 'TimestampDescending',
|
|
746
|
+
StartTime: input.startTime,
|
|
747
|
+
})),
|
|
748
|
+
]);
|
|
749
|
+
const result = metric.MetricDataResults?.find((candidate) => candidate.Id === 'rateblocked');
|
|
750
|
+
const metrics = (result?.Timestamps ?? []).flatMap((timestamp, index) => {
|
|
751
|
+
const value = result?.Values?.[index];
|
|
752
|
+
return timestamp && typeof value === 'number'
|
|
753
|
+
? [
|
|
754
|
+
{
|
|
755
|
+
name: 'BlockedRequests',
|
|
756
|
+
namespace: 'AWS/WAFV2',
|
|
757
|
+
ruleMetricName: input.ruleMetricName,
|
|
758
|
+
timestamp,
|
|
759
|
+
value,
|
|
760
|
+
webAclMetricName: input.webAclMetricName,
|
|
761
|
+
},
|
|
762
|
+
]
|
|
763
|
+
: [];
|
|
764
|
+
});
|
|
765
|
+
const samples = (sampled.SampledRequests ?? []).flatMap((sample) => {
|
|
766
|
+
const markerHeader = sample.Request?.Headers?.find((header) => header.Name?.toLowerCase() === RATE_MARKER_HEADER &&
|
|
767
|
+
header.Value === input.marker);
|
|
768
|
+
return sample.Timestamp &&
|
|
769
|
+
typeof sample.Action === 'string' &&
|
|
770
|
+
sample.Request?.Method === 'GET' &&
|
|
771
|
+
sample.Request.URI === input.probePath &&
|
|
772
|
+
markerHeader
|
|
773
|
+
? [
|
|
774
|
+
{
|
|
775
|
+
action: sample.Action,
|
|
776
|
+
markerDigest: input.markerDigest,
|
|
777
|
+
method: 'GET',
|
|
778
|
+
ruleMetricName: input.ruleMetricName,
|
|
779
|
+
ruleName: router_waf_rate_rehearsal_js_1.ROUTER_WAF_RATE_RULE_NAME,
|
|
780
|
+
timestamp: sample.Timestamp,
|
|
781
|
+
uri: input.probePath,
|
|
782
|
+
},
|
|
783
|
+
]
|
|
784
|
+
: [];
|
|
785
|
+
});
|
|
786
|
+
return { metrics, samples };
|
|
787
|
+
}
|
|
788
|
+
function mergeRateEvidence(evidence, allowedRequestStatus, rateLimit, completedAt) {
|
|
789
|
+
const candidate = evidence;
|
|
790
|
+
const probes = candidate.probes;
|
|
791
|
+
return {
|
|
792
|
+
...candidate,
|
|
793
|
+
completedAt,
|
|
794
|
+
probes: { ...probes, allowedRequestStatus, rateLimit },
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
const SEED_ATTESTATION_RETRIES = 12;
|
|
798
|
+
const SEED_ATTESTATION_INTERVAL_MS = 10_000;
|
|
799
|
+
function rawGet(url, headers) {
|
|
800
|
+
return new Promise((resolve, reject) => {
|
|
801
|
+
const request = (0, node_https_1.request)(url, { headers, method: 'GET', timeout: REQUEST_TIMEOUT_MS }, (response) => {
|
|
802
|
+
response.resume();
|
|
803
|
+
resolve({ status: response.statusCode ?? 0 });
|
|
804
|
+
});
|
|
805
|
+
request.on('timeout', () => request.destroy(new Error('timeout')));
|
|
806
|
+
request.on('error', reject);
|
|
807
|
+
request.end();
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
async function attestOperatorBlocks(input) {
|
|
811
|
+
// AWS WAF publishes sampled requests with a short delay; a seeded run
|
|
812
|
+
// probed moments ago, so its attestation waits a bounded number of times.
|
|
813
|
+
for (let attempt = 0;; attempt += 1) {
|
|
814
|
+
try {
|
|
815
|
+
await Promise.all([
|
|
816
|
+
attestOperatorBlock({
|
|
817
|
+
evidence: input.seed.probes
|
|
818
|
+
.managedRule,
|
|
819
|
+
stage: input.stage,
|
|
820
|
+
type: 'managed',
|
|
821
|
+
waf: input.waf,
|
|
822
|
+
webAclArn: input.seed.webAclArn,
|
|
823
|
+
}),
|
|
824
|
+
attestOperatorBlock({
|
|
825
|
+
evidence: input.seed.probes
|
|
826
|
+
.nonAllowlisted,
|
|
827
|
+
stage: input.stage,
|
|
828
|
+
type: 'nonallowlisted',
|
|
829
|
+
waf: input.waf,
|
|
830
|
+
webAclArn: input.seed.webAclArn,
|
|
831
|
+
}),
|
|
832
|
+
]);
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
catch (error) {
|
|
836
|
+
if (attempt >= input.retries)
|
|
837
|
+
throw error;
|
|
838
|
+
await input.sleep(SEED_ATTESTATION_INTERVAL_MS);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
async function discoverRouterDistribution(input) {
|
|
843
|
+
const items = [];
|
|
844
|
+
let marker;
|
|
845
|
+
const seen = new Set();
|
|
846
|
+
do {
|
|
847
|
+
const page = await input.cloudFront.send(new client_cloudfront_1.ListDistributionsCommand({ Marker: marker }));
|
|
848
|
+
items.push(...(page.DistributionList?.Items ?? []));
|
|
849
|
+
marker = page.DistributionList?.NextMarker;
|
|
850
|
+
if (marker && seen.has(marker)) {
|
|
851
|
+
throw new Error('Router inventory pagination did not advance.');
|
|
852
|
+
}
|
|
853
|
+
if (marker)
|
|
854
|
+
seen.add(marker);
|
|
855
|
+
if (seen.size > 100) {
|
|
856
|
+
throw new Error('Router inventory exceeds bounded inspection.');
|
|
857
|
+
}
|
|
858
|
+
} while (marker);
|
|
859
|
+
const routers = items.filter((router) => router.DomainName === input.probeHost ||
|
|
860
|
+
router.Aliases?.Items?.includes(input.probeHost));
|
|
861
|
+
const router = routers[0];
|
|
862
|
+
if (routers.length !== 1 ||
|
|
863
|
+
!router?.Enabled ||
|
|
864
|
+
typeof router.Id !== 'string' ||
|
|
865
|
+
router.ARN !==
|
|
866
|
+
`arn:${input.partition}:cloudfront::${input.accountId}:distribution/${router.Id}` ||
|
|
867
|
+
typeof router.WebACLId !== 'string' ||
|
|
868
|
+
!WAF_ARN.test(router.WebACLId) ||
|
|
869
|
+
WAF_ARN.exec(router.WebACLId)?.[2] !== input.accountId) {
|
|
870
|
+
throw new Error('Managed rehearsal probe host does not resolve to one exact enabled Router distribution with an account-owned Web ACL.');
|
|
871
|
+
}
|
|
872
|
+
return { distributionId: router.Id, webAclArn: router.WebACLId };
|
|
873
|
+
}
|
|
874
|
+
function createRouterWafRehearsalExecutor(dependencies = {}) {
|
|
875
|
+
return async (options, context) => {
|
|
876
|
+
try {
|
|
877
|
+
const environment = dependencies.environment ?? process.env;
|
|
878
|
+
const root = (0, node_path_1.join)(context.root, FOUNDATION_ROOT);
|
|
879
|
+
const policy = (0, index_js_1.normalizeRouterWafPolicy)(readBoundedJson((0, node_path_1.join)(root, POLICY_PATH), 'Router WAF policy'));
|
|
880
|
+
const workspaceManifest = liveRecord(readBoundedJson((0, node_path_1.join)(context.root, 'package.json'), 'Workspace manifest'), 'Workspace manifest');
|
|
881
|
+
const workspace = workspaceManifest.name;
|
|
882
|
+
if (typeof workspace !== 'string' || !WORKSPACE_PATTERN.test(workspace)) {
|
|
883
|
+
throw new Error('Router WAF rehearsal workspace must match the exact generated root package name.');
|
|
884
|
+
}
|
|
885
|
+
const operatingPolicy = (0, index_js_3.readRepositoryOperatingPolicy)(context.root);
|
|
886
|
+
if (operatingPolicy.deploymentProfile !== policy.deploymentProfile) {
|
|
887
|
+
throw new Error('Router WAF policy deployment profile differs from the committed repository policy.');
|
|
888
|
+
}
|
|
889
|
+
const stage = (0, index_js_3.productionRehearsalStage)(operatingPolicy.deploymentProfile);
|
|
890
|
+
const accountId = (0, index_js_3.configuredDeploymentAccountForStage)(operatingPolicy.deploymentProfile, operatingPolicy.deploymentAccounts, stage);
|
|
891
|
+
const region = (0, index_js_3.configuredDeploymentRegion)(operatingPolicy.deploymentRegion);
|
|
892
|
+
for (const candidate of [
|
|
893
|
+
environment.AWS_REGION?.trim(),
|
|
894
|
+
environment.AWS_DEFAULT_REGION?.trim(),
|
|
895
|
+
]) {
|
|
896
|
+
if (candidate && candidate !== region) {
|
|
897
|
+
throw new Error(`Router WAF rehearsal ambient Region ${candidate} differs from committed deployment Region ${region}.`);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
const partition = partitionForRegion(region);
|
|
901
|
+
const expectedRoleArn = (0, index_js_2.createStageAuthorityIdentity)({
|
|
902
|
+
accountId,
|
|
903
|
+
partition,
|
|
904
|
+
region,
|
|
905
|
+
stage,
|
|
906
|
+
}).deploymentRoleArn;
|
|
907
|
+
const wafContractDigest = (0, index_js_1.routerWafContractDigest)(policy);
|
|
908
|
+
const plan = (0, index_js_1.createRouterWafStagePlan)({
|
|
909
|
+
policy,
|
|
910
|
+
rehearsal: null,
|
|
911
|
+
stage,
|
|
912
|
+
});
|
|
913
|
+
const evidencePath = (0, node_path_1.join)(root, EVIDENCE_PATH);
|
|
914
|
+
const url = normalizedProbeUrl(options.probeUrl);
|
|
915
|
+
const profile = selectedProfile(options, environment);
|
|
916
|
+
const aws = clients(profile, region, dependencies);
|
|
917
|
+
const seedProbes = options.seedProbes === true;
|
|
918
|
+
const sleep = dependencies.sleep ??
|
|
919
|
+
((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)));
|
|
920
|
+
const receiptPath = (0, node_path_1.join)(root, RECEIPT_PATH);
|
|
921
|
+
const retainSealedRehearsal = (sealed) => {
|
|
922
|
+
writeBoundedJson(evidencePath, sealed.evidence, 'Router WAF live evidence');
|
|
923
|
+
writeBoundedJson(receiptPath, sealed.receipt, 'Router WAF blocking rehearsal receipt');
|
|
924
|
+
devkit_1.logger.info(JSON.stringify({
|
|
925
|
+
evidenceDigest: sealed.receipt.evidenceDigest,
|
|
926
|
+
accountId: sealed.receipt.accountId,
|
|
927
|
+
policyDigest: sealed.receipt.policyDigest,
|
|
928
|
+
rateRequests: sealed.rateRequests,
|
|
929
|
+
region: sealed.receipt.region,
|
|
930
|
+
sealed: true,
|
|
931
|
+
stage: sealed.receipt.stage,
|
|
932
|
+
}));
|
|
933
|
+
};
|
|
934
|
+
// `defer` keeps every file write out of the seal: the managed rehearsal
|
|
935
|
+
// retains its receipt only after the admission is removed and the live
|
|
936
|
+
// allowlist reads back exactly as committed.
|
|
937
|
+
const seal = async (options = {}) => {
|
|
938
|
+
const retainedEvidence = options.seedEvidence ??
|
|
939
|
+
readBoundedJson(evidencePath, 'Router WAF live evidence');
|
|
940
|
+
const seed = (0, index_js_1.validateRouterWafLiveEvidenceSeed)({
|
|
941
|
+
evidence: retainedEvidence,
|
|
942
|
+
policy,
|
|
943
|
+
});
|
|
944
|
+
if (seed.stage !== stage ||
|
|
945
|
+
seed.accountId !== accountId ||
|
|
946
|
+
seed.region !== region ||
|
|
947
|
+
seed.deploymentRoleArn !== expectedRoleArn ||
|
|
948
|
+
seed.workspace !== workspace ||
|
|
949
|
+
seed.wafContractDigest !== wafContractDigest) {
|
|
950
|
+
throw new Error('Router WAF live evidence stage, account, Region, deployment role, workspace, or WAF contract differs from committed authority.');
|
|
951
|
+
}
|
|
952
|
+
const invocationStartedAt = (dependencies.now ?? (() => new Date()))();
|
|
953
|
+
if (!Number.isFinite(invocationStartedAt.getTime()) ||
|
|
954
|
+
seed.startedAt > invocationStartedAt.getTime() ||
|
|
955
|
+
seed.completedAt > invocationStartedAt.getTime() ||
|
|
956
|
+
invocationStartedAt.getTime() - seed.completedAt >
|
|
957
|
+
MAX_ROUTER_WAF_SEED_AGE_MS) {
|
|
958
|
+
throw new Error('Router WAF live evidence seed is stale or future-dated; no live request was made.');
|
|
959
|
+
}
|
|
960
|
+
const retainedReceipt = (0, index_js_1.normalizeRouterWafBlockingRehearsal)(readBoundedJson(receiptPath, 'Router WAF blocking rehearsal receipt'));
|
|
961
|
+
if (retainedReceipt) {
|
|
962
|
+
throw new Error('Router WAF blocking rehearsal receipt must be reset to its exact pending seed before another live probe.');
|
|
963
|
+
}
|
|
964
|
+
const identity = await aws.sts.send(new client_sts_1.GetCallerIdentityCommand({}));
|
|
965
|
+
if (identity.Account !== accountId ||
|
|
966
|
+
typeof identity.Arn !== 'string' ||
|
|
967
|
+
!(0, identity_js_1.matchesExpectedAssumedRoleArn)(accountId, identity.Arn, expectedRoleArn)) {
|
|
968
|
+
throw new Error('Router WAF rehearsal caller does not match the committed account and exact stage deployment role.');
|
|
969
|
+
}
|
|
970
|
+
await (dependencies.ownership ?? index_js_4.assertWorkspaceStageOwnershipForWorkspace)(context.root, stage, profile ? { profile } : {});
|
|
971
|
+
const webAclVisibilityMetricName = (0, router_waf_rate_rehearsal_js_1.routerWafWebAclMetricName)(stage);
|
|
972
|
+
const [, baselineAttestation] = await Promise.all([
|
|
973
|
+
attestCloudFrontDistribution({
|
|
974
|
+
accountId,
|
|
975
|
+
cloudFront: aws.cloudFront,
|
|
976
|
+
distributionId: seed.distributionId,
|
|
977
|
+
partition,
|
|
978
|
+
probeHost: url.hostname,
|
|
979
|
+
stage,
|
|
980
|
+
webAclArn: seed.webAclArn,
|
|
981
|
+
workspace,
|
|
982
|
+
}),
|
|
983
|
+
attestRouterWafBaseline({
|
|
984
|
+
accountId,
|
|
985
|
+
...(options.admittedCidr
|
|
986
|
+
? { admittedCidr: options.admittedCidr }
|
|
987
|
+
: {}),
|
|
988
|
+
cloudWatchLogs: aws.cloudWatchLogs,
|
|
989
|
+
plan,
|
|
990
|
+
policyDigest: (0, index_js_1.routerWafPolicyDigest)(policy),
|
|
991
|
+
stage,
|
|
992
|
+
waf: aws.waf,
|
|
993
|
+
webAclArn: seed.webAclArn,
|
|
994
|
+
webAclMetricName: webAclVisibilityMetricName,
|
|
995
|
+
workspace,
|
|
996
|
+
}),
|
|
997
|
+
]);
|
|
998
|
+
const webAclMetricName = baselineAttestation.name;
|
|
999
|
+
await attestOperatorBlocks({
|
|
1000
|
+
retries: seedProbes ? SEED_ATTESTATION_RETRIES : 0,
|
|
1001
|
+
seed,
|
|
1002
|
+
sleep,
|
|
1003
|
+
stage,
|
|
1004
|
+
waf: aws.waf,
|
|
1005
|
+
});
|
|
1006
|
+
const fetchRequest = dependencies.fetch ?? fetch;
|
|
1007
|
+
const baselineResponse = await fetchRequest(url, {
|
|
1008
|
+
cache: 'no-store',
|
|
1009
|
+
headers: { 'cache-control': 'no-cache' },
|
|
1010
|
+
redirect: 'manual',
|
|
1011
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
1012
|
+
});
|
|
1013
|
+
const allowedRequestStatus = baselineResponse.status;
|
|
1014
|
+
await baselineResponse.body?.cancel();
|
|
1015
|
+
if (allowedRequestStatus < 200 || allowedRequestStatus > 399) {
|
|
1016
|
+
throw new Error(`Router WAF rehearsal health preflight returned unexpected HTTP ${allowedRequestStatus}; rate traffic was not sent.`);
|
|
1017
|
+
}
|
|
1018
|
+
const nonce = (dependencies.createNonce ?? (() => (0, node_crypto_1.randomBytes)(32).toString('hex')))();
|
|
1019
|
+
if (!RATE_MARKER_NONCE_PATTERN.test(nonce)) {
|
|
1020
|
+
throw new Error('Router WAF rehearsal invocation nonce is not one opaque 256-bit lowercase hexadecimal value.');
|
|
1021
|
+
}
|
|
1022
|
+
const rateLimit = await (dependencies.runRateRehearsal ?? router_waf_rate_rehearsal_js_1.runRouterWafRateRehearsal)({
|
|
1023
|
+
dependencies: {
|
|
1024
|
+
...(dependencies.now ? { now: dependencies.now } : {}),
|
|
1025
|
+
observe: ({ endTime, markerDigest: expectedMarkerDigest, ruleMetricName, sequence, startTime, webAclMetricName: expectedWebAclMetricName, }) => {
|
|
1026
|
+
const marker = rateMarker(nonce, sequence);
|
|
1027
|
+
if (expectedWebAclMetricName !== webAclMetricName) {
|
|
1028
|
+
throw new Error('Router WAF rate observation metric dimension differs from the attested physical Web ACL name.');
|
|
1029
|
+
}
|
|
1030
|
+
if (markerDigest(marker) !== expectedMarkerDigest) {
|
|
1031
|
+
throw new Error('Router WAF rate observation marker differs from the exact blocked request.');
|
|
1032
|
+
}
|
|
1033
|
+
return observeRateLimit({
|
|
1034
|
+
cloudWatch: aws.cloudWatch,
|
|
1035
|
+
endTime,
|
|
1036
|
+
marker,
|
|
1037
|
+
markerDigest: expectedMarkerDigest,
|
|
1038
|
+
probePath: url.pathname,
|
|
1039
|
+
ruleMetricName,
|
|
1040
|
+
startTime,
|
|
1041
|
+
waf: aws.waf,
|
|
1042
|
+
webAclArn: seed.webAclArn,
|
|
1043
|
+
webAclMetricName: expectedWebAclMetricName,
|
|
1044
|
+
});
|
|
1045
|
+
},
|
|
1046
|
+
request: async (sequence) => {
|
|
1047
|
+
const marker = rateMarker(nonce, sequence);
|
|
1048
|
+
const response = await fetchRequest(url, {
|
|
1049
|
+
cache: 'no-store',
|
|
1050
|
+
headers: {
|
|
1051
|
+
'cache-control': 'no-cache',
|
|
1052
|
+
[RATE_MARKER_HEADER]: marker,
|
|
1053
|
+
},
|
|
1054
|
+
redirect: 'manual',
|
|
1055
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
1056
|
+
});
|
|
1057
|
+
await response.body?.cancel();
|
|
1058
|
+
return {
|
|
1059
|
+
markerDigest: markerDigest(marker),
|
|
1060
|
+
status: response.status,
|
|
1061
|
+
};
|
|
1062
|
+
},
|
|
1063
|
+
...(dependencies.sleep ? { sleep: dependencies.sleep } : {}),
|
|
1064
|
+
},
|
|
1065
|
+
rateLimitPerIp: policy.rateLimitPerIp,
|
|
1066
|
+
stage,
|
|
1067
|
+
webAclMetricName,
|
|
1068
|
+
});
|
|
1069
|
+
const completedAt = (dependencies.now ?? (() => new Date()))().toISOString();
|
|
1070
|
+
const evidence = mergeRateEvidence(retainedEvidence, allowedRequestStatus, rateLimit, completedAt);
|
|
1071
|
+
const receipt = (0, index_js_1.createRouterWafBlockingRehearsal)({ evidence, policy });
|
|
1072
|
+
const sealed = {
|
|
1073
|
+
evidence,
|
|
1074
|
+
expectedTags: requiredIdentityTags({
|
|
1075
|
+
policyDigest: (0, index_js_1.routerWafPolicyDigest)(policy),
|
|
1076
|
+
stage,
|
|
1077
|
+
workspace,
|
|
1078
|
+
}),
|
|
1079
|
+
ipSetArns: baselineAttestation.ipSetArns,
|
|
1080
|
+
rateRequests: rateLimit.requestsSent,
|
|
1081
|
+
receipt,
|
|
1082
|
+
};
|
|
1083
|
+
if (!options.defer)
|
|
1084
|
+
retainSealedRehearsal(sealed);
|
|
1085
|
+
return sealed;
|
|
1086
|
+
};
|
|
1087
|
+
if (!seedProbes) {
|
|
1088
|
+
await seal();
|
|
1089
|
+
return { success: true };
|
|
1090
|
+
}
|
|
1091
|
+
// Managed rehearsal: the protected dispatch performs the operator probes
|
|
1092
|
+
// itself. The non-allowlisted probe runs before the runner is admitted;
|
|
1093
|
+
// the managed-rule probe and the rest of the rehearsal run inside the
|
|
1094
|
+
// temporary allowlist admission that private smoke already governs.
|
|
1095
|
+
if (environment.GITHUB_ACTIONS !== 'true' || stage !== 'staging') {
|
|
1096
|
+
throw new Error('Managed Router WAF rehearsal seeding runs only from the protected GitHub staging dispatch.');
|
|
1097
|
+
}
|
|
1098
|
+
const pendingEvidence = readBoundedJson(evidencePath, 'Router WAF live evidence');
|
|
1099
|
+
if (pendingEvidence?.status !== 'pending') {
|
|
1100
|
+
throw new Error('Managed Router WAF rehearsal seeding requires the exact pending live evidence seed; reset both seeds before another rehearsal.');
|
|
1101
|
+
}
|
|
1102
|
+
const sourceCommit = environment.EBK_SOURCE_COMMIT?.trim() ||
|
|
1103
|
+
environment.GITHUB_SHA?.trim() ||
|
|
1104
|
+
'';
|
|
1105
|
+
if (!/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(sourceCommit)) {
|
|
1106
|
+
throw new Error('Managed Router WAF rehearsal seeding requires the exact protected source commit.');
|
|
1107
|
+
}
|
|
1108
|
+
const identity = await aws.sts.send(new client_sts_1.GetCallerIdentityCommand({}));
|
|
1109
|
+
if (identity.Account !== accountId ||
|
|
1110
|
+
typeof identity.Arn !== 'string' ||
|
|
1111
|
+
!(0, identity_js_1.matchesExpectedAssumedRoleArn)(accountId, identity.Arn, expectedRoleArn)) {
|
|
1112
|
+
throw new Error('Router WAF rehearsal caller does not match the committed account and exact stage deployment role.');
|
|
1113
|
+
}
|
|
1114
|
+
const discovered = await discoverRouterDistribution({
|
|
1115
|
+
accountId,
|
|
1116
|
+
cloudFront: aws.cloudFront,
|
|
1117
|
+
partition,
|
|
1118
|
+
probeHost: url.hostname,
|
|
1119
|
+
});
|
|
1120
|
+
const now = dependencies.now ?? (() => new Date());
|
|
1121
|
+
const get = dependencies.rawGet ?? rawGet;
|
|
1122
|
+
const nonce = (dependencies.createNonce ?? (() => (0, node_crypto_1.randomBytes)(32).toString('hex')))();
|
|
1123
|
+
if (!RATE_MARKER_NONCE_PATTERN.test(nonce)) {
|
|
1124
|
+
throw new Error('Router WAF rehearsal invocation nonce is not one opaque 256-bit lowercase hexadecimal value.');
|
|
1125
|
+
}
|
|
1126
|
+
const startedAt = now().toISOString();
|
|
1127
|
+
const operatorProbe = async (type, ruleName) => {
|
|
1128
|
+
const marker = `${nonce}:${type}`;
|
|
1129
|
+
const response = await get(url, {
|
|
1130
|
+
'cache-control': 'no-cache',
|
|
1131
|
+
[OPERATOR_MARKER_HEADER]: marker,
|
|
1132
|
+
});
|
|
1133
|
+
const blockedAt = now().toISOString();
|
|
1134
|
+
if (response.status !== 403) {
|
|
1135
|
+
throw new Error(`Managed Router WAF ${type} probe returned HTTP ${response.status} instead of the expected 403 BLOCK.`);
|
|
1136
|
+
}
|
|
1137
|
+
return {
|
|
1138
|
+
blockedAt,
|
|
1139
|
+
marker,
|
|
1140
|
+
method: 'GET',
|
|
1141
|
+
path: url.pathname,
|
|
1142
|
+
ruleName,
|
|
1143
|
+
status: 403,
|
|
1144
|
+
};
|
|
1145
|
+
};
|
|
1146
|
+
const nonAllowlisted = await operatorProbe('nonallowlisted', 'StageIpAllowlist');
|
|
1147
|
+
let sealed;
|
|
1148
|
+
await (dependencies.privateSmoke ?? private_smoke_access_js_1.runGithubPrivateSmoke)({
|
|
1149
|
+
account: accountId,
|
|
1150
|
+
deploymentProfile: operatingPolicy.deploymentProfile,
|
|
1151
|
+
root: context.root,
|
|
1152
|
+
run: async (lease) => {
|
|
1153
|
+
const managedRule = await operatorProbe('managed', 'AWSManagedRulesCommonRuleSet#NoUserAgent_HEADER');
|
|
1154
|
+
const seedEvidence = {
|
|
1155
|
+
accountId,
|
|
1156
|
+
completedAt: now().toISOString(),
|
|
1157
|
+
deploymentRoleArn: expectedRoleArn,
|
|
1158
|
+
distributionId: discovered.distributionId,
|
|
1159
|
+
kind: 'ebk-router-waf-live-evidence',
|
|
1160
|
+
mode: 'block',
|
|
1161
|
+
policyDigest: (0, index_js_1.routerWafPolicyDigest)(policy),
|
|
1162
|
+
probes: {
|
|
1163
|
+
allowedRequestStatus: 200,
|
|
1164
|
+
cloudWatchMetricsObserved: true,
|
|
1165
|
+
countModeTuningObserved: false,
|
|
1166
|
+
managedRule,
|
|
1167
|
+
nonAllowlisted,
|
|
1168
|
+
rateLimit: null,
|
|
1169
|
+
sampledRequestsObserved: true,
|
|
1170
|
+
},
|
|
1171
|
+
region,
|
|
1172
|
+
requiredRules: [
|
|
1173
|
+
'StageIpAllowlist',
|
|
1174
|
+
'RateLimitPerIP',
|
|
1175
|
+
...index_js_1.ROUTER_WAF_REQUIRED_MANAGED_RULES,
|
|
1176
|
+
],
|
|
1177
|
+
result: 'passed',
|
|
1178
|
+
schemaVersion: index_js_1.ROUTER_WAF_LIVE_EVIDENCE_SCHEMA_VERSION,
|
|
1179
|
+
sourceCommit,
|
|
1180
|
+
stage,
|
|
1181
|
+
startedAt,
|
|
1182
|
+
wafContractDigest,
|
|
1183
|
+
webAclArn: discovered.webAclArn,
|
|
1184
|
+
webAclScope: 'CLOUDFRONT',
|
|
1185
|
+
workspace,
|
|
1186
|
+
};
|
|
1187
|
+
sealed = await seal({
|
|
1188
|
+
...(lease ? { admittedCidr: lease.cidr } : {}),
|
|
1189
|
+
defer: true,
|
|
1190
|
+
seedEvidence: JSON.parse(JSON.stringify(seedEvidence)),
|
|
1191
|
+
});
|
|
1192
|
+
},
|
|
1193
|
+
source: sourceCommit,
|
|
1194
|
+
stage,
|
|
1195
|
+
url: url.href,
|
|
1196
|
+
workspace,
|
|
1197
|
+
}, {
|
|
1198
|
+
...(dependencies.environment
|
|
1199
|
+
? { environment: dependencies.environment }
|
|
1200
|
+
: {}),
|
|
1201
|
+
...(dependencies.fetch ? { fetch: dependencies.fetch } : {}),
|
|
1202
|
+
});
|
|
1203
|
+
if (!sealed) {
|
|
1204
|
+
throw new Error('Managed Router WAF rehearsal did not seal inside the admission.');
|
|
1205
|
+
}
|
|
1206
|
+
// The admission is removed and its revocation observed. Nothing is
|
|
1207
|
+
// retained until the live allowlist reads back exactly as committed, so
|
|
1208
|
+
// a cleanup failure never leaves a usable passed receipt.
|
|
1209
|
+
const restored = sealed;
|
|
1210
|
+
await Promise.all(restored.ipSetArns.map((arn) => attestIpSet({
|
|
1211
|
+
accountId,
|
|
1212
|
+
arn,
|
|
1213
|
+
expectedIpv4Cidrs: plan.ipv4Cidrs,
|
|
1214
|
+
expectedIpv6Cidrs: plan.ipv6Cidrs,
|
|
1215
|
+
expectedTags: restored.expectedTags,
|
|
1216
|
+
waf: aws.waf,
|
|
1217
|
+
})));
|
|
1218
|
+
retainSealedRehearsal(restored);
|
|
1219
|
+
return { success: true };
|
|
1220
|
+
}
|
|
1221
|
+
catch (error) {
|
|
1222
|
+
devkit_1.logger.error(error instanceof Error ? error.message : String(error));
|
|
1223
|
+
return { success: false };
|
|
1224
|
+
}
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1227
|
+
const executor = createRouterWafRehearsalExecutor();
|
|
1228
|
+
exports.default = executor;
|
|
1229
|
+
//# sourceMappingURL=router-waf-rehearsal.js.map
|