Repository: getmoto/moto (python)

Issue:
ec2.describe_security_group_rules bad output response for IPv4, IPv6, Prefix Lists, Security Groups targets and overlapping port/protocols
# How to reproduce:
```python
from pprint import pprint

import boto3
from botocore.config import Config
from moto import mock_aws

with mock_aws():
    ec2_client = boto3.client("ec2", config=Config(region_name="us-east-1"))
    vpc = ec2_client.create_vpc(CidrBlock="10.0.0.0/24")
    vpc_id = vpc["Vpc"]["VpcId"]
    security_group = ec2_client.create_security_group(
        Description="test",
        GroupName="test",
        VpcId=vpc_id,
    )
    security_group_id = security_group["GroupId"]

    prefix_list = ec2_client.create_managed_prefix_list(
        PrefixListName="test",
        AddressFamily="IPv4",
        MaxEntries=1,
    )
    prefix_list_id = prefix_list["PrefixList"]["PrefixListId"]

    rules = [
        # starting rule with tcp/22
        (22, "tcp", {"IpRanges": [{"CidrIp": "10.0.0.0/24"}]}),
        # keeping the port/protocol the same but changing the CIDR will NOT show up in describe_security_group_rules
        (22, "tcp", {"IpRanges": [{"CidrIp": "10.0.1.0/24"}]}),
        # changing just the port will show up in describe_security_group_rules
        (80, "tcp", {"IpRanges": [{"CidrIp": "10.0.0.0/24"}]}),
        # changing just the protocol will show up in describe_security_group_rules
        (22, "udp", {"IpRanges": [{"CidrIp": "10.0.0.0/24"}]}),
        # changing the port and protocol will show up in describe_security_group_rules
        (-1, "-1", {"IpRanges": [{"CidrIp": "10.0.0.0/24"}]}),
        #
        # The same issues are present with IPv6 in that, with matching port/protocol to the original rule, it will be omitted from describe_security_group_rules
        (22, "tcp", {"Ipv6Ranges": [{"CidrIpv6": "2001:db8::/32"}]}),
        # changing the port to a new port will cause it to show up in describe_security_group_rules
        # however, a the value cannot be seen. An empty `CidrIpv4` key is returned and the `CidrIpv6` key is not returned.
        (443, "tcp", {"Ipv6Ranges": [{"CidrIpv6": "2001:db8::/64"}]}),
        #
        # The same issues are present with PrefixListIds in that, with matching port/protocol to the original rule, it will be omitted from describe_security_group_rules
        (22, "tcp", {"PrefixListIds": [{"PrefixListId": prefix_list_id}]}),
        # changing the port to a new port will cause it to show up in describe_security_group_rules
        # however there is no indication that the rule is for a prefix list.
        (8080, "tcp", {"PrefixListIds": [{"PrefixListId": prefix_list_id}]}),
        #
        # the same issues are present with security group references in that, with matching port/protocol to the original rule, it will be omitted from describe_security_group_rules
        (22, "tcp", {"UserIdGroupPairs": [{"GroupId": security_group_id}]}),
        # changing the port to a new port will cause it to show up in describe_security_group_rules
        # however there is no indication of this pointing to itself as a target.
        (25, "tcp", {"UserIdGroupPairs": [{"GroupId": security_group_id}]}),
    ]
    for rule in rules:
        ec2_client.authorize_security_group_ingress(
            GroupId=security_group_id,
            IpPermissions=[
                {
                    "FromPort": rule[0],
                    "IpProtocol": rule[1],
                    "ToPort": rule[0],
                    **rule[2],
                }
            ],
        )
    response = ec2_client.describe_security_group_rules(
        Filters=[
            {
                "Name": "group-id",
                "Values": [security_group_id],
            }
        ]
    )
    print("DescribeSecurityGroupRules:")
    pprint(response)
    print("#" * 80)

    response = ec2_client.describe_security_groups(GroupIds=[security_group_id])
    print("DescribeSecurityGroups:")
    pprint(response)

```

