regscale-cli 6.27.3.0__py3-none-any.whl → 6.28.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of regscale-cli might be problematic. Click here for more details.
- regscale/_version.py +1 -1
- regscale/core/app/utils/app_utils.py +11 -2
- regscale/dev/cli.py +26 -0
- regscale/dev/version.py +72 -0
- regscale/integrations/commercial/__init__.py +15 -1
- regscale/integrations/commercial/amazon/amazon/__init__.py +0 -0
- regscale/integrations/commercial/amazon/amazon/common.py +204 -0
- regscale/integrations/commercial/amazon/common.py +48 -58
- regscale/integrations/commercial/aws/audit_manager_compliance.py +2671 -0
- regscale/integrations/commercial/aws/cli.py +3093 -55
- regscale/integrations/commercial/aws/cloudtrail_control_mappings.py +333 -0
- regscale/integrations/commercial/aws/cloudtrail_evidence.py +501 -0
- regscale/integrations/commercial/aws/cloudwatch_control_mappings.py +357 -0
- regscale/integrations/commercial/aws/cloudwatch_evidence.py +490 -0
- regscale/integrations/commercial/aws/config_compliance.py +914 -0
- regscale/integrations/commercial/aws/conformance_pack_mappings.py +198 -0
- regscale/integrations/commercial/aws/evidence_generator.py +283 -0
- regscale/integrations/commercial/aws/guardduty_control_mappings.py +340 -0
- regscale/integrations/commercial/aws/guardduty_evidence.py +1053 -0
- regscale/integrations/commercial/aws/iam_control_mappings.py +368 -0
- regscale/integrations/commercial/aws/iam_evidence.py +574 -0
- regscale/integrations/commercial/aws/inventory/__init__.py +223 -22
- regscale/integrations/commercial/aws/inventory/base.py +107 -5
- regscale/integrations/commercial/aws/inventory/resources/audit_manager.py +513 -0
- regscale/integrations/commercial/aws/inventory/resources/cloudtrail.py +315 -0
- regscale/integrations/commercial/aws/inventory/resources/cloudtrail_logs_metadata.py +476 -0
- regscale/integrations/commercial/aws/inventory/resources/cloudwatch.py +191 -0
- regscale/integrations/commercial/aws/inventory/resources/compute.py +66 -9
- regscale/integrations/commercial/aws/inventory/resources/config.py +464 -0
- regscale/integrations/commercial/aws/inventory/resources/containers.py +74 -9
- regscale/integrations/commercial/aws/inventory/resources/database.py +106 -31
- regscale/integrations/commercial/aws/inventory/resources/guardduty.py +286 -0
- regscale/integrations/commercial/aws/inventory/resources/iam.py +470 -0
- regscale/integrations/commercial/aws/inventory/resources/inspector.py +476 -0
- regscale/integrations/commercial/aws/inventory/resources/integration.py +175 -61
- regscale/integrations/commercial/aws/inventory/resources/kms.py +447 -0
- regscale/integrations/commercial/aws/inventory/resources/networking.py +103 -67
- regscale/integrations/commercial/aws/inventory/resources/s3.py +394 -0
- regscale/integrations/commercial/aws/inventory/resources/security.py +268 -72
- regscale/integrations/commercial/aws/inventory/resources/securityhub.py +473 -0
- regscale/integrations/commercial/aws/inventory/resources/storage.py +53 -29
- regscale/integrations/commercial/aws/inventory/resources/systems_manager.py +657 -0
- regscale/integrations/commercial/aws/inventory/resources/vpc.py +655 -0
- regscale/integrations/commercial/aws/kms_control_mappings.py +288 -0
- regscale/integrations/commercial/aws/kms_evidence.py +879 -0
- regscale/integrations/commercial/aws/ocsf/__init__.py +7 -0
- regscale/integrations/commercial/aws/ocsf/constants.py +115 -0
- regscale/integrations/commercial/aws/ocsf/mapper.py +435 -0
- regscale/integrations/commercial/aws/org_control_mappings.py +286 -0
- regscale/integrations/commercial/aws/org_evidence.py +666 -0
- regscale/integrations/commercial/aws/s3_control_mappings.py +356 -0
- regscale/integrations/commercial/aws/s3_evidence.py +632 -0
- regscale/integrations/commercial/aws/scanner.py +851 -206
- regscale/integrations/commercial/aws/security_hub.py +319 -0
- regscale/integrations/commercial/aws/session_manager.py +282 -0
- regscale/integrations/commercial/aws/ssm_control_mappings.py +291 -0
- regscale/integrations/commercial/aws/ssm_evidence.py +492 -0
- regscale/integrations/commercial/synqly/ticketing.py +27 -0
- regscale/integrations/compliance_integration.py +308 -38
- regscale/integrations/due_date_handler.py +3 -0
- regscale/integrations/scanner_integration.py +399 -84
- regscale/models/integration_models/cisa_kev_data.json +65 -5
- regscale/models/integration_models/synqly_models/capabilities.json +1 -1
- regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +17 -9
- regscale/models/regscale_models/assessment.py +2 -1
- regscale/models/regscale_models/control_objective.py +74 -5
- regscale/models/regscale_models/file.py +2 -0
- regscale/models/regscale_models/issue.py +2 -5
- {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.1.0.dist-info}/METADATA +1 -1
- {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.1.0.dist-info}/RECORD +113 -34
- tests/regscale/integrations/commercial/aws/__init__.py +0 -0
- tests/regscale/integrations/commercial/aws/test_audit_manager_compliance.py +1304 -0
- tests/regscale/integrations/commercial/aws/test_audit_manager_evidence_aggregation.py +341 -0
- tests/regscale/integrations/commercial/aws/test_aws_audit_manager_collector.py +1155 -0
- tests/regscale/integrations/commercial/aws/test_aws_cloudtrail_collector.py +534 -0
- tests/regscale/integrations/commercial/aws/test_aws_config_collector.py +400 -0
- tests/regscale/integrations/commercial/aws/test_aws_guardduty_collector.py +315 -0
- tests/regscale/integrations/commercial/aws/test_aws_iam_collector.py +458 -0
- tests/regscale/integrations/commercial/aws/test_aws_inspector_collector.py +353 -0
- tests/regscale/integrations/commercial/aws/test_aws_inventory_integration.py +530 -0
- tests/regscale/integrations/commercial/aws/test_aws_kms_collector.py +919 -0
- tests/regscale/integrations/commercial/aws/test_aws_s3_collector.py +722 -0
- tests/regscale/integrations/commercial/aws/test_aws_scanner_integration.py +722 -0
- tests/regscale/integrations/commercial/aws/test_aws_securityhub_collector.py +792 -0
- tests/regscale/integrations/commercial/aws/test_aws_systems_manager_collector.py +918 -0
- tests/regscale/integrations/commercial/aws/test_aws_vpc_collector.py +996 -0
- tests/regscale/integrations/commercial/aws/test_cli_evidence.py +431 -0
- tests/regscale/integrations/commercial/aws/test_cloudtrail_control_mappings.py +452 -0
- tests/regscale/integrations/commercial/aws/test_cloudtrail_evidence.py +788 -0
- tests/regscale/integrations/commercial/aws/test_config_compliance.py +298 -0
- tests/regscale/integrations/commercial/aws/test_conformance_pack_mappings.py +200 -0
- tests/regscale/integrations/commercial/aws/test_evidence_generator.py +386 -0
- tests/regscale/integrations/commercial/aws/test_guardduty_control_mappings.py +564 -0
- tests/regscale/integrations/commercial/aws/test_guardduty_evidence.py +1041 -0
- tests/regscale/integrations/commercial/aws/test_iam_control_mappings.py +718 -0
- tests/regscale/integrations/commercial/aws/test_iam_evidence.py +1375 -0
- tests/regscale/integrations/commercial/aws/test_kms_control_mappings.py +656 -0
- tests/regscale/integrations/commercial/aws/test_kms_evidence.py +1163 -0
- tests/regscale/integrations/commercial/aws/test_ocsf_mapper.py +370 -0
- tests/regscale/integrations/commercial/aws/test_org_control_mappings.py +546 -0
- tests/regscale/integrations/commercial/aws/test_org_evidence.py +1240 -0
- tests/regscale/integrations/commercial/aws/test_s3_control_mappings.py +672 -0
- tests/regscale/integrations/commercial/aws/test_s3_evidence.py +987 -0
- tests/regscale/integrations/commercial/aws/test_scanner_evidence.py +373 -0
- tests/regscale/integrations/commercial/aws/test_security_hub_config_filtering.py +539 -0
- tests/regscale/integrations/commercial/aws/test_session_manager.py +516 -0
- tests/regscale/integrations/commercial/aws/test_ssm_control_mappings.py +588 -0
- tests/regscale/integrations/commercial/aws/test_ssm_evidence.py +735 -0
- tests/regscale/integrations/commercial/test_aws.py +55 -56
- {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.1.0.dist-info}/LICENSE +0 -0
- {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.1.0.dist-info}/WHEEL +0 -0
- {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.1.0.dist-info}/entry_points.txt +0 -0
- {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.1.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,996 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Unit tests for AWS VPC collector."""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from unittest.mock import MagicMock, patch
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
from botocore.exceptions import ClientError
|
|
11
|
+
|
|
12
|
+
from regscale.integrations.commercial.aws.inventory.resources.vpc import VPCCollector
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger("regscale")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TestVPCCollector:
|
|
18
|
+
"""Test suite for VPCCollector."""
|
|
19
|
+
|
|
20
|
+
@pytest.fixture
|
|
21
|
+
def mock_session(self):
|
|
22
|
+
"""Create a mock boto3 session."""
|
|
23
|
+
session = MagicMock()
|
|
24
|
+
return session
|
|
25
|
+
|
|
26
|
+
@pytest.fixture
|
|
27
|
+
def mock_ec2_client(self):
|
|
28
|
+
"""Create a mock EC2 client."""
|
|
29
|
+
client = MagicMock()
|
|
30
|
+
return client
|
|
31
|
+
|
|
32
|
+
@pytest.fixture
|
|
33
|
+
def vpc_collector(self, mock_session):
|
|
34
|
+
"""Create a VPCCollector instance with mock session."""
|
|
35
|
+
return VPCCollector(session=mock_session, region="us-east-1", account_id="123456789012")
|
|
36
|
+
|
|
37
|
+
@pytest.fixture
|
|
38
|
+
def vpc_collector_no_filter(self, mock_session):
|
|
39
|
+
"""Create a VPCCollector instance without account filtering."""
|
|
40
|
+
return VPCCollector(session=mock_session, region="us-east-1", account_id=None)
|
|
41
|
+
|
|
42
|
+
# Test initialization
|
|
43
|
+
|
|
44
|
+
def test_vpc_collector_initialization(self, vpc_collector):
|
|
45
|
+
"""Test VPCCollector initialization with account_id."""
|
|
46
|
+
assert vpc_collector.region == "us-east-1"
|
|
47
|
+
assert vpc_collector.account_id == "123456789012"
|
|
48
|
+
|
|
49
|
+
def test_vpc_collector_initialization_no_filter(self, vpc_collector_no_filter):
|
|
50
|
+
"""Test VPCCollector initialization without account_id."""
|
|
51
|
+
assert vpc_collector_no_filter.region == "us-east-1"
|
|
52
|
+
assert vpc_collector_no_filter.account_id is None
|
|
53
|
+
|
|
54
|
+
# Test _matches_account_id
|
|
55
|
+
|
|
56
|
+
def test_matches_account_id_with_matching_owner(self, vpc_collector):
|
|
57
|
+
"""Test _matches_account_id with matching owner ID."""
|
|
58
|
+
assert vpc_collector._matches_account_id("123456789012") is True
|
|
59
|
+
|
|
60
|
+
def test_matches_account_id_with_non_matching_owner(self, vpc_collector):
|
|
61
|
+
"""Test _matches_account_id with non-matching owner ID."""
|
|
62
|
+
assert vpc_collector._matches_account_id("999999999999") is False
|
|
63
|
+
|
|
64
|
+
def test_matches_account_id_no_filter(self, vpc_collector_no_filter):
|
|
65
|
+
"""Test _matches_account_id when no account_id filter is set."""
|
|
66
|
+
assert vpc_collector_no_filter._matches_account_id("123456789012") is True
|
|
67
|
+
assert vpc_collector_no_filter._matches_account_id("999999999999") is True
|
|
68
|
+
|
|
69
|
+
# Test _get_vpc_attributes
|
|
70
|
+
|
|
71
|
+
def test_get_vpc_attributes_success(self, vpc_collector, mock_ec2_client):
|
|
72
|
+
"""Test _get_vpc_attributes with successful API calls."""
|
|
73
|
+
mock_ec2_client.describe_vpc_attribute.side_effect = [
|
|
74
|
+
{"EnableDnsSupport": {"Value": True}},
|
|
75
|
+
{"EnableDnsHostnames": {"Value": False}},
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
result = vpc_collector._get_vpc_attributes(mock_ec2_client, "vpc-12345")
|
|
79
|
+
|
|
80
|
+
assert result["EnableDnsSupport"] is True
|
|
81
|
+
assert result["EnableDnsHostnames"] is False
|
|
82
|
+
assert mock_ec2_client.describe_vpc_attribute.call_count == 2
|
|
83
|
+
|
|
84
|
+
def test_get_vpc_attributes_unauthorized(self, vpc_collector, mock_ec2_client):
|
|
85
|
+
"""Test _get_vpc_attributes with UnauthorizedOperation error."""
|
|
86
|
+
error_response = {"Error": {"Code": "UnauthorizedOperation"}}
|
|
87
|
+
mock_ec2_client.describe_vpc_attribute.side_effect = ClientError(error_response, "describe_vpc_attribute")
|
|
88
|
+
|
|
89
|
+
result = vpc_collector._get_vpc_attributes(mock_ec2_client, "vpc-12345")
|
|
90
|
+
|
|
91
|
+
assert result == {}
|
|
92
|
+
|
|
93
|
+
def test_get_vpc_attributes_vpc_not_found(self, vpc_collector, mock_ec2_client):
|
|
94
|
+
"""Test _get_vpc_attributes with InvalidVpcID.NotFound error."""
|
|
95
|
+
error_response = {"Error": {"Code": "InvalidVpcID.NotFound"}}
|
|
96
|
+
mock_ec2_client.describe_vpc_attribute.side_effect = ClientError(error_response, "describe_vpc_attribute")
|
|
97
|
+
|
|
98
|
+
result = vpc_collector._get_vpc_attributes(mock_ec2_client, "vpc-invalid")
|
|
99
|
+
|
|
100
|
+
assert result == {}
|
|
101
|
+
|
|
102
|
+
def test_get_vpc_attributes_other_error(self, vpc_collector, mock_ec2_client):
|
|
103
|
+
"""Test _get_vpc_attributes with other ClientError."""
|
|
104
|
+
error_response = {"Error": {"Code": "InternalError"}}
|
|
105
|
+
mock_ec2_client.describe_vpc_attribute.side_effect = ClientError(error_response, "describe_vpc_attribute")
|
|
106
|
+
|
|
107
|
+
result = vpc_collector._get_vpc_attributes(mock_ec2_client, "vpc-12345")
|
|
108
|
+
|
|
109
|
+
# Should return empty dict but log the error
|
|
110
|
+
assert result == {}
|
|
111
|
+
|
|
112
|
+
# Test _list_vpcs
|
|
113
|
+
|
|
114
|
+
def test_list_vpcs_success(self, vpc_collector, mock_ec2_client):
|
|
115
|
+
"""Test _list_vpcs with successful API call."""
|
|
116
|
+
mock_paginator = MagicMock()
|
|
117
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
118
|
+
mock_paginator.paginate.return_value = [
|
|
119
|
+
{
|
|
120
|
+
"Vpcs": [
|
|
121
|
+
{
|
|
122
|
+
"VpcId": "vpc-12345",
|
|
123
|
+
"OwnerId": "123456789012",
|
|
124
|
+
"CidrBlock": "10.0.0.0/16",
|
|
125
|
+
"CidrBlockAssociationSet": [],
|
|
126
|
+
"Ipv6CidrBlockAssociationSet": [],
|
|
127
|
+
"State": "available",
|
|
128
|
+
"IsDefault": False,
|
|
129
|
+
"DhcpOptionsId": "dopt-12345",
|
|
130
|
+
"InstanceTenancy": "default",
|
|
131
|
+
"Tags": [{"Key": "Name", "Value": "Test VPC"}],
|
|
132
|
+
}
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
# Mock _get_vpc_attributes
|
|
138
|
+
with patch.object(vpc_collector, "_get_vpc_attributes") as mock_get_attributes:
|
|
139
|
+
mock_get_attributes.return_value = {"EnableDnsSupport": True, "EnableDnsHostnames": True}
|
|
140
|
+
|
|
141
|
+
result = vpc_collector._list_vpcs(mock_ec2_client)
|
|
142
|
+
|
|
143
|
+
assert len(result) == 1
|
|
144
|
+
assert result[0]["VpcId"] == "vpc-12345"
|
|
145
|
+
assert result[0]["Region"] == "us-east-1"
|
|
146
|
+
assert result[0]["EnableDnsSupport"] is True
|
|
147
|
+
|
|
148
|
+
def test_list_vpcs_with_pagination(self, vpc_collector, mock_ec2_client):
|
|
149
|
+
"""Test _list_vpcs with multiple pages."""
|
|
150
|
+
mock_paginator = MagicMock()
|
|
151
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
152
|
+
mock_paginator.paginate.return_value = [
|
|
153
|
+
{"Vpcs": [{"VpcId": "vpc-1", "OwnerId": "123456789012", "CidrBlock": "10.0.0.0/16", "State": "available"}]},
|
|
154
|
+
{"Vpcs": [{"VpcId": "vpc-2", "OwnerId": "123456789012", "CidrBlock": "10.1.0.0/16", "State": "available"}]},
|
|
155
|
+
]
|
|
156
|
+
|
|
157
|
+
with patch.object(vpc_collector, "_get_vpc_attributes") as mock_get_attributes:
|
|
158
|
+
mock_get_attributes.return_value = {}
|
|
159
|
+
|
|
160
|
+
result = vpc_collector._list_vpcs(mock_ec2_client)
|
|
161
|
+
|
|
162
|
+
assert len(result) == 2
|
|
163
|
+
assert result[0]["VpcId"] == "vpc-1"
|
|
164
|
+
assert result[1]["VpcId"] == "vpc-2"
|
|
165
|
+
|
|
166
|
+
def test_list_vpcs_account_filtering(self, vpc_collector, mock_ec2_client):
|
|
167
|
+
"""Test _list_vpcs filters by account ID."""
|
|
168
|
+
mock_paginator = MagicMock()
|
|
169
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
170
|
+
mock_paginator.paginate.return_value = [
|
|
171
|
+
{
|
|
172
|
+
"Vpcs": [
|
|
173
|
+
{"VpcId": "vpc-1", "OwnerId": "123456789012", "CidrBlock": "10.0.0.0/16", "State": "available"},
|
|
174
|
+
{"VpcId": "vpc-2", "OwnerId": "999999999999", "CidrBlock": "10.1.0.0/16", "State": "available"},
|
|
175
|
+
]
|
|
176
|
+
}
|
|
177
|
+
]
|
|
178
|
+
|
|
179
|
+
with patch.object(vpc_collector, "_get_vpc_attributes") as mock_get_attributes:
|
|
180
|
+
mock_get_attributes.return_value = {}
|
|
181
|
+
|
|
182
|
+
result = vpc_collector._list_vpcs(mock_ec2_client)
|
|
183
|
+
|
|
184
|
+
# Should only include vpc-1 (matching account ID)
|
|
185
|
+
assert len(result) == 1
|
|
186
|
+
assert result[0]["VpcId"] == "vpc-1"
|
|
187
|
+
|
|
188
|
+
def test_list_vpcs_unauthorized(self, vpc_collector, mock_ec2_client):
|
|
189
|
+
"""Test _list_vpcs with UnauthorizedOperation error."""
|
|
190
|
+
mock_paginator = MagicMock()
|
|
191
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
192
|
+
error_response = {"Error": {"Code": "UnauthorizedOperation"}}
|
|
193
|
+
mock_paginator.paginate.side_effect = ClientError(error_response, "describe_vpcs")
|
|
194
|
+
|
|
195
|
+
result = vpc_collector._list_vpcs(mock_ec2_client)
|
|
196
|
+
|
|
197
|
+
assert result == []
|
|
198
|
+
|
|
199
|
+
def test_list_vpcs_other_error(self, vpc_collector, mock_ec2_client):
|
|
200
|
+
"""Test _list_vpcs with other ClientError."""
|
|
201
|
+
mock_paginator = MagicMock()
|
|
202
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
203
|
+
error_response = {"Error": {"Code": "InternalError"}}
|
|
204
|
+
mock_paginator.paginate.side_effect = ClientError(error_response, "describe_vpcs")
|
|
205
|
+
|
|
206
|
+
result = vpc_collector._list_vpcs(mock_ec2_client)
|
|
207
|
+
|
|
208
|
+
assert result == []
|
|
209
|
+
|
|
210
|
+
# Test _list_subnets
|
|
211
|
+
|
|
212
|
+
def test_list_subnets_success(self, vpc_collector, mock_ec2_client):
|
|
213
|
+
"""Test _list_subnets with successful API call."""
|
|
214
|
+
mock_paginator = MagicMock()
|
|
215
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
216
|
+
mock_paginator.paginate.return_value = [
|
|
217
|
+
{
|
|
218
|
+
"Subnets": [
|
|
219
|
+
{
|
|
220
|
+
"SubnetId": "subnet-12345",
|
|
221
|
+
"VpcId": "vpc-12345",
|
|
222
|
+
"OwnerId": "123456789012",
|
|
223
|
+
"AvailabilityZone": "us-east-1a",
|
|
224
|
+
"AvailabilityZoneId": "use1-az1",
|
|
225
|
+
"CidrBlock": "10.0.1.0/24",
|
|
226
|
+
"Ipv6CidrBlockAssociationSet": [],
|
|
227
|
+
"State": "available",
|
|
228
|
+
"AvailableIpAddressCount": 251,
|
|
229
|
+
"DefaultForAz": False,
|
|
230
|
+
"MapPublicIpOnLaunch": True,
|
|
231
|
+
"AssignIpv6AddressOnCreation": False,
|
|
232
|
+
"Tags": [{"Key": "Name", "Value": "Public Subnet"}],
|
|
233
|
+
}
|
|
234
|
+
]
|
|
235
|
+
}
|
|
236
|
+
]
|
|
237
|
+
|
|
238
|
+
result = vpc_collector._list_subnets(mock_ec2_client)
|
|
239
|
+
|
|
240
|
+
assert len(result) == 1
|
|
241
|
+
assert result[0]["SubnetId"] == "subnet-12345"
|
|
242
|
+
assert result[0]["Region"] == "us-east-1"
|
|
243
|
+
|
|
244
|
+
def test_list_subnets_account_filtering(self, vpc_collector, mock_ec2_client):
|
|
245
|
+
"""Test _list_subnets filters by account ID."""
|
|
246
|
+
mock_paginator = MagicMock()
|
|
247
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
248
|
+
mock_paginator.paginate.return_value = [
|
|
249
|
+
{
|
|
250
|
+
"Subnets": [
|
|
251
|
+
{"SubnetId": "subnet-1", "VpcId": "vpc-1", "OwnerId": "123456789012", "CidrBlock": "10.0.1.0/24"},
|
|
252
|
+
{"SubnetId": "subnet-2", "VpcId": "vpc-2", "OwnerId": "999999999999", "CidrBlock": "10.1.1.0/24"},
|
|
253
|
+
]
|
|
254
|
+
}
|
|
255
|
+
]
|
|
256
|
+
|
|
257
|
+
result = vpc_collector._list_subnets(mock_ec2_client)
|
|
258
|
+
|
|
259
|
+
assert len(result) == 1
|
|
260
|
+
assert result[0]["SubnetId"] == "subnet-1"
|
|
261
|
+
|
|
262
|
+
def test_list_subnets_unauthorized(self, vpc_collector, mock_ec2_client):
|
|
263
|
+
"""Test _list_subnets with UnauthorizedOperation error."""
|
|
264
|
+
mock_paginator = MagicMock()
|
|
265
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
266
|
+
error_response = {"Error": {"Code": "UnauthorizedOperation"}}
|
|
267
|
+
mock_paginator.paginate.side_effect = ClientError(error_response, "describe_subnets")
|
|
268
|
+
|
|
269
|
+
result = vpc_collector._list_subnets(mock_ec2_client)
|
|
270
|
+
|
|
271
|
+
assert result == []
|
|
272
|
+
|
|
273
|
+
def test_list_subnets_other_error(self, vpc_collector, mock_ec2_client):
|
|
274
|
+
"""Test _list_subnets with other ClientError."""
|
|
275
|
+
mock_paginator = MagicMock()
|
|
276
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
277
|
+
error_response = {"Error": {"Code": "InternalError"}}
|
|
278
|
+
mock_paginator.paginate.side_effect = ClientError(error_response, "describe_subnets")
|
|
279
|
+
|
|
280
|
+
result = vpc_collector._list_subnets(mock_ec2_client)
|
|
281
|
+
|
|
282
|
+
assert result == []
|
|
283
|
+
|
|
284
|
+
# Test _list_security_groups
|
|
285
|
+
|
|
286
|
+
def test_list_security_groups_success(self, vpc_collector, mock_ec2_client):
|
|
287
|
+
"""Test _list_security_groups with successful API call."""
|
|
288
|
+
mock_paginator = MagicMock()
|
|
289
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
290
|
+
mock_paginator.paginate.return_value = [
|
|
291
|
+
{
|
|
292
|
+
"SecurityGroups": [
|
|
293
|
+
{
|
|
294
|
+
"GroupId": "sg-12345",
|
|
295
|
+
"GroupName": "default",
|
|
296
|
+
"VpcId": "vpc-12345",
|
|
297
|
+
"OwnerId": "123456789012",
|
|
298
|
+
"Description": "Default security group",
|
|
299
|
+
"IpPermissions": [],
|
|
300
|
+
"IpPermissionsEgress": [],
|
|
301
|
+
"Tags": [],
|
|
302
|
+
}
|
|
303
|
+
]
|
|
304
|
+
}
|
|
305
|
+
]
|
|
306
|
+
|
|
307
|
+
result = vpc_collector._list_security_groups(mock_ec2_client)
|
|
308
|
+
|
|
309
|
+
assert len(result) == 1
|
|
310
|
+
assert result[0]["GroupId"] == "sg-12345"
|
|
311
|
+
assert result[0]["Region"] == "us-east-1"
|
|
312
|
+
|
|
313
|
+
def test_list_security_groups_account_filtering(self, vpc_collector, mock_ec2_client):
|
|
314
|
+
"""Test _list_security_groups filters by account ID."""
|
|
315
|
+
mock_paginator = MagicMock()
|
|
316
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
317
|
+
mock_paginator.paginate.return_value = [
|
|
318
|
+
{
|
|
319
|
+
"SecurityGroups": [
|
|
320
|
+
{"GroupId": "sg-1", "GroupName": "sg1", "OwnerId": "123456789012", "Description": "SG 1"},
|
|
321
|
+
{"GroupId": "sg-2", "GroupName": "sg2", "OwnerId": "999999999999", "Description": "SG 2"},
|
|
322
|
+
]
|
|
323
|
+
}
|
|
324
|
+
]
|
|
325
|
+
|
|
326
|
+
result = vpc_collector._list_security_groups(mock_ec2_client)
|
|
327
|
+
|
|
328
|
+
assert len(result) == 1
|
|
329
|
+
assert result[0]["GroupId"] == "sg-1"
|
|
330
|
+
|
|
331
|
+
def test_list_security_groups_unauthorized(self, vpc_collector, mock_ec2_client):
|
|
332
|
+
"""Test _list_security_groups with UnauthorizedOperation error."""
|
|
333
|
+
mock_paginator = MagicMock()
|
|
334
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
335
|
+
error_response = {"Error": {"Code": "UnauthorizedOperation"}}
|
|
336
|
+
mock_paginator.paginate.side_effect = ClientError(error_response, "describe_security_groups")
|
|
337
|
+
|
|
338
|
+
result = vpc_collector._list_security_groups(mock_ec2_client)
|
|
339
|
+
|
|
340
|
+
assert result == []
|
|
341
|
+
|
|
342
|
+
def test_list_security_groups_other_error(self, vpc_collector, mock_ec2_client):
|
|
343
|
+
"""Test _list_security_groups with other ClientError."""
|
|
344
|
+
mock_paginator = MagicMock()
|
|
345
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
346
|
+
error_response = {"Error": {"Code": "InternalError"}}
|
|
347
|
+
mock_paginator.paginate.side_effect = ClientError(error_response, "describe_security_groups")
|
|
348
|
+
|
|
349
|
+
result = vpc_collector._list_security_groups(mock_ec2_client)
|
|
350
|
+
|
|
351
|
+
assert result == []
|
|
352
|
+
|
|
353
|
+
# Test _list_network_acls
|
|
354
|
+
|
|
355
|
+
def test_list_network_acls_success(self, vpc_collector, mock_ec2_client):
|
|
356
|
+
"""Test _list_network_acls with successful API call."""
|
|
357
|
+
mock_paginator = MagicMock()
|
|
358
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
359
|
+
mock_paginator.paginate.return_value = [
|
|
360
|
+
{
|
|
361
|
+
"NetworkAcls": [
|
|
362
|
+
{
|
|
363
|
+
"NetworkAclId": "acl-12345",
|
|
364
|
+
"VpcId": "vpc-12345",
|
|
365
|
+
"OwnerId": "123456789012",
|
|
366
|
+
"IsDefault": True,
|
|
367
|
+
"Entries": [],
|
|
368
|
+
"Associations": [],
|
|
369
|
+
"Tags": [],
|
|
370
|
+
}
|
|
371
|
+
]
|
|
372
|
+
}
|
|
373
|
+
]
|
|
374
|
+
|
|
375
|
+
result = vpc_collector._list_network_acls(mock_ec2_client)
|
|
376
|
+
|
|
377
|
+
assert len(result) == 1
|
|
378
|
+
assert result[0]["NetworkAclId"] == "acl-12345"
|
|
379
|
+
assert result[0]["Region"] == "us-east-1"
|
|
380
|
+
|
|
381
|
+
def test_list_network_acls_account_filtering(self, vpc_collector, mock_ec2_client):
|
|
382
|
+
"""Test _list_network_acls filters by account ID."""
|
|
383
|
+
mock_paginator = MagicMock()
|
|
384
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
385
|
+
mock_paginator.paginate.return_value = [
|
|
386
|
+
{
|
|
387
|
+
"NetworkAcls": [
|
|
388
|
+
{"NetworkAclId": "acl-1", "VpcId": "vpc-1", "OwnerId": "123456789012", "IsDefault": True},
|
|
389
|
+
{"NetworkAclId": "acl-2", "VpcId": "vpc-2", "OwnerId": "999999999999", "IsDefault": False},
|
|
390
|
+
]
|
|
391
|
+
}
|
|
392
|
+
]
|
|
393
|
+
|
|
394
|
+
result = vpc_collector._list_network_acls(mock_ec2_client)
|
|
395
|
+
|
|
396
|
+
assert len(result) == 1
|
|
397
|
+
assert result[0]["NetworkAclId"] == "acl-1"
|
|
398
|
+
|
|
399
|
+
def test_list_network_acls_unauthorized(self, vpc_collector, mock_ec2_client):
|
|
400
|
+
"""Test _list_network_acls with UnauthorizedOperation error."""
|
|
401
|
+
mock_paginator = MagicMock()
|
|
402
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
403
|
+
error_response = {"Error": {"Code": "UnauthorizedOperation"}}
|
|
404
|
+
mock_paginator.paginate.side_effect = ClientError(error_response, "describe_network_acls")
|
|
405
|
+
|
|
406
|
+
result = vpc_collector._list_network_acls(mock_ec2_client)
|
|
407
|
+
|
|
408
|
+
assert result == []
|
|
409
|
+
|
|
410
|
+
def test_list_network_acls_other_error(self, vpc_collector, mock_ec2_client):
|
|
411
|
+
"""Test _list_network_acls with other ClientError."""
|
|
412
|
+
mock_paginator = MagicMock()
|
|
413
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
414
|
+
error_response = {"Error": {"Code": "InternalError"}}
|
|
415
|
+
mock_paginator.paginate.side_effect = ClientError(error_response, "describe_network_acls")
|
|
416
|
+
|
|
417
|
+
result = vpc_collector._list_network_acls(mock_ec2_client)
|
|
418
|
+
|
|
419
|
+
assert result == []
|
|
420
|
+
|
|
421
|
+
# Test _list_route_tables
|
|
422
|
+
|
|
423
|
+
def test_list_route_tables_success(self, vpc_collector, mock_ec2_client):
|
|
424
|
+
"""Test _list_route_tables with successful API call."""
|
|
425
|
+
mock_paginator = MagicMock()
|
|
426
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
427
|
+
mock_paginator.paginate.return_value = [
|
|
428
|
+
{
|
|
429
|
+
"RouteTables": [
|
|
430
|
+
{
|
|
431
|
+
"RouteTableId": "rtb-12345",
|
|
432
|
+
"VpcId": "vpc-12345",
|
|
433
|
+
"OwnerId": "123456789012",
|
|
434
|
+
"Routes": [],
|
|
435
|
+
"Associations": [],
|
|
436
|
+
"PropagatingVgws": [],
|
|
437
|
+
"Tags": [],
|
|
438
|
+
}
|
|
439
|
+
]
|
|
440
|
+
}
|
|
441
|
+
]
|
|
442
|
+
|
|
443
|
+
result = vpc_collector._list_route_tables(mock_ec2_client)
|
|
444
|
+
|
|
445
|
+
assert len(result) == 1
|
|
446
|
+
assert result[0]["RouteTableId"] == "rtb-12345"
|
|
447
|
+
assert result[0]["Region"] == "us-east-1"
|
|
448
|
+
|
|
449
|
+
def test_list_route_tables_account_filtering(self, vpc_collector, mock_ec2_client):
|
|
450
|
+
"""Test _list_route_tables filters by account ID."""
|
|
451
|
+
mock_paginator = MagicMock()
|
|
452
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
453
|
+
mock_paginator.paginate.return_value = [
|
|
454
|
+
{
|
|
455
|
+
"RouteTables": [
|
|
456
|
+
{"RouteTableId": "rtb-1", "VpcId": "vpc-1", "OwnerId": "123456789012"},
|
|
457
|
+
{"RouteTableId": "rtb-2", "VpcId": "vpc-2", "OwnerId": "999999999999"},
|
|
458
|
+
]
|
|
459
|
+
}
|
|
460
|
+
]
|
|
461
|
+
|
|
462
|
+
result = vpc_collector._list_route_tables(mock_ec2_client)
|
|
463
|
+
|
|
464
|
+
assert len(result) == 1
|
|
465
|
+
assert result[0]["RouteTableId"] == "rtb-1"
|
|
466
|
+
|
|
467
|
+
def test_list_route_tables_unauthorized(self, vpc_collector, mock_ec2_client):
|
|
468
|
+
"""Test _list_route_tables with UnauthorizedOperation error."""
|
|
469
|
+
mock_paginator = MagicMock()
|
|
470
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
471
|
+
error_response = {"Error": {"Code": "UnauthorizedOperation"}}
|
|
472
|
+
mock_paginator.paginate.side_effect = ClientError(error_response, "describe_route_tables")
|
|
473
|
+
|
|
474
|
+
result = vpc_collector._list_route_tables(mock_ec2_client)
|
|
475
|
+
|
|
476
|
+
assert result == []
|
|
477
|
+
|
|
478
|
+
def test_list_route_tables_other_error(self, vpc_collector, mock_ec2_client):
|
|
479
|
+
"""Test _list_route_tables with other ClientError."""
|
|
480
|
+
mock_paginator = MagicMock()
|
|
481
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
482
|
+
error_response = {"Error": {"Code": "InternalError"}}
|
|
483
|
+
mock_paginator.paginate.side_effect = ClientError(error_response, "describe_route_tables")
|
|
484
|
+
|
|
485
|
+
result = vpc_collector._list_route_tables(mock_ec2_client)
|
|
486
|
+
|
|
487
|
+
assert result == []
|
|
488
|
+
|
|
489
|
+
# Test _list_internet_gateways
|
|
490
|
+
|
|
491
|
+
def test_list_internet_gateways_success(self, vpc_collector, mock_ec2_client):
|
|
492
|
+
"""Test _list_internet_gateways with successful API call."""
|
|
493
|
+
mock_paginator = MagicMock()
|
|
494
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
495
|
+
mock_paginator.paginate.return_value = [
|
|
496
|
+
{
|
|
497
|
+
"InternetGateways": [
|
|
498
|
+
{
|
|
499
|
+
"InternetGatewayId": "igw-12345",
|
|
500
|
+
"OwnerId": "123456789012",
|
|
501
|
+
"Attachments": [{"State": "available", "VpcId": "vpc-12345"}],
|
|
502
|
+
"Tags": [],
|
|
503
|
+
}
|
|
504
|
+
]
|
|
505
|
+
}
|
|
506
|
+
]
|
|
507
|
+
|
|
508
|
+
result = vpc_collector._list_internet_gateways(mock_ec2_client)
|
|
509
|
+
|
|
510
|
+
assert len(result) == 1
|
|
511
|
+
assert result[0]["InternetGatewayId"] == "igw-12345"
|
|
512
|
+
assert result[0]["Region"] == "us-east-1"
|
|
513
|
+
|
|
514
|
+
def test_list_internet_gateways_account_filtering(self, vpc_collector, mock_ec2_client):
|
|
515
|
+
"""Test _list_internet_gateways filters by account ID."""
|
|
516
|
+
mock_paginator = MagicMock()
|
|
517
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
518
|
+
mock_paginator.paginate.return_value = [
|
|
519
|
+
{
|
|
520
|
+
"InternetGateways": [
|
|
521
|
+
{"InternetGatewayId": "igw-1", "OwnerId": "123456789012", "Attachments": []},
|
|
522
|
+
{"InternetGatewayId": "igw-2", "OwnerId": "999999999999", "Attachments": []},
|
|
523
|
+
]
|
|
524
|
+
}
|
|
525
|
+
]
|
|
526
|
+
|
|
527
|
+
result = vpc_collector._list_internet_gateways(mock_ec2_client)
|
|
528
|
+
|
|
529
|
+
assert len(result) == 1
|
|
530
|
+
assert result[0]["InternetGatewayId"] == "igw-1"
|
|
531
|
+
|
|
532
|
+
def test_list_internet_gateways_unauthorized(self, vpc_collector, mock_ec2_client):
|
|
533
|
+
"""Test _list_internet_gateways with UnauthorizedOperation error."""
|
|
534
|
+
mock_paginator = MagicMock()
|
|
535
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
536
|
+
error_response = {"Error": {"Code": "UnauthorizedOperation"}}
|
|
537
|
+
mock_paginator.paginate.side_effect = ClientError(error_response, "describe_internet_gateways")
|
|
538
|
+
|
|
539
|
+
result = vpc_collector._list_internet_gateways(mock_ec2_client)
|
|
540
|
+
|
|
541
|
+
assert result == []
|
|
542
|
+
|
|
543
|
+
def test_list_internet_gateways_other_error(self, vpc_collector, mock_ec2_client):
|
|
544
|
+
"""Test _list_internet_gateways with other ClientError."""
|
|
545
|
+
mock_paginator = MagicMock()
|
|
546
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
547
|
+
error_response = {"Error": {"Code": "InternalError"}}
|
|
548
|
+
mock_paginator.paginate.side_effect = ClientError(error_response, "describe_internet_gateways")
|
|
549
|
+
|
|
550
|
+
result = vpc_collector._list_internet_gateways(mock_ec2_client)
|
|
551
|
+
|
|
552
|
+
assert result == []
|
|
553
|
+
|
|
554
|
+
# Test _list_nat_gateways
|
|
555
|
+
|
|
556
|
+
def test_list_nat_gateways_success(self, vpc_collector, mock_ec2_client):
|
|
557
|
+
"""Test _list_nat_gateways with successful API call."""
|
|
558
|
+
mock_paginator = MagicMock()
|
|
559
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
560
|
+
mock_paginator.paginate.return_value = [
|
|
561
|
+
{
|
|
562
|
+
"NatGateways": [
|
|
563
|
+
{
|
|
564
|
+
"NatGatewayId": "nat-12345",
|
|
565
|
+
"VpcId": "vpc-12345",
|
|
566
|
+
"SubnetId": "subnet-12345",
|
|
567
|
+
"State": "available",
|
|
568
|
+
"ConnectivityType": "public",
|
|
569
|
+
"NatGatewayAddresses": [],
|
|
570
|
+
"CreateTime": datetime(2024, 1, 1),
|
|
571
|
+
"DeleteTime": None,
|
|
572
|
+
"Tags": [],
|
|
573
|
+
}
|
|
574
|
+
]
|
|
575
|
+
}
|
|
576
|
+
]
|
|
577
|
+
|
|
578
|
+
result = vpc_collector._list_nat_gateways(mock_ec2_client)
|
|
579
|
+
|
|
580
|
+
assert len(result) == 1
|
|
581
|
+
assert result[0]["NatGatewayId"] == "nat-12345"
|
|
582
|
+
assert result[0]["Region"] == "us-east-1"
|
|
583
|
+
|
|
584
|
+
def test_list_nat_gateways_with_delete_time(self, vpc_collector, mock_ec2_client):
|
|
585
|
+
"""Test _list_nat_gateways with DeleteTime present."""
|
|
586
|
+
mock_paginator = MagicMock()
|
|
587
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
588
|
+
mock_paginator.paginate.return_value = [
|
|
589
|
+
{
|
|
590
|
+
"NatGateways": [
|
|
591
|
+
{
|
|
592
|
+
"NatGatewayId": "nat-12345",
|
|
593
|
+
"VpcId": "vpc-12345",
|
|
594
|
+
"SubnetId": "subnet-12345",
|
|
595
|
+
"State": "deleted",
|
|
596
|
+
"ConnectivityType": "public",
|
|
597
|
+
"NatGatewayAddresses": [],
|
|
598
|
+
"CreateTime": datetime(2024, 1, 1),
|
|
599
|
+
"DeleteTime": datetime(2024, 1, 2),
|
|
600
|
+
"Tags": [],
|
|
601
|
+
}
|
|
602
|
+
]
|
|
603
|
+
}
|
|
604
|
+
]
|
|
605
|
+
|
|
606
|
+
result = vpc_collector._list_nat_gateways(mock_ec2_client)
|
|
607
|
+
|
|
608
|
+
assert len(result) == 1
|
|
609
|
+
assert result[0]["DeleteTime"] is not None
|
|
610
|
+
|
|
611
|
+
def test_list_nat_gateways_unauthorized(self, vpc_collector, mock_ec2_client):
|
|
612
|
+
"""Test _list_nat_gateways with UnauthorizedOperation error."""
|
|
613
|
+
mock_paginator = MagicMock()
|
|
614
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
615
|
+
error_response = {"Error": {"Code": "UnauthorizedOperation"}}
|
|
616
|
+
mock_paginator.paginate.side_effect = ClientError(error_response, "describe_nat_gateways")
|
|
617
|
+
|
|
618
|
+
result = vpc_collector._list_nat_gateways(mock_ec2_client)
|
|
619
|
+
|
|
620
|
+
assert result == []
|
|
621
|
+
|
|
622
|
+
def test_list_nat_gateways_other_error(self, vpc_collector, mock_ec2_client):
|
|
623
|
+
"""Test _list_nat_gateways with other ClientError."""
|
|
624
|
+
mock_paginator = MagicMock()
|
|
625
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
626
|
+
error_response = {"Error": {"Code": "InternalError"}}
|
|
627
|
+
mock_paginator.paginate.side_effect = ClientError(error_response, "describe_nat_gateways")
|
|
628
|
+
|
|
629
|
+
result = vpc_collector._list_nat_gateways(mock_ec2_client)
|
|
630
|
+
|
|
631
|
+
assert result == []
|
|
632
|
+
|
|
633
|
+
# Test _list_vpc_endpoints
|
|
634
|
+
|
|
635
|
+
def test_list_vpc_endpoints_success(self, vpc_collector, mock_ec2_client):
|
|
636
|
+
"""Test _list_vpc_endpoints with successful API call."""
|
|
637
|
+
mock_paginator = MagicMock()
|
|
638
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
639
|
+
mock_paginator.paginate.return_value = [
|
|
640
|
+
{
|
|
641
|
+
"VpcEndpoints": [
|
|
642
|
+
{
|
|
643
|
+
"VpcEndpointId": "vpce-12345",
|
|
644
|
+
"VpcId": "vpc-12345",
|
|
645
|
+
"OwnerId": "123456789012",
|
|
646
|
+
"ServiceName": "com.amazonaws.us-east-1.s3",
|
|
647
|
+
"VpcEndpointType": "Gateway",
|
|
648
|
+
"State": "available",
|
|
649
|
+
"PolicyDocument": '{"Version": "2012-10-17"}',
|
|
650
|
+
"SubnetIds": [],
|
|
651
|
+
"RouteTableIds": ["rtb-12345"],
|
|
652
|
+
"Groups": [],
|
|
653
|
+
"PrivateDnsEnabled": False,
|
|
654
|
+
"DnsEntries": [],
|
|
655
|
+
"CreationTimestamp": datetime(2024, 1, 1),
|
|
656
|
+
"Tags": [],
|
|
657
|
+
}
|
|
658
|
+
]
|
|
659
|
+
}
|
|
660
|
+
]
|
|
661
|
+
|
|
662
|
+
result = vpc_collector._list_vpc_endpoints(mock_ec2_client)
|
|
663
|
+
|
|
664
|
+
assert len(result) == 1
|
|
665
|
+
assert result[0]["VpcEndpointId"] == "vpce-12345"
|
|
666
|
+
assert result[0]["Region"] == "us-east-1"
|
|
667
|
+
|
|
668
|
+
def test_list_vpc_endpoints_account_filtering(self, vpc_collector, mock_ec2_client):
|
|
669
|
+
"""Test _list_vpc_endpoints filters by account ID."""
|
|
670
|
+
mock_paginator = MagicMock()
|
|
671
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
672
|
+
mock_paginator.paginate.return_value = [
|
|
673
|
+
{
|
|
674
|
+
"VpcEndpoints": [
|
|
675
|
+
{
|
|
676
|
+
"VpcEndpointId": "vpce-1",
|
|
677
|
+
"VpcId": "vpc-1",
|
|
678
|
+
"OwnerId": "123456789012",
|
|
679
|
+
"ServiceName": "com.amazonaws.us-east-1.s3",
|
|
680
|
+
"VpcEndpointType": "Gateway",
|
|
681
|
+
"State": "available",
|
|
682
|
+
"CreationTimestamp": datetime(2024, 1, 1),
|
|
683
|
+
},
|
|
684
|
+
{
|
|
685
|
+
"VpcEndpointId": "vpce-2",
|
|
686
|
+
"VpcId": "vpc-2",
|
|
687
|
+
"OwnerId": "999999999999",
|
|
688
|
+
"ServiceName": "com.amazonaws.us-east-1.ec2",
|
|
689
|
+
"VpcEndpointType": "Interface",
|
|
690
|
+
"State": "available",
|
|
691
|
+
"CreationTimestamp": datetime(2024, 1, 1),
|
|
692
|
+
},
|
|
693
|
+
]
|
|
694
|
+
}
|
|
695
|
+
]
|
|
696
|
+
|
|
697
|
+
result = vpc_collector._list_vpc_endpoints(mock_ec2_client)
|
|
698
|
+
|
|
699
|
+
assert len(result) == 1
|
|
700
|
+
assert result[0]["VpcEndpointId"] == "vpce-1"
|
|
701
|
+
|
|
702
|
+
def test_list_vpc_endpoints_unauthorized(self, vpc_collector, mock_ec2_client):
|
|
703
|
+
"""Test _list_vpc_endpoints with UnauthorizedOperation error."""
|
|
704
|
+
mock_paginator = MagicMock()
|
|
705
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
706
|
+
error_response = {"Error": {"Code": "UnauthorizedOperation"}}
|
|
707
|
+
mock_paginator.paginate.side_effect = ClientError(error_response, "describe_vpc_endpoints")
|
|
708
|
+
|
|
709
|
+
result = vpc_collector._list_vpc_endpoints(mock_ec2_client)
|
|
710
|
+
|
|
711
|
+
assert result == []
|
|
712
|
+
|
|
713
|
+
def test_list_vpc_endpoints_other_error(self, vpc_collector, mock_ec2_client):
|
|
714
|
+
"""Test _list_vpc_endpoints with other ClientError."""
|
|
715
|
+
mock_paginator = MagicMock()
|
|
716
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
717
|
+
error_response = {"Error": {"Code": "InternalError"}}
|
|
718
|
+
mock_paginator.paginate.side_effect = ClientError(error_response, "describe_vpc_endpoints")
|
|
719
|
+
|
|
720
|
+
result = vpc_collector._list_vpc_endpoints(mock_ec2_client)
|
|
721
|
+
|
|
722
|
+
assert result == []
|
|
723
|
+
|
|
724
|
+
# Test _list_vpc_peering_connections
|
|
725
|
+
|
|
726
|
+
def test_list_vpc_peering_connections_success(self, vpc_collector, mock_ec2_client):
|
|
727
|
+
"""Test _list_vpc_peering_connections with successful API call."""
|
|
728
|
+
mock_paginator = MagicMock()
|
|
729
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
730
|
+
mock_paginator.paginate.return_value = [
|
|
731
|
+
{
|
|
732
|
+
"VpcPeeringConnections": [
|
|
733
|
+
{
|
|
734
|
+
"VpcPeeringConnectionId": "pcx-12345",
|
|
735
|
+
"RequesterVpcInfo": {
|
|
736
|
+
"VpcId": "vpc-12345",
|
|
737
|
+
"OwnerId": "123456789012",
|
|
738
|
+
"CidrBlock": "10.0.0.0/16",
|
|
739
|
+
"Region": "us-east-1",
|
|
740
|
+
},
|
|
741
|
+
"AccepterVpcInfo": {
|
|
742
|
+
"VpcId": "vpc-67890",
|
|
743
|
+
"OwnerId": "999999999999",
|
|
744
|
+
"CidrBlock": "10.1.0.0/16",
|
|
745
|
+
"Region": "us-west-2",
|
|
746
|
+
},
|
|
747
|
+
"Status": {"Code": "active"},
|
|
748
|
+
"ExpirationTime": None,
|
|
749
|
+
"Tags": [],
|
|
750
|
+
}
|
|
751
|
+
]
|
|
752
|
+
}
|
|
753
|
+
]
|
|
754
|
+
|
|
755
|
+
result = vpc_collector._list_vpc_peering_connections(mock_ec2_client)
|
|
756
|
+
|
|
757
|
+
assert len(result) == 1
|
|
758
|
+
assert result[0]["VpcPeeringConnectionId"] == "pcx-12345"
|
|
759
|
+
assert result[0]["Region"] == "us-east-1"
|
|
760
|
+
|
|
761
|
+
def test_list_vpc_peering_connections_requester_filter(self, vpc_collector, mock_ec2_client):
|
|
762
|
+
"""Test _list_vpc_peering_connections filters by requester account ID."""
|
|
763
|
+
mock_paginator = MagicMock()
|
|
764
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
765
|
+
mock_paginator.paginate.return_value = [
|
|
766
|
+
{
|
|
767
|
+
"VpcPeeringConnections": [
|
|
768
|
+
{
|
|
769
|
+
"VpcPeeringConnectionId": "pcx-1",
|
|
770
|
+
"RequesterVpcInfo": {"VpcId": "vpc-1", "OwnerId": "123456789012"},
|
|
771
|
+
"AccepterVpcInfo": {"VpcId": "vpc-2", "OwnerId": "999999999999"},
|
|
772
|
+
"Status": {"Code": "active"},
|
|
773
|
+
},
|
|
774
|
+
{
|
|
775
|
+
"VpcPeeringConnectionId": "pcx-2",
|
|
776
|
+
"RequesterVpcInfo": {"VpcId": "vpc-3", "OwnerId": "888888888888"},
|
|
777
|
+
"AccepterVpcInfo": {"VpcId": "vpc-4", "OwnerId": "777777777777"},
|
|
778
|
+
"Status": {"Code": "active"},
|
|
779
|
+
},
|
|
780
|
+
]
|
|
781
|
+
}
|
|
782
|
+
]
|
|
783
|
+
|
|
784
|
+
result = vpc_collector._list_vpc_peering_connections(mock_ec2_client)
|
|
785
|
+
|
|
786
|
+
# Should include pcx-1 (requester matches account_id)
|
|
787
|
+
assert len(result) == 1
|
|
788
|
+
assert result[0]["VpcPeeringConnectionId"] == "pcx-1"
|
|
789
|
+
|
|
790
|
+
def test_list_vpc_peering_connections_accepter_filter(self, vpc_collector, mock_ec2_client):
|
|
791
|
+
"""Test _list_vpc_peering_connections filters by accepter account ID."""
|
|
792
|
+
mock_paginator = MagicMock()
|
|
793
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
794
|
+
mock_paginator.paginate.return_value = [
|
|
795
|
+
{
|
|
796
|
+
"VpcPeeringConnections": [
|
|
797
|
+
{
|
|
798
|
+
"VpcPeeringConnectionId": "pcx-1",
|
|
799
|
+
"RequesterVpcInfo": {"VpcId": "vpc-1", "OwnerId": "999999999999"},
|
|
800
|
+
"AccepterVpcInfo": {"VpcId": "vpc-2", "OwnerId": "123456789012"},
|
|
801
|
+
"Status": {"Code": "active"},
|
|
802
|
+
}
|
|
803
|
+
]
|
|
804
|
+
}
|
|
805
|
+
]
|
|
806
|
+
|
|
807
|
+
result = vpc_collector._list_vpc_peering_connections(mock_ec2_client)
|
|
808
|
+
|
|
809
|
+
# Should include pcx-1 (accepter matches account_id)
|
|
810
|
+
assert len(result) == 1
|
|
811
|
+
assert result[0]["VpcPeeringConnectionId"] == "pcx-1"
|
|
812
|
+
|
|
813
|
+
def test_list_vpc_peering_connections_with_expiration(self, vpc_collector, mock_ec2_client):
|
|
814
|
+
"""Test _list_vpc_peering_connections with ExpirationTime."""
|
|
815
|
+
mock_paginator = MagicMock()
|
|
816
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
817
|
+
mock_paginator.paginate.return_value = [
|
|
818
|
+
{
|
|
819
|
+
"VpcPeeringConnections": [
|
|
820
|
+
{
|
|
821
|
+
"VpcPeeringConnectionId": "pcx-1",
|
|
822
|
+
"RequesterVpcInfo": {"VpcId": "vpc-1", "OwnerId": "123456789012"},
|
|
823
|
+
"AccepterVpcInfo": {"VpcId": "vpc-2", "OwnerId": "999999999999"},
|
|
824
|
+
"Status": {"Code": "expired", "Message": "Expired"},
|
|
825
|
+
"ExpirationTime": datetime(2024, 1, 1),
|
|
826
|
+
"Tags": [],
|
|
827
|
+
}
|
|
828
|
+
]
|
|
829
|
+
}
|
|
830
|
+
]
|
|
831
|
+
|
|
832
|
+
result = vpc_collector._list_vpc_peering_connections(mock_ec2_client)
|
|
833
|
+
|
|
834
|
+
assert len(result) == 1
|
|
835
|
+
assert result[0]["ExpirationTime"] is not None
|
|
836
|
+
assert result[0]["StatusMessage"] == "Expired"
|
|
837
|
+
|
|
838
|
+
def test_list_vpc_peering_connections_unauthorized(self, vpc_collector, mock_ec2_client):
|
|
839
|
+
"""Test _list_vpc_peering_connections with UnauthorizedOperation error."""
|
|
840
|
+
mock_paginator = MagicMock()
|
|
841
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
842
|
+
error_response = {"Error": {"Code": "UnauthorizedOperation"}}
|
|
843
|
+
mock_paginator.paginate.side_effect = ClientError(error_response, "describe_vpc_peering_connections")
|
|
844
|
+
|
|
845
|
+
result = vpc_collector._list_vpc_peering_connections(mock_ec2_client)
|
|
846
|
+
|
|
847
|
+
assert result == []
|
|
848
|
+
|
|
849
|
+
def test_list_vpc_peering_connections_other_error(self, vpc_collector, mock_ec2_client):
|
|
850
|
+
"""Test _list_vpc_peering_connections with other ClientError."""
|
|
851
|
+
mock_paginator = MagicMock()
|
|
852
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
853
|
+
error_response = {"Error": {"Code": "InternalError"}}
|
|
854
|
+
mock_paginator.paginate.side_effect = ClientError(error_response, "describe_vpc_peering_connections")
|
|
855
|
+
|
|
856
|
+
result = vpc_collector._list_vpc_peering_connections(mock_ec2_client)
|
|
857
|
+
|
|
858
|
+
assert result == []
|
|
859
|
+
|
|
860
|
+
def test_list_vpc_peering_connections_no_match_filter(self, vpc_collector, mock_ec2_client):
|
|
861
|
+
"""Test _list_vpc_peering_connections filters out non-matching peering connections."""
|
|
862
|
+
mock_paginator = MagicMock()
|
|
863
|
+
mock_ec2_client.get_paginator.return_value = mock_paginator
|
|
864
|
+
mock_paginator.paginate.return_value = [
|
|
865
|
+
{
|
|
866
|
+
"VpcPeeringConnections": [
|
|
867
|
+
{
|
|
868
|
+
"VpcPeeringConnectionId": "pcx-1",
|
|
869
|
+
"RequesterVpcInfo": {"VpcId": "vpc-1", "OwnerId": "888888888888"},
|
|
870
|
+
"AccepterVpcInfo": {"VpcId": "vpc-2", "OwnerId": "999999999999"},
|
|
871
|
+
"Status": {"Code": "active"},
|
|
872
|
+
}
|
|
873
|
+
]
|
|
874
|
+
}
|
|
875
|
+
]
|
|
876
|
+
|
|
877
|
+
result = vpc_collector._list_vpc_peering_connections(mock_ec2_client)
|
|
878
|
+
|
|
879
|
+
# Should not include pcx-1 (neither requester nor accepter matches account_id)
|
|
880
|
+
assert len(result) == 0
|
|
881
|
+
|
|
882
|
+
# Test collect method
|
|
883
|
+
|
|
884
|
+
def test_collect_success(self, vpc_collector, mock_session):
|
|
885
|
+
"""Test collect method with successful API calls."""
|
|
886
|
+
mock_ec2_client = MagicMock()
|
|
887
|
+
mock_session.client.return_value = mock_ec2_client
|
|
888
|
+
|
|
889
|
+
# Mock all list methods
|
|
890
|
+
with patch.object(vpc_collector, "_list_vpcs") as mock_vpcs, patch.object(
|
|
891
|
+
vpc_collector, "_list_subnets"
|
|
892
|
+
) as mock_subnets, patch.object(vpc_collector, "_list_security_groups") as mock_sgs, patch.object(
|
|
893
|
+
vpc_collector, "_list_network_acls"
|
|
894
|
+
) as mock_acls, patch.object(
|
|
895
|
+
vpc_collector, "_list_route_tables"
|
|
896
|
+
) as mock_route_tables, patch.object(
|
|
897
|
+
vpc_collector, "_list_internet_gateways"
|
|
898
|
+
) as mock_igws, patch.object(
|
|
899
|
+
vpc_collector, "_list_nat_gateways"
|
|
900
|
+
) as mock_nats, patch.object(
|
|
901
|
+
vpc_collector, "_list_vpc_endpoints"
|
|
902
|
+
) as mock_endpoints, patch.object(
|
|
903
|
+
vpc_collector, "_list_vpc_peering_connections"
|
|
904
|
+
) as mock_peering:
|
|
905
|
+
mock_vpcs.return_value = [{"VpcId": "vpc-1"}]
|
|
906
|
+
mock_subnets.return_value = [{"SubnetId": "subnet-1"}]
|
|
907
|
+
mock_sgs.return_value = [{"GroupId": "sg-1"}]
|
|
908
|
+
mock_acls.return_value = [{"NetworkAclId": "acl-1"}]
|
|
909
|
+
mock_route_tables.return_value = [{"RouteTableId": "rtb-1"}]
|
|
910
|
+
mock_igws.return_value = [{"InternetGatewayId": "igw-1"}]
|
|
911
|
+
mock_nats.return_value = [{"NatGatewayId": "nat-1"}]
|
|
912
|
+
mock_endpoints.return_value = [{"VpcEndpointId": "vpce-1"}]
|
|
913
|
+
mock_peering.return_value = [{"VpcPeeringConnectionId": "pcx-1"}]
|
|
914
|
+
|
|
915
|
+
result = vpc_collector.collect()
|
|
916
|
+
|
|
917
|
+
assert len(result["VPCs"]) == 1
|
|
918
|
+
assert len(result["Subnets"]) == 1
|
|
919
|
+
assert len(result["SecurityGroups"]) == 1
|
|
920
|
+
assert len(result["NetworkACLs"]) == 1
|
|
921
|
+
assert len(result["RouteTables"]) == 1
|
|
922
|
+
assert len(result["InternetGateways"]) == 1
|
|
923
|
+
assert len(result["NATGateways"]) == 1
|
|
924
|
+
assert len(result["VPCEndpoints"]) == 1
|
|
925
|
+
assert len(result["VPCPeeringConnections"]) == 1
|
|
926
|
+
|
|
927
|
+
def test_collect_with_client_error(self, vpc_collector, mock_session):
|
|
928
|
+
"""Test collect method with ClientError."""
|
|
929
|
+
mock_ec2_client = MagicMock()
|
|
930
|
+
mock_session.client.return_value = mock_ec2_client
|
|
931
|
+
|
|
932
|
+
error_response = {"Error": {"Code": "AccessDeniedException"}}
|
|
933
|
+
|
|
934
|
+
with patch.object(vpc_collector, "_list_vpcs") as mock_vpcs:
|
|
935
|
+
mock_vpcs.side_effect = ClientError(error_response, "describe_vpcs")
|
|
936
|
+
|
|
937
|
+
result = vpc_collector.collect()
|
|
938
|
+
|
|
939
|
+
# Should return empty structure but not crash
|
|
940
|
+
assert result["VPCs"] == []
|
|
941
|
+
|
|
942
|
+
def test_collect_with_unexpected_error(self, vpc_collector, mock_session):
|
|
943
|
+
"""Test collect method with unexpected error."""
|
|
944
|
+
mock_ec2_client = MagicMock()
|
|
945
|
+
mock_session.client.return_value = mock_ec2_client
|
|
946
|
+
|
|
947
|
+
with patch.object(vpc_collector, "_list_vpcs") as mock_vpcs:
|
|
948
|
+
mock_vpcs.side_effect = Exception("Unexpected error")
|
|
949
|
+
|
|
950
|
+
result = vpc_collector.collect()
|
|
951
|
+
|
|
952
|
+
# Should return empty structure but not crash
|
|
953
|
+
assert result["VPCs"] == []
|
|
954
|
+
|
|
955
|
+
def test_collect_empty_results(self, vpc_collector, mock_session):
|
|
956
|
+
"""Test collect method with no VPC resources."""
|
|
957
|
+
mock_ec2_client = MagicMock()
|
|
958
|
+
mock_session.client.return_value = mock_ec2_client
|
|
959
|
+
|
|
960
|
+
# Mock all list methods to return empty lists
|
|
961
|
+
with patch.object(vpc_collector, "_list_vpcs") as mock_vpcs, patch.object(
|
|
962
|
+
vpc_collector, "_list_subnets"
|
|
963
|
+
) as mock_subnets, patch.object(vpc_collector, "_list_security_groups") as mock_sgs, patch.object(
|
|
964
|
+
vpc_collector, "_list_network_acls"
|
|
965
|
+
) as mock_acls, patch.object(
|
|
966
|
+
vpc_collector, "_list_route_tables"
|
|
967
|
+
) as mock_route_tables, patch.object(
|
|
968
|
+
vpc_collector, "_list_internet_gateways"
|
|
969
|
+
) as mock_igws, patch.object(
|
|
970
|
+
vpc_collector, "_list_nat_gateways"
|
|
971
|
+
) as mock_nats, patch.object(
|
|
972
|
+
vpc_collector, "_list_vpc_endpoints"
|
|
973
|
+
) as mock_endpoints, patch.object(
|
|
974
|
+
vpc_collector, "_list_vpc_peering_connections"
|
|
975
|
+
) as mock_peering:
|
|
976
|
+
mock_vpcs.return_value = []
|
|
977
|
+
mock_subnets.return_value = []
|
|
978
|
+
mock_sgs.return_value = []
|
|
979
|
+
mock_acls.return_value = []
|
|
980
|
+
mock_route_tables.return_value = []
|
|
981
|
+
mock_igws.return_value = []
|
|
982
|
+
mock_nats.return_value = []
|
|
983
|
+
mock_endpoints.return_value = []
|
|
984
|
+
mock_peering.return_value = []
|
|
985
|
+
|
|
986
|
+
result = vpc_collector.collect()
|
|
987
|
+
|
|
988
|
+
assert result["VPCs"] == []
|
|
989
|
+
assert result["Subnets"] == []
|
|
990
|
+
assert result["SecurityGroups"] == []
|
|
991
|
+
assert result["NetworkACLs"] == []
|
|
992
|
+
assert result["RouteTables"] == []
|
|
993
|
+
assert result["InternetGateways"] == []
|
|
994
|
+
assert result["NATGateways"] == []
|
|
995
|
+
assert result["VPCEndpoints"] == []
|
|
996
|
+
assert result["VPCPeeringConnections"] == []
|