audit-labs/audit-tools

A collection of scripts, queries, and other goodies you can use in an audit.

clone: git clone https://gitbay.org/audit-labs/audit-tools.git

v1.0.0: tui/tests/test_aws_collectors.py · raw

  1"""Unit tests for the new AWS security collectors, mocking boto3 clients."""
  2
  3from unittest.mock import MagicMock
  4
  5from applications.aws.collectors import iam as aws_iam
  6from applications.aws.collectors import monitoring
  7from applications.aws.collectors import security_groups as sg
  8
  9
 10class FakeSession:
 11    """Dispatch .client(service, region_name=...) to preconfigured mocks."""
 12
 13    def __init__(self, clients):
 14        self._clients = clients
 15
 16    def client(self, service, region_name=None):
 17        return self._clients[service]
 18
 19
 20def _cfg(clients):
 21    return {"session": FakeSession(clients), "region": "us-east-1"}
 22
 23
 24# --- account_security -------------------------------------------------------
 25
 26
 27def test_account_security():
 28    iam = MagicMock()
 29    iam.get_account_summary.return_value = {
 30        "SummaryMap": {
 31            "AccountMFAEnabled": 1,
 32            "AccountAccessKeysPresent": 0,
 33            "Users": 5,
 34            "Roles": 12,
 35        }
 36    }
 37    rows = aws_iam.account_security(_cfg({"iam": iam}))
 38    assert rows[0]["root_mfa_enabled"] is True
 39    assert rows[0]["root_access_keys_present"] is False
 40    assert rows[0]["users"] == 5
 41    assert rows[0]["roles"] == 12
 42
 43
 44# --- cloudtrail -------------------------------------------------------------
 45
 46
 47def test_cloudtrail():
 48    ct = MagicMock()
 49    ct.describe_trails.return_value = {
 50        "trailList": [
 51            {
 52                "Name": "org-trail",
 53                "TrailARN": "arn:aws:cloudtrail:...:trail/org-trail",
 54                "HomeRegion": "us-east-1",
 55                "IsMultiRegionTrail": True,
 56                "LogFileValidationEnabled": True,
 57                "S3BucketName": "logs",
 58            }
 59        ]
 60    }
 61    ct.get_trail_status.return_value = {"IsLogging": True}
 62    rows = monitoring.cloudtrail(_cfg({"cloudtrail": ct}))
 63    assert rows[0]["is_logging"] is True
 64    assert rows[0]["multi_region"] is True
 65    assert rows[0]["s3_bucket"] == "logs"
 66
 67
 68# --- config_recorders -------------------------------------------------------
 69
 70
 71def _ec2_one_region():
 72    ec2 = MagicMock()
 73    ec2.describe_regions.return_value = {"Regions": [{"RegionName": "us-east-1"}]}
 74    return ec2
 75
 76
 77def test_config_recorders_recording():
 78    config = MagicMock()
 79    config.describe_configuration_recorders.return_value = {
 80        "ConfigurationRecorders": [{"name": "default"}]
 81    }
 82    config.describe_configuration_recorder_status.return_value = {
 83        "ConfigurationRecordersStatus": [
 84            {"name": "default", "recording": True, "lastStatus": "SUCCESS"}
 85        ]
 86    }
 87    rows = monitoring.config_recorders(
 88        _cfg({"ec2": _ec2_one_region(), "config": config})
 89    )
 90    assert rows == [
 91        {
 92            "region": "us-east-1",
 93            "recorder": "default",
 94            "recording": True,
 95            "last_status": "SUCCESS",
 96        }
 97    ]
 98
 99
100def test_config_recorders_reports_gap():
101    config = MagicMock()
102    config.describe_configuration_recorders.return_value = {
103        "ConfigurationRecorders": []
104    }
105    config.describe_configuration_recorder_status.return_value = {
106        "ConfigurationRecordersStatus": []
107    }
108    rows = monitoring.config_recorders(
109        _cfg({"ec2": _ec2_one_region(), "config": config})
110    )
111    assert rows[0]["recorder"] == "(none)"
112    assert rows[0]["recording"] is False
113
114
115# --- security_groups --------------------------------------------------------
116
117
118def _ec2_with_groups(groups):
119    ec2 = _ec2_one_region()
120    paginator = MagicMock()
121    paginator.paginate.return_value = [{"SecurityGroups": groups}]
122    ec2.get_paginator.return_value = paginator
123    return ec2
124
125
126def test_security_groups_flags_open_ingress():
127    groups = [
128        {
129            "GroupId": "sg-1",
130            "GroupName": "web",
131            "IpPermissions": [
132                {
133                    "IpProtocol": "tcp",
134                    "FromPort": 22,
135                    "ToPort": 22,
136                    "IpRanges": [{"CidrIp": "0.0.0.0/0"}],
137                    "Ipv6Ranges": [],
138                }
139            ],
140        }
141    ]
142    rows = sg.security_groups(_cfg({"ec2": _ec2_with_groups(groups)}))
143    assert len(rows) == 1
144    assert rows[0]["group_id"] == "sg-1"
145    assert rows[0]["from_port"] == 22
146    assert rows[0]["open_to"] == "0.0.0.0/0"
147
148
149def test_security_groups_ignores_scoped_ingress():
150    groups = [
151        {
152            "GroupId": "sg-2",
153            "GroupName": "internal",
154            "IpPermissions": [
155                {
156                    "IpProtocol": "tcp",
157                    "FromPort": 5432,
158                    "ToPort": 5432,
159                    "IpRanges": [{"CidrIp": "10.0.0.0/8"}],
160                    "Ipv6Ranges": [],
161                }
162            ],
163        }
164    ]
165    rows = sg.security_groups(_cfg({"ec2": _ec2_with_groups(groups)}))
166    assert rows == []