From 0473a6306b2ce92c5e164546308a51881952d368 Mon Sep 17 00:00:00 2001 From: Aditya Jain Date: Fri, 21 Aug 2026 18:55:37 -0700 Subject: [PATCH] fix(cloudformation-diff): security group rule diff ignores which group a rule belongs to SecurityGroupRule.equal() compared protocol/port/peer but not groupId, so a rule with the same shape moving from one security group to another was reported as unchanged instead of a removal + addition. --- .../lib/network/security-group-rule.ts | 3 +- .../test/network/detect-changes.test.ts | 59 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/cloudformation-diff/lib/network/security-group-rule.ts b/packages/@aws-cdk/cloudformation-diff/lib/network/security-group-rule.ts index 4df15fc0d..5577927ba 100644 --- a/packages/@aws-cdk/cloudformation-diff/lib/network/security-group-rule.ts +++ b/packages/@aws-cdk/cloudformation-diff/lib/network/security-group-rule.ts @@ -48,7 +48,8 @@ export class SecurityGroupRule { } public equal(other: SecurityGroupRule) { - return this.ipProtocol === other.ipProtocol + return this.groupId === other.groupId + && this.ipProtocol === other.ipProtocol && this.fromPort === other.fromPort && this.toPort === other.toPort && peerEqual(this.peer, other.peer); diff --git a/packages/@aws-cdk/cloudformation-diff/test/network/detect-changes.test.ts b/packages/@aws-cdk/cloudformation-diff/test/network/detect-changes.test.ts index 6542a72ea..64bf0fae8 100644 --- a/packages/@aws-cdk/cloudformation-diff/test/network/detect-changes.test.ts +++ b/packages/@aws-cdk/cloudformation-diff/test/network/detect-changes.test.ts @@ -74,3 +74,62 @@ test('detect addition of all types of rules', () => { ], }); }); + +test('detect a rule moving from one security group to a different one', () => { + // A rule with the same protocol/port/peer moves from WebSG to DbSG between the + // old and new templates. This must be reported as a removal from WebSG and an + // addition to DbSG -- it must not be treated as unchanged just because the + // rule's shape (ignoring which group it's attached to) happens to match. + const oldTemplate = template({ + WebSG: resource('AWS::EC2::SecurityGroup', { + SecurityGroupIngress: [ + { + CidrIp: '10.0.0.0/24', + FromPort: 22, + ToPort: 22, + IpProtocol: 'tcp', + }, + ], + }), + DbSG: resource('AWS::EC2::SecurityGroup', {}), + }); + + const newTemplate = template({ + WebSG: resource('AWS::EC2::SecurityGroup', {}), + DbSG: resource('AWS::EC2::SecurityGroup', { + SecurityGroupIngress: [ + { + CidrIp: '10.0.0.0/24', + FromPort: 22, + ToPort: 22, + IpProtocol: 'tcp', + }, + ], + }), + }); + + // WHEN + const diff = fullDiff(oldTemplate, newTemplate); + + // THEN + expect(diff.securityGroupChanges.toJson()).toEqual({ + ingressRuleAdditions: [ + { + groupId: '${DbSG.GroupId}', + ipProtocol: 'tcp', + fromPort: 22, + toPort: 22, + peer: { kind: 'cidr-ip', ip: '10.0.0.0/24' }, + }, + ], + ingressRuleRemovals: [ + { + groupId: '${WebSG.GroupId}', + ipProtocol: 'tcp', + fromPort: 22, + toPort: 22, + peer: { kind: 'cidr-ip', ip: '10.0.0.0/24' }, + }, + ], + }); +});