# Example output when using Moto
```bash
{'ResponseMetadata': {'HTTPHeaders': {'date': 'Thu, 01 Aug 2024 09:00:42 GMT',
                                      'server': 'amazon.com',
                                      'x-amzn-requestid': 'tLxfyrhEc7WD1SbGcDUTAN3YidmthVwgXRqYEt5sO6n443bhVQHJ'},
                      'HTTPStatusCode': 200,
                      'RequestId': None,
                      'RetryAttempts': 0},
 'SecurityGroupRules': [{'CidrIpv4': '10.0.0.0/24',
                         'FromPort': 22,
                         'GroupId': 'sg-11b52ebe9f53962d5',
                         'GroupOwnerId': '123456789012',
                         'IpProtocol': 'tcp',
                         'IsEgress': False,
                         'SecurityGroupRuleId': 'sgr-1610c474fe83d59c1',
                         'Tags': [],
                         'ToPort': 22},
                        {'CidrIpv4': '10.0.0.0/24',
                         'FromPort': 80,
                         'GroupId': 'sg-11b52ebe9f53962d5',
                         'GroupOwnerId': '123456789012',
                         'IpProtocol': 'tcp',
                         'IsEgress': False,
                         'SecurityGroupRuleId': 'sgr-ee22c9b3a574dde95',
                         'Tags': [],
                         'ToPort': 80},
                        {'CidrIpv4': '10.0.0.0/24',
                         'FromPort': 22,
                         'GroupId': 'sg-11b52ebe9f53962d5',
                         'GroupOwnerId': '123456789012',
                         'IpProtocol': 'udp',
                         'IsEgress': False,
                         'SecurityGroupRuleId': 'sgr-aa48fa09076e78f30',
                         'Tags': [],
                         'ToPort': 22},
                        {'CidrIpv4': '10.0.0.0/24',
                         'GroupId': 'sg-11b52ebe9f53962d5',
                         'GroupOwnerId': '123456789012',
                         'IpProtocol': '-1',
                         'IsEgress': False,
                         'SecurityGroupRuleId': 'sgr-3625584fa8fd23f61',
                         'Tags': []},
                        # this is the ipv6 based rule that has an empty CidrIpv4 value instead of CidrIpv6
                        {'CidrIpv4': '',
                         'FromPort': 443,
                         'GroupId': 'sg-11b52ebe9f53962d5',
                         'GroupOwnerId': '123456789012',
                         'IpProtocol': 'tcp',
                         'IsEgress': False,
                         'SecurityGroupRuleId': 'sgr-c26acf93f6f35b3fa',
                         'Tags': [],
                         'ToPort': 443},
                        # this is the prefix list rule that doesn't reference a prefix list id.
                        {'FromPort': 8080,
                         'GroupId': 'sg-11b52ebe9f53962d5',
                         'GroupOwnerId': '123456789012',
                         'IpProtocol': 'tcp',
                         'IsEgress': False,
                         'SecurityGroupRuleId': 'sgr-b7370f9726454ace5',
                         'Tags': [],
                         'ToPort': 8080},
                        # this is the security group based rule that is missing the UserIdGroupPairs key.
                        {'FromPort': 25,
                         'GroupId': 'sg-11b52ebe9f53962d5',
                         'GroupOwnerId': '123456789012',
                         'IpProtocol': 'tcp',
                         'IsEgress': False,
                         'SecurityGroupRuleId': 'sgr-240dfdb92342a2f3e',
                         'Tags': [],
                         'ToPort': 25},
                        {'CidrIpv4': '0.0.0.0/0',
                         'GroupId': 'sg-11b52ebe9f53962d5',
                         'GroupOwnerId': '123456789012',
                         'IpProtocol': '-1',
                         'IsEgress': True,
                         'SecurityGroupRuleId': 'sgr-972db2badd67f2a36',
                         'Tags': []}]}
################################################################################
DescribeSecurityGroups:
{'ResponseMetadata': {'HTTPHeaders': {'date': 'Thu, 01 Aug 2024 09:00:42 GMT',
                                      'server': 'amazon.com',
                                      'x-amzn-requestid': '0rJIGXkFOpDqWtQCfoKj9Kd7ZrknHHY7VNpkzS2lHPQjQCMKGhiS'},
                      'HTTPStatusCode': 200,
                      'RequestId': '59dbff89-35bd-4eac-99ed-be587EXAMPLE',
                      'RetryAttempts': 0},
 'SecurityGroups': [{'Description': 'test',
                     'GroupId': 'sg-11b52ebe9f53962d5',
                     'GroupName': 'test',
                     'IpPermissions': [{'FromPort': 22,
                                        'IpProtocol': 'tcp',
                                        'IpRanges': [{'CidrIp': '10.0.0.0/24'},
                                                     {'CidrIp': '10.0.1.0/24'}],
                                        'Ipv6Ranges': [{'CidrIpv6': '2001:db8::/32'}],
                                        'PrefixListIds': [{'PrefixListId': 'pl-61d2b755'}],
                                        'ToPort': 22,
                                        'UserIdGroupPairs': [{'GroupId': 'sg-11b52ebe9f53962d5',
                                                              'UserId': '123456789012'}]},
                                       {'FromPort': 80,
                                        'IpProtocol': 'tcp',
                                        'IpRanges': [{'CidrIp': '10.0.0.0/24'}],
                                        'Ipv6Ranges': [],
                                        'PrefixListIds': [],
                                        'ToPort': 80,
                                        'UserIdGroupPairs': []},
                                       {'FromPort': 22,
                                        'IpProtocol': 'udp',
                                        'IpRanges': [{'CidrIp': '10.0.0.0/24'}],
                                        'Ipv6Ranges': [],
                                        'PrefixListIds': [],
                                        'ToPort': 22,
                                        'UserIdGroupPairs': []},
                                       {'IpProtocol': '-1',
                                        'IpRanges': [{'CidrIp': '10.0.0.0/24'}],
                                        'Ipv6Ranges': [],
                                        'PrefixListIds': [],
                                        'UserIdGroupPairs': []},
                                       {'FromPort': 443,
                                        'IpProtocol': 'tcp',
                                        'IpRanges': [],
                                        'Ipv6Ranges': [{'CidrIpv6': '2001:db8::/64'}],
                                        'PrefixListIds': [],
                                        'ToPort': 443,
                                        'UserIdGroupPairs': []},
                                       {'FromPort': 8080,
                                        'IpProtocol': 'tcp',
                                        'IpRanges': [],
                                        'Ipv6Ranges': [],
                                        'PrefixListIds': [{'PrefixListId': 'pl-61d2b755'}],
                                        'ToPort': 8080,
                                        'UserIdGroupPairs': []},
                                       {'FromPort': 25,
                                        'IpProtocol': 'tcp',
                                        'IpRanges': [],
                                        'Ipv6Ranges': [],
                                        'PrefixListIds': [],
                                        'ToPort': 25,
                                        'UserIdGroupPairs': [{'GroupId': 'sg-11b52ebe9f53962d5',
                                                              'UserId': '123456789012'}]}],
                     'IpPermissionsEgress': [{'IpProtocol': '-1',
                                              'IpRanges': [{'CidrIp': '0.0.0.0/0'}],
                                              'Ipv6Ranges': [],
                                              'PrefixListIds': [],
                                              'UserIdGroupPairs': []}],
                     'OwnerId': '123456789012',
                     'Tags': [],
                     'VpcId': 'vpc-3c3a0fb2'}]}
```

