diff --git a/README.md b/README.md index 3b90afe..50e1bde 100644 --- a/README.md +++ b/README.md @@ -409,13 +409,14 @@ external-dev IAM guardrail SCP is 5,095 compact characters against its boundaries first.** The three retained boundaries are `shoc-frontend-new-{tf-poc,dev,staging}-deploy-boundary`. Each permits only bucket location/list/version reads, object get/put/current and version delete, -and invalidation create/read for one exact distribution. Dev is pinned to -`E2CWLM1AFB964P`; staging is pinned to `E2JDVEZ6EGD49J`. The tf-poc -distribution, OAC, function, hosted-zone, and certificate identifiers are -intentionally empty in `cdk.json`. They must come from the frontend shared -creator outputs; this substrate does not reuse the backend tf-poc zone or -certificate. Its site name is `frontend-tf-poc.seahaven.com`. While the -identifier set is empty, its boundary omits invalidation access and +`GetDistribution`/`GetDistributionConfig`, and invalidation create/read for +one exact distribution. Dev is pinned to `E2CWLM1AFB964P`; staging is pinned +to `E2JDVEZ6EGD49J`. The tf-poc distribution, OAC, function, hosted-zone, and +certificate identifiers are intentionally empty in `cdk.json`. They must come +from the frontend shared creator outputs; this substrate does not reuse the +backend tf-poc zone or certificate. Its site name is +`frontend-tf-poc.seahaven.com`. While the identifier set is empty, its +boundary omits distribution read and invalidation access and `ShouldManageShocFrontendPocRoles` remains false even if its role gate is mistakenly enabled. diff --git a/lib/terraform-substrate/shoc-frontend-resources.ts b/lib/terraform-substrate/shoc-frontend-resources.ts index 58b8dbf..098998b 100644 --- a/lib/terraform-substrate/shoc-frontend-resources.ts +++ b/lib/terraform-substrate/shoc-frontend-resources.ts @@ -640,6 +640,27 @@ export class ShocFrontendResources extends Construct { Resource: `${siteBucketArn}/*`, }, ]; + const wrapDistributionStatement = ( + statement: Record, + ): unknown => + environment.invalidationCondition === undefined + ? statement + : cdk.Fn.conditionIf( + environment.invalidationCondition.logicalId, + statement, + cdk.Aws.NO_VALUE, + ); + // GitHub verify and live-state summary call get-distribution. The identity + // policy already grants these; the boundary was the deny. + const readDistributionStatement = { + Sid: "ReadExactDistribution", + Effect: "Allow", + Action: [ + "cloudfront:GetDistribution", + "cloudfront:GetDistributionConfig", + ], + Resource: exactDistributionArn, + }; const invalidationStatement = { Sid: "InvalidateExactDistribution", Effect: "Allow", @@ -647,13 +668,8 @@ export class ShocFrontendResources extends Construct { Resource: exactDistributionArn, }; boundaryStatements.push( - environment.invalidationCondition === undefined - ? invalidationStatement - : cdk.Fn.conditionIf( - environment.invalidationCondition.logicalId, - invalidationStatement, - cdk.Aws.NO_VALUE, - ), + wrapDistributionStatement(readDistributionStatement), + wrapDistributionStatement(invalidationStatement), ); const deployBoundary = new iam.CfnManagedPolicy(