Skip to content

Commit c81e079

Browse files
isadeksbgagent
andauthored
fix(cdk): grant s3:PutBucketVersioning in bootstrap policy (#404) (#405)
The AttachmentsBucket (cdk/src/constructs/attachments-bucket.ts) sets `versioned: true`, so CloudFormation calls s3:PutBucketVersioning during creation. The S3ApplicationBuckets statement in the bootstrap observability policy grants CreateBucket, PutEncryptionConfiguration, PutLifecycleConfiguration, PutBucketPublicAccessBlock (+Get), PutBucketTagging (+Get), etc. — but not s3:PutBucketVersioning, so a fresh `mise //cdk:bootstrap` + deploy of current main rolls back with: cdk-hnb659fds-cfn-exec-role is not authorized to perform s3:PutBucketVersioning Same drift class as #402/#403: a stack feature whose CREATE-time action was never added to the scoped exec-role policy — invisible to existing stacks (the action only fires on first bucket creation), fatal to a from-scratch deploy. Audit of all three stack buckets (Attachments, TraceArtifacts, Screenshot) confirms versioning is the only gap; every other feature (encryption, lifecycle, SSL, public-access-block, tagging) already maps to a granted action. Add s3:PutBucketVersioning (create) and s3:GetBucketVersioning (read/drift), matching the existing Put/Get pairing for PublicAccessBlock and Tagging. Regenerate bootstrap artifacts and update DEPLOYMENT_ROLES.md golden (+ Starlight mirror) so golden-baseline parity passes. Add a regression guard in policies.test.ts asserting S3ApplicationBuckets carries an action for each bucket feature the stack enables. Fixes #404 Co-authored-by: bgagent <bgagent@noreply.github.com>
1 parent 0d9c7e2 commit c81e079

6 files changed

Lines changed: 42 additions & 0 deletions

File tree

cdk/bootstrap/bootstrap-template.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1225,6 +1225,8 @@ Resources:
12251225
- s3:GetBucketPublicAccessBlock
12261226
- s3:PutEncryptionConfiguration
12271227
- s3:PutLifecycleConfiguration
1228+
- s3:PutBucketVersioning
1229+
- s3:GetBucketVersioning
12281230
- s3:GetBucketLocation
12291231
- s3:ListBucket
12301232
- s3:PutBucketTagging

cdk/bootstrap/policies/observability.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@
9090
"s3:GetBucketPublicAccessBlock",
9191
"s3:PutEncryptionConfiguration",
9292
"s3:PutLifecycleConfiguration",
93+
"s3:PutBucketVersioning",
94+
"s3:GetBucketVersioning",
9395
"s3:GetBucketLocation",
9496
"s3:ListBucket",
9597
"s3:PutBucketTagging",

cdk/src/bootstrap/policies/observability.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ export function observabilityPolicy(): iam.PolicyDocument {
127127
's3:GetBucketPublicAccessBlock',
128128
's3:PutEncryptionConfiguration',
129129
's3:PutLifecycleConfiguration',
130+
's3:PutBucketVersioning',
131+
's3:GetBucketVersioning',
130132
's3:GetBucketLocation',
131133
's3:ListBucket',
132134
's3:PutBucketTagging',

cdk/test/bootstrap/policies.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,38 @@ describe('IaCRole-ABCA-Observability', () => {
247247
]),
248248
);
249249
});
250+
251+
it('S3ApplicationBuckets grants the bucket-feature actions the stack buckets enable', () => {
252+
// Regression guard for #404: the exec role must hold a CreateBucket-time
253+
// action for every feature the application buckets turn on, or a fresh
254+
// deploy rolls back with AccessDenied at that specific configure call.
255+
// AttachmentsBucket sets `versioned: true`, so PutBucketVersioning (and
256+
// GetBucketVersioning for the read/drift path) are required — they were
257+
// missing, which is how this reached main. The service-prefix check above
258+
// can't catch it (still `s3:`). If a bucket later enables notifications,
259+
// CORS, etc., add the matching action here and to the policy together.
260+
const resolvedDoc = stack.resolve(doc);
261+
const statements = resolvedDoc.Statement as Array<{
262+
Sid: string;
263+
Action?: string | string[];
264+
}>;
265+
const s3Buckets = statements.find((s) => s.Sid === 'S3ApplicationBuckets');
266+
expect(s3Buckets).toBeDefined();
267+
268+
const actions = Array.isArray(s3Buckets!.Action)
269+
? s3Buckets!.Action
270+
: [s3Buckets!.Action];
271+
272+
for (const action of [
273+
's3:CreateBucket',
274+
's3:PutEncryptionConfiguration',
275+
's3:PutLifecycleConfiguration',
276+
's3:PutBucketVersioning',
277+
's3:GetBucketVersioning',
278+
]) {
279+
expect(actions).toContain(action);
280+
}
281+
});
250282
});
251283

252284
describe('IaCRole-ABCA-Compute-AgentCore', () => {

docs/design/DEPLOYMENT_ROLES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,8 @@ Bedrock Guardrails, CloudWatch Logs/Dashboards/Alarms, X-Ray, S3 (CDK assets), K
598598
"s3:GetBucketPublicAccessBlock",
599599
"s3:PutEncryptionConfiguration",
600600
"s3:PutLifecycleConfiguration",
601+
"s3:PutBucketVersioning",
602+
"s3:GetBucketVersioning",
601603
"s3:GetBucketLocation",
602604
"s3:ListBucket",
603605
"s3:PutBucketTagging",

docs/src/content/docs/architecture/Deployment-roles.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,8 @@ Bedrock Guardrails, CloudWatch Logs/Dashboards/Alarms, X-Ray, S3 (CDK assets), K
602602
"s3:GetBucketPublicAccessBlock",
603603
"s3:PutEncryptionConfiguration",
604604
"s3:PutLifecycleConfiguration",
605+
"s3:PutBucketVersioning",
606+
"s3:GetBucketVersioning",
605607
"s3:GetBucketLocation",
606608
"s3:ListBucket",
607609
"s3:PutBucketTagging",

0 commit comments

Comments
 (0)