# Example output against test AWS account
*Please note, Response Metadata and other account specific information was removed.*
To recreate this, comment out `with mock_aws():` and unindent the remaining code under it.
Run the code after setting AWS access credentials.

```bash
DescribeSecurityGroupRules:
{'ResponseMetadata': {'REMOVED'},
 'SecurityGroupRules': [{'CidrIpv4': '10.0.0.0/24',
                         'FromPort': 22,
                         'GroupId': 'sg-0589d96908f0d5176',
                         'GroupOwnerId': 'REMOVED FROM OUTPUT',
                         'IpProtocol': 'udp',
                         'IsEgress': False,
                         'SecurityGroupRuleId': 'sgr-096b3d731004acd87',
                         'Tags': [],
                         'ToPort': 22},
                        {'CidrIpv6': '2001:db8::/32',
                         'FromPort': 22,
                         'GroupId': 'sg-0589d96908f0d5176',
                         'GroupOwnerId': 'REMOVED FROM OUTPUT',
                         'IpProtocol': 'tcp',
                         'IsEgress': False,
                         'SecurityGroupRuleId': 'sgr-036b7f2b4f982b1f6',
                         'Tags': [],
                         'ToPort': 22},
                        {'FromPort': 22,
                         'GroupId': 'sg-0589d96908f0d5176',
                         'GroupOwnerId': 'REMOVED FROM OUTPUT',
                         'IpProtocol': 'tcp',
                         'IsEgress': False,
                         'ReferencedGroupInfo': {'GroupId': 'sg-0589d96908f0d5176',
                                                 'UserId': 'REMOVED FROM OUTPUT'},
                         'SecurityGroupRuleId': 'sgr-055c772805790eaa8',
                         'Tags': [],
                         'ToPort': 22},
                        {'CidrIpv4': '10.0.0.0/24',
                         'FromPort': 22,
                         'GroupId': 'sg-0589d96908f0d5176',
                         'GroupOwnerId': 'REMOVED FROM OUTPUT',
                         'IpProtocol': 'tcp',
                         'IsEgress': False,
                         'SecurityGroupRuleId': 'sgr-0a25ab81ed7e94bd2',
                         'Tags': [],
                         'ToPort': 22},
                        {'FromPort': 8080,
                         'GroupId': 'sg-0589d96908f0d5176',
                         'GroupOwnerId': 'REMOVED FROM OUTPUT',
                         'IpProtocol': 'tcp',
                         'IsEgress': False,
                         'PrefixListId': 'pl-0562ff94ad95b979a',
                         'SecurityGroupRuleId': 'sgr-04321a137bca65300',
                         'Tags': [],
                         'ToPort': 8080},
                        {'CidrIpv6': '2001:db8::/64',
                         'FromPort': 443,
                         'GroupId': 'sg-0589d96908f0d5176',
                         'GroupOwnerId': 'REMOVED FROM OUTPUT',
                         'IpProtocol': 'tcp',
                         'IsEgress': False,
                         'SecurityGroupRuleId': 'sgr-09aba839a4b9d222e',
                         'Tags': [],
                         'ToPort': 443},
                        {'CidrIpv4': '0.0.0.0/0',
                         'FromPort': -1,
                         'GroupId': 'sg-0589d96908f0d5176',
                         'GroupOwnerId': 'REMOVED FROM OUTPUT',
                         'IpProtocol': '-1',
                         'IsEgress': True,
                         'SecurityGroupRuleId': 'sgr-05f118d5b9ac63830',
                         'Tags': [],
                         'ToPort': -1},
                        {'CidrIpv4': '10.0.0.0/24',
                         'FromPort': -1,
                         'GroupId': 'sg-0589d96908f0d5176',
                         'GroupOwnerId': 'REMOVED FROM OUTPUT',
                         'IpProtocol': '-1',
                         'IsEgress': False,
                         'SecurityGroupRuleId': 'sgr-0971c864fda2af012',
                         'Tags': [],
                         'ToPort': -1},
                        {'FromPort': 25,
                         'GroupId': 'sg-0589d96908f0d5176',
                         'GroupOwnerId': 'REMOVED FROM OUTPUT',
                         'IpProtocol': 'tcp',
                         'IsEgress': False,
                         'ReferencedGroupInfo': {'GroupId': 'sg-0589d96908f0d5176',
                                                 'UserId': 'REMOVED FROM OUTPUT'},
                         'SecurityGroupRuleId': 'sgr-0b1d574908be972be',
                         'Tags': [],
                         'ToPort': 25},
                        {'CidrIpv4': '10.0.0.0/24',
                         'FromPort': 80,
                         'GroupId': 'sg-0589d96908f0d5176',
                         'GroupOwnerId': 'REMOVED FROM OUTPUT',
                         'IpProtocol': 'tcp',
                         'IsEgress': False,
                         'SecurityGroupRuleId': 'sgr-063b0f3ea78f30190',
                         'Tags': [],
                         'ToPort': 80},
                        {'CidrIpv4': '10.0.1.0/24',
                         'FromPort': 22,
                         'GroupId': 'sg-0589d96908f0d5176',
                         'GroupOwnerId': 'REMOVED FROM OUTPUT',
                         'IpProtocol': 'tcp',
                         'IsEgress': False,
                         'SecurityGroupRuleId': 'sgr-0d7b551c6de330689',
                         'Tags': [],
                         'ToPort': 22},
                        {'FromPort': 22,
                         'GroupId': 'sg-0589d96908f0d5176',
                         'GroupOwnerId': 'REMOVED FROM OUTPUT',
                         'IpProtocol': 'tcp',
                         'IsEgress': False,
                         'PrefixListId': 'pl-0562ff94ad95b979a',
                         'SecurityGroupRuleId': 'sgr-024a055c2011592f9',
                         'Tags': [],
                         'ToPort': 22}]}
################################################################################
DescribeSecurityGroups:
{'ResponseMetadata': {'REMOVED'},
 'SecurityGroups': [{'Description': 'test',
                     'GroupId': 'sg-0589d96908f0d5176',
                     'GroupName': 'test',
                     'IpPermissions': [{'FromPort': 80,
                                        'IpProtocol': 'tcp',
                                        'IpRanges': [{'CidrIp': '10.0.0.0/24'}],
                                        'Ipv6Ranges': [],
                                        'PrefixListIds': [],
                                        'ToPort': 80,
                                        'UserIdGroupPairs': []},
                                       {'FromPort': 8080,
                                        'IpProtocol': 'tcp',
                                        'IpRanges': [],
                                        'Ipv6Ranges': [],
                                        'PrefixListIds': [{'PrefixListId': 'pl-0562ff94ad95b979a'}],
                                        'ToPort': 8080,
                                        'UserIdGroupPairs': []},
                                       {'IpProtocol': '-1',
                                        'IpRanges': [{'CidrIp': '10.0.0.0/24'}],
                                        'Ipv6Ranges': [],
                                        'PrefixListIds': [],
                                        'UserIdGroupPairs': []},
                                       {'FromPort': 22,
                                        'IpProtocol': 'udp',
                                        'IpRanges': [{'CidrIp': '10.0.0.0/24'}],
                                        'Ipv6Ranges': [],
                                        'PrefixListIds': [],
                                        'ToPort': 22,
                                        'UserIdGroupPairs': []},
                                       {'FromPort': 22,
                                        'IpProtocol': 'tcp',
                                        'IpRanges': [{'CidrIp': '10.0.0.0/24'},
                                                     {'CidrIp': '10.0.1.0/24'}],
                                        'Ipv6Ranges': [{'CidrIpv6': '2001:db8::/32'}],
                                        'PrefixListIds': [{'PrefixListId': 'pl-0562ff94ad95b979a'}],
                                        'ToPort': 22,
                                        'UserIdGroupPairs': [{'GroupId': 'sg-0589d96908f0d5176',
                                                              'UserId': 'REMOVED FROM OUTPUT'}]},
                                       {'FromPort': 25,
                                        'IpProtocol': 'tcp',
                                        'IpRanges': [],
                                        'Ipv6Ranges': [],
                                        'PrefixListIds': [],
                                        'ToPort': 25,
                                        'UserIdGroupPairs': [{'GroupId': 'sg-0589d96908f0d5176',
                                                              'UserId': 'REMOVED FROM OUTPUT'}]},
                                       {'FromPort': 443,
                                        'IpProtocol': 'tcp',
                                        'IpRanges': [],
                                        'Ipv6Ranges': [{'CidrIpv6': '2001:db8::/64'}],
                                        'PrefixListIds': [],
                                        'ToPort': 443,
                                        'UserIdGroupPairs': []}],
                     'IpPermissionsEgress': [{'IpProtocol': '-1',
                                              'IpRanges': [{'CidrIp': '0.0.0.0/0'}],
                                              'Ipv6Ranges': [],
                                              'PrefixListIds': [],
                                              'UserIdGroupPairs': []}],
                     'OwnerId': 'REMOVED FROM OUTPUT',
                     'VpcId': 'vpc-01235fd402ad87191'}]}
```


