import * as cdk from "aws-cdk-lib"; import { Match, Template } from "aws-cdk-lib/assertions"; import { OpenSweIamStack } from "../lib/open-swe-iam-stack"; import { OpenSweStack } from "../lib/open-swe-stack"; const ENV = { account: "328440206208", region: "us-east-1" }; // F-1 / IAC-04: s3:ListBucket must be constrained to the releases/ prefix so a // compromised box / leaked CI token cannot enumerate the rest of the bucket. const RELEASES_PREFIX_CONDITION = { StringLike: { "s3:prefix": ["releases/*"] } }; describe("S3 ListBucket prefix scoping (F-1/IAC-04)", () => { it("instance role ListBucket is constrained to releases/*", () => { const app = new cdk.App(); const stack = new OpenSweStack(app, "OpenSweDevStack", { stackName: "open-swe-dev", env: ENV, envName: "dev", }); Template.fromStack(stack).hasResourceProperties("AWS::IAM::Policy", { PolicyDocument: Match.objectLike({ Statement: Match.arrayWith([ Match.objectLike({ Sid: "ListArtifactBucket", Action: "s3:ListBucket", Condition: RELEASES_PREFIX_CONDITION, }), ]), }), }); }); it("github deploy app role ListBucket is constrained to releases/*", () => { const app = new cdk.App(); const stack = new OpenSweIamStack(app, "OpenSweIamStack", { stackName: "open-swe-iam", env: ENV, }); Template.fromStack(stack).hasResourceProperties("AWS::IAM::Policy", { PolicyDocument: Match.objectLike({ Statement: Match.arrayWith([ Match.objectLike({ Sid: "ListArtifactBucket", Action: "s3:ListBucket", Condition: RELEASES_PREFIX_CONDITION, }), ]), }), }); }); it("GetBucketLocation stays a separate, unconditioned statement", () => { const app = new cdk.App(); const stack = new OpenSweStack(app, "OpenSweDevStack", { stackName: "open-swe-dev", env: ENV, envName: "dev", }); Template.fromStack(stack).hasResourceProperties("AWS::IAM::Policy", { PolicyDocument: Match.objectLike({ Statement: Match.arrayWith([ Match.objectLike({ Sid: "GetArtifactBucketLocation", Action: "s3:GetBucketLocation", Condition: Match.absent(), }), ]), }), }); }); });