# Expected outcome
Overall, when calling `ec2.describe_security_group_rules`, all rules on the security group should be accurately reflected.


# What actually happens
When describing a security groups rules using `ec2.describe_security_group_rules`, if a rules port and protocol are the same as an existing rule, it will not be show in the output of `ec2.describe_security_group_rules` regardless of what target the rule is for. The issue persists for ipv4, ipv6, prefix lists, and security group based rules.
This is in contrast to a live environment which will show all rules created with their correct values and key fields.

Please review comments from lines 30-56 (the rules list variable) for specific example rules and how they behave when shown in the output of `ec2.describe_security_group_rules`.

Prefix lists, security group targets, and ipv6 targets also have output issues specifically.

Prefix list based rules, in the output of `ec2.describe_security_group_rules`, do not indicate they are for prefix lists.
Security Group based rules, in the output of `ec2.describe_security_group_rules`, do not indicate they are for Security Groups.
IPv6 based rules, in the output of `ec2.describe_security_group_rules`, do not indicate they are for IPv6 and have an empty CidrIpv4 field.


# Package Version Info
boto3 = "1.34.150"

botocore = "1.34.150"

moto = "5.0.11"

Python = "3.11.8"

The above packages were installed with poetry into a local virtual environment.
The above code is using the mock_aws decorator.


Pull request:
EC2: describe_security_group_rules() should not combine Rules
Fixes #7915 

Both the (very extensive) new test and the modified test are now verified against AWS.

Patch:
diff --git a/moto/ec2/models/security_groups.py b/moto/ec2/models/security_groups.py
index 869c47d98..eac8cf5f2 100644
--- a/moto/ec2/models/security_groups.py
+++ b/moto/ec2/models/security_groups.py
@@ -517,6 +517,49 @@ class SecurityGroup(TaggedEC2Resource, CloudFormationModel):
             len(rule.ip_ranges) + len(rule.source_groups) for rule in self.egress_rules
         )
 
+    @property
+    def flattened_ingress_rules(self) -> List[SecurityRule]:
+        return self._flattened_rules(copy.copy(self.ingress_rules))
+
+    @property
+    def flattened_egress_rules(self) -> List[SecurityRule]:
+        return self._flattened_rules(copy.copy(self.egress_rules))
+
+    def _flattened_rules(self, rules: List[SecurityRule]) -> List[SecurityRule]:
+        rules_to_return: List[SecurityRule] = []
+        for rule in rules:
+            for already_added in rules_to_return:
+                if (
+                    already_added.from_port == rule.from_port
+                    and already_added.to_port == rule.to_port
+                    and already_added.ip_protocol == rule.ip_protocol
+                ):
+                    already_added.ip_ranges.extend(
+                        [
+                            item
+                            for item in rule.ip_ranges
+                            if item not in already_added.ip_ranges
+                        ]
+                    )
+                    already_added.source_groups.extend(
+                        [
+                            item
+                            for item in rule.source_groups
+                            if item not in already_added.source_groups
+                        ]
+                    )
+                    already_added.prefix_list_ids.extend(
+                        [
+                            item
+                            for item in rule.prefix_list_ids
+                            if item not in already_added.prefix_list_ids
+                        ]
+                    )
+                    break
+            else:
+                rules_to_return.append(rule)
+        return rules_to_return
+
 
 class SecurityGroupBackend:
     def __init__(self) -> None:
@@ -767,6 +810,8 @@ class SecurityGroupBackend:
                 security_rule.from_port == rule.from_port
                 and security_rule.to_port == rule.to_port
                 and security_rule.ip_protocol == rule.ip_protocol
+                and security_rule.ip_ranges == rule.ip_ranges
+                and security_rule.prefix_list_ids == rule.prefix_list_ids
             ):
                 rule.ip_ranges.extend(
                     [
@@ -941,6 +986,8 @@ class SecurityGroupBackend:
                 security_rule.from_port == rule.from_port
                 and security_rule.to_port == rule.to_port
                 and security_rule.ip_protocol == rule.ip_protocol
+                and security_rule.ip_ranges == rule.ip_ranges
+                and security_rule.prefix_list_ids == rule.prefix_list_ids
             ):
                 rule.ip_ranges.extend(
                     [
diff --git a/moto/ec2/responses/security_groups.py b/moto/ec2/responses/security_groups.py
index cf1db0b2b..d71b88b9d 100644
--- a/moto/ec2/responses/security_groups.py
+++ b/moto/ec2/responses/security_groups.py
@@ -280,13 +280,19 @@ DESCRIBE_SECURITY_GROUP_RULES_RESPONSE = """
             <item>
                 <fromPort>{{ rule.from_port if rule.from_port is not none else -1 }}</fromPort>
                 <toPort>{{ rule.to_port if rule.to_port is not none else -1 }}</toPort>
-                <cidrIpv4>{{ ip_range['CidrIp'] }}</cidrIpv4>
+                {% if ip_range['CidrIp'] %}<cidrIpv4>{{ ip_range['CidrIp'] }}</cidrIpv4>{% endif %}
+                {% if ip_range['CidrIpv6'] %}<cidrIpv6>{{ ip_range['CidrIpv6'] }}</cidrIpv6>{% endif %}
                 <ipProtocol>{{ rule.ip_protocol }}</ipProtocol>
                 <groupId>{{ rule.group_id }}</groupId>
                 <groupOwnerId>{{ rule.owner_id }}</groupOwnerId>
-                <description>{{ ip_range['Description'] }}</description>
+                {% if ip_range['Description'] %}<description>{{ ip_range['Description'] }}</description>{% endif %}
                 <isEgress>{{ 'true' if rule.is_egress else 'false' }}</isEgress>
                 <securityGroupRuleId>{{ rule.id }}</securityGroupRuleId>
+                {% if (rule.source_groups | length) > 0 %}
+                <referencedGroupInfo>
+                  <groupId>{{ rule.source_groups[0]['GroupId'] }}</groupId><userId>{{ rule.source_groups[0]['OwnerId'] }}</userId>
+                </referencedGroupInfo>
+                {% endif %}
                 <tagSet>
                 {% for tag in rule.get_tags() %}
                     <item>
@@ -297,6 +303,31 @@ DESCRIBE_SECURITY_GROUP_RULES_RESPONSE = """
                 </tagSet> 
             </item>
           {% endfor %}
+          {% if rule.ip_ranges | length == 0 %}
+            <item>
+                <fromPort>{{ rule.from_port if rule.from_port is not none else -1 }}</fromPort>
+                <toPort>{{ rule.to_port if rule.to_port is not none else -1 }}</toPort>
+                <ipProtocol>{{ rule.ip_protocol }}</ipProtocol>
+                {% if rule.prefix_list_ids | length > 0 %}<prefixListId>{{ rule.prefix_list_ids[0]['PrefixListId'] }}</prefixListId>{% endif %}
+                <groupId>{{ rule.group_id }}</groupId>
+                <groupOwnerId>{{ rule.owner_id }}</groupOwnerId>
+                <isEgress>{{ 'true' if rule.is_egress else 'false' }}</isEgress>
+                <securityGroupRuleId>{{ rule.id }}</securityGroupRuleId>
+                {% if (rule.source_groups | length) > 0 %}
+                <referencedGroupInfo>
+                  <groupId>{{ rule.source_groups[0]['GroupId'] }}</groupId><userId>{{ rule.source_groups[0]['OwnerId'] }}</userId>
+                </referencedGroupInfo>
+                {% endif %}
+                <tagSet>
+                {% for tag in rule.get_tags() %}
+                    <item>
+                      <key>{{ tag.key }}</key>
+                      <value>{{ tag.value }}</value>
+                    </item>
+                {% endfor %}
+                </tagSet> 
+            </item>
+          {% endif %}
         {% endfor %}
   </securityGroupRuleSet>
 </DescribeSecurityGroupRulesResponse>"""
@@ -319,7 +350,7 @@ DESCRIBE_SECURITY_GROUPS_RESPONSE = """<DescribeSecurityGroupsResponse xmlns="ht
              <vpcId>{{ group.vpc_id }}</vpcId>
              {% endif %}
              <ipPermissions>
-               {% for rule in group.ingress_rules %}
+               {% for rule in group.flattened_ingress_rules %}
                     <item>
                        <ipProtocol>{{ rule.ip_protocol }}</ipProtocol>
                        {% if rule.from_port is not none %}
@@ -384,7 +415,7 @@ DESCRIBE_SECURITY_GROUPS_RESPONSE = """<DescribeSecurityGroupsResponse xmlns="ht
                 {% endfor %}
              </ipPermissions>
              <ipPermissionsEgress>
-               {% for rule in group.egress_rules %}
+               {% for rule in group.flattened_egress_rules %}
                     <item>
                        <ipProtocol>{{ rule.ip_protocol }}</ipProtocol>
                        {% if rule.from_port is not none %}


Test patch:
diff --git a/tests/test_ec2/test_security_groups.py b/tests/test_ec2/test_security_groups.py
index 73a371e1b..519b5c2c6 100644
--- a/tests/test_ec2/test_security_groups.py
+++ b/tests/test_ec2/test_security_groups.py
@@ -12,6 +12,7 @@ from botocore.exceptions import ClientError
 from moto import mock_aws, settings
 from moto.core import DEFAULT_ACCOUNT_ID
 from moto.ec2 import ec2_backends
+from tests import aws_verified
 
 REGION = "us-east-1"
 
@@ -132,17 +133,30 @@ def test_create_two_security_groups_with_same_name_in_different_vpc():
     assert name in group_names
 
 
-@mock_aws
+@aws_verified
+@pytest.mark.aws_verified
 def test_create_two_security_groups_in_vpc_with_ipv6_enabled():
     ec2 = boto3.resource("ec2", region_name=REGION)
     vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16", AmazonProvidedIpv6CidrBlock=True)
+    security_group = None
 
-    security_group = ec2.create_security_group(
-        GroupName=str(uuid4()), Description="Test security group sg01", VpcId=vpc.id
-    )
+    try:
+        security_group = ec2.create_security_group(
+            GroupName=str(uuid4()), Description="Test", VpcId=vpc.id
+        )
 
-    # The security group must have two defaul egress rules (one for ipv4 and aonther for ipv6)
-    assert len(security_group.ip_permissions_egress) == 2
+        # The security group must have one egress rule with an IP range for ipv4 and ipv6
+        assert len(security_group.ip_permissions_egress) == 1
+        assert security_group.ip_permissions_egress[0]["IpRanges"] == [
+            {"CidrIp": "0.0.0.0/0"}
+        ]
+        assert security_group.ip_permissions_egress[0]["Ipv6Ranges"] == [
+            {"CidrIpv6": "::/0"}
+        ]
+    finally:
+        if security_group:
+            security_group.delete()
+        vpc.delete()
 
 
 @mock_aws
@@ -868,7 +882,6 @@ def test_description_in_ip_permissions():
     assert result["SecurityGroupRules"][1]["FromPort"] == 27017
     assert result["SecurityGroupRules"][1]["ToPort"] == 27018
 
-    assert result["SecurityGroupRules"][2]["Description"] == ""
     assert result["SecurityGroupRules"][2]["IsEgress"] is True
     assert result["SecurityGroupRules"][2]["FromPort"] == -1
     assert result["SecurityGroupRules"][2]["ToPort"] == -1
@@ -2025,3 +2038,284 @@ def test_invalid_security_group_id_in_rules_search():
         Filters=[{"Name": "group-id", "Values": [group_id]}]
     )
     assert len(response["SecurityGroupRules"]) == 1
+
+
+@aws_verified
+@pytest.mark.aws_verified
+@pytest.mark.parametrize("is_ingress", [True, False], ids=["ingress", "egress"])
+def test_authorize_security_group_rules_with_different_ipranges_or_prefixes(is_ingress):
+    is_egress = not is_ingress
+    sts = boto3.client("sts", "us-east-1")
+    account_id = sts.get_caller_identity()["Account"]
+
+    ec2_client = boto3.client("ec2", "us-east-1")
+    vpc = ec2_client.create_vpc(CidrBlock="10.0.0.0/24")
+    vpc_id = vpc["Vpc"]["VpcId"]
+    sg_id = pl_id = None
+
+    try:
+        sg = ec2_client.create_security_group(
+            Description="test", GroupName=f"test_{str(uuid4())[0:6]}", VpcId=vpc_id
+        )
+        sg_id = sg["GroupId"]
+
+        prefix_list = ec2_client.create_managed_prefix_list(
+            PrefixListName=f"test{str(uuid4())[0:6]}",
+            AddressFamily="IPv4",
+            MaxEntries=1,
+        )
+        pl_id = prefix_list["PrefixList"]["PrefixListId"]
+
+        rules = [
+            # starting rule with tcp/22
+            (22, "tcp", {"IpRanges": [{"CidrIp": "10.0.0.0/24"}]}),
+            # keeping the port/protocol the same but changing the CIDR will NOT show up in describe_security_group_rules
+            (22, "tcp", {"IpRanges": [{"CidrIp": "10.0.1.0/24"}]}),
+            # changing just the port will show up in describe_security_group_rules
+            (80, "tcp", {"IpRanges": [{"CidrIp": "10.0.0.0/24"}]}),
+            # changing just the protocol will show up in describe_security_group_rules
+            (22, "udp", {"IpRanges": [{"CidrIp": "10.0.0.0/24"}]}),
+            # changing the port and protocol will show up in describe_security_group_rules
+            (-1, "-1", {"IpRanges": [{"CidrIp": "10.0.0.0/24"}]}),
+            #
+            # The same issues are present with IPv6 in that, with matching port/protocol to the original rule, it will be omitted from describe_security_group_rules
+            (22, "tcp", {"Ipv6Ranges": [{"CidrIpv6": "2001:db8::/32"}]}),
+            # changing the port to a new port will cause it to show up in describe_security_group_rules
+            # however, a the value cannot be seen. An empty `CidrIpv4` key is returned and the `CidrIpv6` key is not returned.
+            (443, "tcp", {"Ipv6Ranges": [{"CidrIpv6": "2001:db8::/64"}]}),
+            #
+            # The same issues are present with PrefixListIds in that, with matching port/protocol to the original rule, it will be omitted from describe_security_group_rules
+            (22, "tcp", {"PrefixListIds": [{"PrefixListId": pl_id}]}),
+            # changing the port to a new port will cause it to show up in describe_security_group_rules
+            # however there is no indication that the rule is for a prefix list.
+            (8080, "tcp", {"PrefixListIds": [{"PrefixListId": pl_id}]}),
+            #
+            # the same issues are present with security group references in that, with matching port/protocol to the original rule, it will be omitted from describe_security_group_rules
+            (22, "tcp", {"UserIdGroupPairs": [{"GroupId": sg_id}]}),
+            # changing the port to a new port will cause it to show up in describe_security_group_rules
+            # however there is no indication of this pointing to itself as a target.
+            (25, "tcp", {"UserIdGroupPairs": [{"GroupId": sg_id}]}),
+        ]
+        method = (
+            ec2_client.authorize_security_group_ingress
+            if is_ingress
+            else ec2_client.authorize_security_group_egress
+        )
+        for rule in rules:
+            method(
+                GroupId=sg_id,
+                IpPermissions=[
+                    {
+                        "FromPort": rule[0],
+                        "IpProtocol": rule[1],
+                        "ToPort": rule[0],
+                        **rule[2],
+                    }
+                ],
+            )
+        created_rules = ec2_client.describe_security_group_rules(
+            Filters=[{"Name": "group-id", "Values": [sg_id]}]
+        )["SecurityGroupRules"]
+        for rule in created_rules:
+            rule.pop("GroupOwnerId", None)
+            rule.pop("Tags", None)
+            rule.pop("SecurityGroupRuleId", None)
+            del rule["GroupId"]
+
+        assert {
+            "CidrIpv6": "2001:db8::/64",
+            "FromPort": 443,
+            "IpProtocol": "tcp",
+            "IsEgress": is_egress,
+            "ToPort": 443,
+        } in created_rules
+        assert {
+            "FromPort": 22,
+            "IpProtocol": "tcp",
+            "IsEgress": is_egress,
+            "ReferencedGroupInfo": {"GroupId": sg_id, "UserId": account_id},
+            "ToPort": 22,
+        } in created_rules
+        assert {
+            "FromPort": 25,
+            "IpProtocol": "tcp",
+            "IsEgress": is_egress,
+            "ReferencedGroupInfo": {"GroupId": sg_id, "UserId": account_id},
+            "ToPort": 25,
+        } in created_rules
+        assert {
+            "CidrIpv4": "10.0.0.0/24",
+            "FromPort": 22,
+            "IpProtocol": "tcp",
+            "IsEgress": is_egress,
+            "ToPort": 22,
+        } in created_rules
+        assert {
+            "CidrIpv4": "10.0.0.0/24",
+            "FromPort": -1,
+            "IpProtocol": "-1",
+            "IsEgress": is_egress,
+            "ToPort": -1,
+        } in created_rules
+        assert {
+            "FromPort": 8080,
+            "IpProtocol": "tcp",
+            "IsEgress": is_egress,
+            "PrefixListId": pl_id,
+            "ToPort": 8080,
+        } in created_rules
+        assert {
+            "CidrIpv6": "2001:db8::/32",
+            "FromPort": 22,
+            "IpProtocol": "tcp",
+            "IsEgress": is_egress,
+            "ToPort": 22,
+        } in created_rules
+        assert {
+            "FromPort": 22,
+            "IpProtocol": "tcp",
+            "IsEgress": is_egress,
+            "PrefixListId": pl_id,
+            "ToPort": 22,
+        } in created_rules
+        assert {
+            "CidrIpv4": "10.0.0.0/24",
+            "FromPort": 80,
+            "IpProtocol": "tcp",
+            "IsEgress": is_egress,
+            "ToPort": 80,
+        } in created_rules
+        assert {
+            "CidrIpv4": "10.0.1.0/24",
+            "FromPort": 22,
+            "IpProtocol": "tcp",
+            "IsEgress": is_egress,
+            "ToPort": 22,
+        } in created_rules
+        assert {
+            "CidrIpv4": "0.0.0.0/0",
+            "FromPort": -1,
+            "IpProtocol": "-1",
+            "IsEgress": True,
+            "ToPort": -1,
+        } in created_rules
+        assert {
+            "CidrIpv4": "10.0.0.0/24",
+            "FromPort": 22,
+            "IpProtocol": "udp",
+            "IsEgress": is_egress,
+            "ToPort": 22,
+        } in created_rules
+
+        groups = ec2_client.describe_security_groups(GroupIds=[sg_id])[
+            "SecurityGroups"
+        ][0]
+        permissions = groups["IpPermissions"]
+        permissions_egress = groups["IpPermissionsEgress"]
+        # Drop empty fields
+        permissions = [{k: p[k] for k in p if p[k]} for p in permissions]
+        permissions_egress = [{k: p[k] for k in p if p[k]} for p in permissions_egress]
+
+        if is_ingress:
+            assert len(permissions) == 7
+            assert {
+                "FromPort": 80,
+                "IpProtocol": "tcp",
+                "IpRanges": [{"CidrIp": "10.0.0.0/24"}],
+                "ToPort": 80,
+            } in permissions
+            assert {
+                "FromPort": 8080,
+                "IpProtocol": "tcp",
+                "PrefixListIds": [{"PrefixListId": pl_id}],
+                "ToPort": 8080,
+            } in permissions
+            assert {
+                "IpProtocol": "-1",
+                "IpRanges": [{"CidrIp": "10.0.0.0/24"}],
+            } in permissions
+            assert {
+                "FromPort": 22,
+                "IpProtocol": "udp",
+                "IpRanges": [{"CidrIp": "10.0.0.0/24"}],
+                "ToPort": 22,
+            } in permissions
+            assert {
+                "FromPort": 22,
+                "IpProtocol": "tcp",
+                "IpRanges": [{"CidrIp": "10.0.0.0/24"}, {"CidrIp": "10.0.1.0/24"}],
+                "Ipv6Ranges": [{"CidrIpv6": "2001:db8::/32"}],
+                "PrefixListIds": [{"PrefixListId": pl_id}],
+                "ToPort": 22,
+                "UserIdGroupPairs": [{"GroupId": sg_id, "UserId": account_id}],
+            } in permissions
+            assert {
+                "FromPort": 25,
+                "IpProtocol": "tcp",
+                "ToPort": 25,
+                "UserIdGroupPairs": [{"GroupId": sg_id, "UserId": account_id}],
+            } in permissions
+            assert {
+                "FromPort": 443,
+                "IpProtocol": "tcp",
+                "Ipv6Ranges": [{"CidrIpv6": "2001:db8::/64"}],
+                "ToPort": 443,
+            } in permissions
+
+            assert permissions_egress == [
+                {"IpProtocol": "-1", "IpRanges": [{"CidrIp": "0.0.0.0/0"}]}
+            ]
+        if is_egress:
+            assert len(permissions) == 0
+            assert len(permissions_egress) == 7
+
+            assert {
+                "FromPort": 80,
+                "IpProtocol": "tcp",
+                "IpRanges": [{"CidrIp": "10.0.0.0/24"}],
+                "ToPort": 80,
+            } in permissions_egress
+            assert {
+                "FromPort": 8080,
+                "IpProtocol": "tcp",
+                "PrefixListIds": [{"PrefixListId": pl_id}],
+                "ToPort": 8080,
+            } in permissions_egress
+            assert {
+                "IpProtocol": "-1",
+                "IpRanges": [{"CidrIp": "0.0.0.0/0"}, {"CidrIp": "10.0.0.0/24"}],
+            } in permissions_egress
+            assert {
+                "FromPort": 22,
+                "IpProtocol": "udp",
+                "IpRanges": [{"CidrIp": "10.0.0.0/24"}],
+                "ToPort": 22,
+            } in permissions_egress
+            assert {
+                "FromPort": 22,
+                "IpProtocol": "tcp",
+                "IpRanges": [{"CidrIp": "10.0.0.0/24"}, {"CidrIp": "10.0.1.0/24"}],
+                "Ipv6Ranges": [{"CidrIpv6": "2001:db8::/32"}],
+                "PrefixListIds": [{"PrefixListId": pl_id}],
+                "ToPort": 22,
+                "UserIdGroupPairs": [{"GroupId": sg_id, "UserId": account_id}],
+            } in permissions_egress
+            assert {
+                "FromPort": 25,
+                "IpProtocol": "tcp",
+                "ToPort": 25,
+                "UserIdGroupPairs": [{"GroupId": sg_id, "UserId": account_id}],
+            } in permissions_egress
+            assert {
+                "FromPort": 443,
+                "IpProtocol": "tcp",
+                "Ipv6Ranges": [{"CidrIpv6": "2001:db8::/64"}],
+                "ToPort": 443,
+            } in permissions_egress
+
+    finally:
+        if pl_id:
+            ec2_client.delete_managed_prefix_list(PrefixListId=pl_id)
+        if sg_id:
+            ec2_client.delete_security_group(GroupId=sg_id)
+        ec2_client.delete_vpc(VpcId=vpc_id)