diff --git a/poetry.lock b/poetry.lock index 1d324d5149..32d7da3d99 100644 --- a/poetry.lock +++ b/poetry.lock @@ -4901,4 +4901,4 @@ test = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-it [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "57c55e469f3de00e5271bbe65871aea4ee7c6f9186fc6df6479f9cb3b2cb4564" +content-hash = "f3711e90f7f6fa4d32fac8ddc071bd01c7aa86e31d66f58f7bd1af8a2c0ad2d5" diff --git a/prowler/providers/aws/services/iam/iam_avoid_root_usage/iam_avoid_root_usage.py b/prowler/providers/aws/services/iam/iam_avoid_root_usage/iam_avoid_root_usage.py index 59f1ecbec5..00f9bdcf12 100644 --- a/prowler/providers/aws/services/iam/iam_avoid_root_usage/iam_avoid_root_usage.py +++ b/prowler/providers/aws/services/iam/iam_avoid_root_usage/iam_avoid_root_usage.py @@ -1,5 +1,8 @@ import datetime +import pytz +from dateutil import parser + from prowler.lib.check.models import Check, Check_Report_AWS from prowler.providers.aws.services.iam.iam_client import iam_client @@ -24,27 +27,18 @@ class iam_avoid_root_usage(Check): ): if user["password_last_used"] != "no_information": days_since_accessed = ( - datetime.datetime.now() - - datetime.datetime.strptime( - user["password_last_used"], - "%Y-%m-%dT%H:%M:%S+00:00", - ) + datetime.datetime.now(pytz.utc) + - parser.parse(user["password_last_used"]) ).days elif user["access_key_1_last_used_date"] != "N/A": days_since_accessed = ( - datetime.datetime.now() - - datetime.datetime.strptime( - user["access_key_1_last_used_date"], - "%Y-%m-%dT%H:%M:%S+00:00", - ) + datetime.datetime.now(pytz.utc) + - parser.parse(user["access_key_1_last_used_date"]) ).days elif user["access_key_2_last_used_date"] != "N/A": days_since_accessed = ( - datetime.datetime.now() - - datetime.datetime.strptime( - user["access_key_2_last_used_date"], - "%Y-%m-%dT%H:%M:%S+00:00", - ) + datetime.datetime.now(pytz.utc) + - parser.parse(user["access_key_2_last_used_date"]) ).days if maximum_access_days >= days_since_accessed: report.status = "FAIL" diff --git a/prowler/providers/aws/services/iam/iam_rotate_access_key_90_days/iam_rotate_access_key_90_days.py b/prowler/providers/aws/services/iam/iam_rotate_access_key_90_days/iam_rotate_access_key_90_days.py index b23cd4798f..cc7b3bfbe7 100644 --- a/prowler/providers/aws/services/iam/iam_rotate_access_key_90_days/iam_rotate_access_key_90_days.py +++ b/prowler/providers/aws/services/iam/iam_rotate_access_key_90_days/iam_rotate_access_key_90_days.py @@ -1,5 +1,8 @@ import datetime +import pytz +from dateutil import parser + from prowler.lib.check.models import Check, Check_Report_AWS from prowler.providers.aws.services.iam.iam_client import iam_client @@ -32,13 +35,9 @@ class iam_rotate_access_key_90_days(Check): user["access_key_1_last_rotated"] != "N/A" and user["access_key_1_active"] == "true" ): - access_key_1_last_rotated = ( - datetime.datetime.now() - - datetime.datetime.strptime( - user["access_key_1_last_rotated"], - "%Y-%m-%dT%H:%M:%S+00:00", - ) - ) + access_key_1_last_rotated = datetime.datetime.now( + pytz.utc + ) - parser.parse(user["access_key_1_last_rotated"]) if access_key_1_last_rotated.days > maximum_expiration_days: old_access_keys = True report = Check_Report_AWS(self.metadata()) @@ -52,13 +51,9 @@ class iam_rotate_access_key_90_days(Check): user["access_key_2_last_rotated"] != "N/A" and user["access_key_2_active"] == "true" ): - access_key_2_last_rotated = ( - datetime.datetime.now() - - datetime.datetime.strptime( - user["access_key_2_last_rotated"], - "%Y-%m-%dT%H:%M:%S+00:00", - ) - ) + access_key_2_last_rotated = datetime.datetime.now( + pytz.utc + ) - parser.parse(user["access_key_2_last_rotated"]) if access_key_2_last_rotated.days > maximum_expiration_days: old_access_keys = True report = Check_Report_AWS(self.metadata()) diff --git a/prowler/providers/aws/services/iam/iam_user_accesskey_unused/iam_user_accesskey_unused.py b/prowler/providers/aws/services/iam/iam_user_accesskey_unused/iam_user_accesskey_unused.py index 5c8c948daa..5463585bc0 100644 --- a/prowler/providers/aws/services/iam/iam_user_accesskey_unused/iam_user_accesskey_unused.py +++ b/prowler/providers/aws/services/iam/iam_user_accesskey_unused/iam_user_accesskey_unused.py @@ -1,5 +1,8 @@ import datetime +import pytz +from dateutil import parser + from prowler.lib.check.models import Check, Check_Report_AWS from prowler.providers.aws.services.iam.iam_client import iam_client @@ -29,13 +32,9 @@ class iam_user_accesskey_unused(Check): old_access_keys = False if user["access_key_1_active"] == "true": if user["access_key_1_last_used_date"] != "N/A": - access_key_1_last_used_date = ( - datetime.datetime.now() - - datetime.datetime.strptime( - user["access_key_1_last_used_date"], - "%Y-%m-%dT%H:%M:%S+00:00", - ) - ) + access_key_1_last_used_date = datetime.datetime.now( + pytz.utc + ) - parser.parse(user["access_key_1_last_used_date"]) if access_key_1_last_used_date.days > maximum_expiration_days: old_access_keys = True report = Check_Report_AWS(self.metadata()) @@ -48,13 +47,9 @@ class iam_user_accesskey_unused(Check): if user["access_key_2_active"] == "true": if user["access_key_2_last_used_date"] != "N/A": - access_key_2_last_used_date = ( - datetime.datetime.now() - - datetime.datetime.strptime( - user["access_key_2_last_used_date"], - "%Y-%m-%dT%H:%M:%S+00:00", - ) - ) + access_key_2_last_used_date = datetime.datetime.now( + pytz.utc + ) - parser.parse(user["access_key_2_last_used_date"]) if access_key_2_last_used_date.days > maximum_expiration_days: old_access_keys = True report = Check_Report_AWS(self.metadata()) diff --git a/pyproject.toml b/pyproject.toml index 889a545df0..cb0567bdb3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,6 +63,7 @@ pandas = "2.2.2" py-ocsf-models = "0.1.1" pydantic = "1.10.15" python = ">=3.9,<3.13" +python-dateutil = "^2.9.0.post0" pytz = "2024.1" schema = "0.7.7" shodan = "1.31.0" diff --git a/tests/providers/aws/services/iam/iam_avoid_root_usage/iam_avoid_root_usage_test.py b/tests/providers/aws/services/iam/iam_avoid_root_usage/iam_avoid_root_usage_test.py index 3485b158c4..6f12ae8708 100644 --- a/tests/providers/aws/services/iam/iam_avoid_root_usage/iam_avoid_root_usage_test.py +++ b/tests/providers/aws/services/iam/iam_avoid_root_usage/iam_avoid_root_usage_test.py @@ -12,7 +12,7 @@ class Test_iam_avoid_root_usage: @mock_aws def test_root_not_used(self): raw_credential_report = r"""user,arn,user_creation_time,password_enabled,password_last_used,password_last_changed,password_next_rotation,mfa_active,access_key_1_active,access_key_1_last_rotated,access_key_1_last_used_date,access_key_1_last_used_region,access_key_1_last_used_service,access_key_2_active,access_key_2_last_rotated,access_key_2_last_used_date,access_key_2_last_used_region,access_key_2_last_used_service,cert_1_active,cert_1_last_rotated,cert_2_active,cert_2_last_rotated -,arn:aws:iam::123456789012:,2022-04-17T14:59:38+00:00,true,no_information,not_supported,not_supported,false,true,N/A,N/A,N/A,N/A,false,N/A,N/A,N/A,N/A,false,N/A,false,N/A""" +,arn:aws:iam::123456789012:,2022-04-17T14:59:38Z,true,no_information,not_supported,not_supported,false,true,N/A,N/A,N/A,N/A,false,N/A,N/A,N/A,N/A,false,N/A,false,N/A""" credential_lines = raw_credential_report.split("\n") csv_reader = DictReader(credential_lines, delimiter=",") credential_list = list(csv_reader) @@ -48,11 +48,9 @@ class Test_iam_avoid_root_usage: @mock_aws def test_root_password_recently_used(self): - password_last_used = (datetime.datetime.now()).strftime( - "%Y-%m-%dT%H:%M:%S+00:00" - ) + password_last_used = (datetime.datetime.now()).strftime("%Y-%m-%dT%H:%M:%SZ") raw_credential_report = rf"""user,arn,user_creation_time,password_enabled,password_last_used,password_last_changed,password_next_rotation,mfa_active,access_key_1_active,access_key_1_last_rotated,access_key_1_last_used_date,access_key_1_last_used_region,access_key_1_last_used_service,access_key_2_active,access_key_2_last_rotated,access_key_2_last_used_date,access_key_2_last_used_region,access_key_2_last_used_service,cert_1_active,cert_1_last_rotated,cert_2_active,cert_2_last_rotated -,arn:aws:iam::123456789012:,2022-04-17T14:59:38+00:00,true,{password_last_used},not_supported,not_supported,false,true,N/A,N/A,N/A,N/A,false,N/A,N/A,N/A,N/A,false,N/A,false,N/A""" +,arn:aws:iam::123456789012:,2022-04-17T14:59:38Z,true,{password_last_used},not_supported,not_supported,false,true,N/A,N/A,N/A,N/A,false,N/A,N/A,N/A,N/A,false,N/A,false,N/A""" credential_lines = raw_credential_report.split("\n") csv_reader = DictReader(credential_lines, delimiter=",") credential_list = list(csv_reader) @@ -89,10 +87,10 @@ class Test_iam_avoid_root_usage: @mock_aws def test_root_access_key_1_recently_used(self): access_key_1_last_used = (datetime.datetime.now()).strftime( - "%Y-%m-%dT%H:%M:%S+00:00" + "%Y-%m-%dT%H:%M:%SZ" ) raw_credential_report = rf"""user,arn,user_creation_time,password_enabled,password_last_used,password_last_changed,password_next_rotation,mfa_active,access_key_1_active,access_key_1_last_rotated,access_key_1_last_used_date,access_key_1_last_used_region,access_key_1_last_used_service,access_key_2_active,access_key_2_last_rotated,access_key_2_last_used_date,access_key_2_last_used_region,access_key_2_last_used_service,cert_1_active,cert_1_last_rotated,cert_2_active,cert_2_last_rotated -,arn:aws:iam::123456789012:,2022-04-17T14:59:38+00:00,true,no_information,not_supported,not_supported,false,true,N/A,{access_key_1_last_used},N/A,N/A,false,N/A,N/A,N/A,N/A,false,N/A,false,N/A""" +,arn:aws:iam::123456789012:,2022-04-17T14:59:38Z,true,no_information,not_supported,not_supported,false,true,N/A,{access_key_1_last_used},N/A,N/A,false,N/A,N/A,N/A,N/A,false,N/A,false,N/A""" credential_lines = raw_credential_report.split("\n") csv_reader = DictReader(credential_lines, delimiter=",") credential_list = list(csv_reader) @@ -129,10 +127,10 @@ class Test_iam_avoid_root_usage: @mock_aws def test_root_access_key_2_recently_used(self): access_key_2_last_used = (datetime.datetime.now()).strftime( - "%Y-%m-%dT%H:%M:%S+00:00" + "%Y-%m-%dT%H:%M:%SZ" ) raw_credential_report = rf"""user,arn,user_creation_time,password_enabled,password_last_used,password_last_changed,password_next_rotation,mfa_active,access_key_1_active,access_key_1_last_rotated,access_key_1_last_used_date,access_key_1_last_used_region,access_key_1_last_used_service,access_key_2_active,access_key_2_last_rotated,access_key_2_last_used_date,access_key_2_last_used_region,access_key_2_last_used_service,cert_1_active,cert_1_last_rotated,cert_2_active,cert_2_last_rotated -,arn:aws:iam::123456789012:,2022-04-17T14:59:38+00:00,true,no_information,not_supported,not_supported,false,true,N/A,N/A,N/A,N/A,false,N/A,{access_key_2_last_used},N/A,N/A,false,N/A,false,N/A""" +,arn:aws:iam::123456789012:,2022-04-17T14:59:38Z,true,no_information,not_supported,not_supported,false,true,N/A,N/A,N/A,N/A,false,N/A,{access_key_2_last_used},N/A,N/A,false,N/A,false,N/A""" credential_lines = raw_credential_report.split("\n") csv_reader = DictReader(credential_lines, delimiter=",") credential_list = list(csv_reader) @@ -170,9 +168,9 @@ class Test_iam_avoid_root_usage: def test_root_password_used(self): password_last_used = ( datetime.datetime.now() - datetime.timedelta(days=100) - ).strftime("%Y-%m-%dT%H:%M:%S+00:00") + ).strftime("%Y-%m-%dT%H:%M:%SZ") raw_credential_report = rf"""user,arn,user_creation_time,password_enabled,password_last_used,password_last_changed,password_next_rotation,mfa_active,access_key_1_active,access_key_1_last_rotated,access_key_1_last_used_date,access_key_1_last_used_region,access_key_1_last_used_service,access_key_2_active,access_key_2_last_rotated,access_key_2_last_used_date,access_key_2_last_used_region,access_key_2_last_used_service,cert_1_active,cert_1_last_rotated,cert_2_active,cert_2_last_rotated -,arn:aws:iam::123456789012:,2022-04-17T14:59:38+00:00,true,{password_last_used},not_supported,not_supported,false,true,N/A,N/A,N/A,N/A,false,N/A,N/A,N/A,N/A,false,N/A,false,N/A""" +,arn:aws:iam::123456789012:,2022-04-17T14:59:38Z,true,{password_last_used},not_supported,not_supported,false,true,N/A,N/A,N/A,N/A,false,N/A,N/A,N/A,N/A,false,N/A,false,N/A""" credential_lines = raw_credential_report.split("\n") csv_reader = DictReader(credential_lines, delimiter=",") credential_list = list(csv_reader) @@ -210,9 +208,9 @@ class Test_iam_avoid_root_usage: def test_root_access_key_1_used(self): access_key_1_last_used = ( datetime.datetime.now() - datetime.timedelta(days=100) - ).strftime("%Y-%m-%dT%H:%M:%S+00:00") + ).strftime("%Y-%m-%dT%H:%M:%SZ") raw_credential_report = rf"""user,arn,user_creation_time,password_enabled,password_last_used,password_last_changed,password_next_rotation,mfa_active,access_key_1_active,access_key_1_last_rotated,access_key_1_last_used_date,access_key_1_last_used_region,access_key_1_last_used_service,access_key_2_active,access_key_2_last_rotated,access_key_2_last_used_date,access_key_2_last_used_region,access_key_2_last_used_service,cert_1_active,cert_1_last_rotated,cert_2_active,cert_2_last_rotated -,arn:aws:iam::123456789012:,2022-04-17T14:59:38+00:00,true,no_information,not_supported,not_supported,false,true,N/A,{access_key_1_last_used},N/A,N/A,false,N/A,N/A,N/A,N/A,false,N/A,false,N/A""" +,arn:aws:iam::123456789012:,2022-04-17T14:59:38Z,true,no_information,not_supported,not_supported,false,true,N/A,{access_key_1_last_used},N/A,N/A,false,N/A,N/A,N/A,N/A,false,N/A,false,N/A""" credential_lines = raw_credential_report.split("\n") csv_reader = DictReader(credential_lines, delimiter=",") credential_list = list(csv_reader) @@ -250,9 +248,9 @@ class Test_iam_avoid_root_usage: def test_root_access_key_2_used(self): access_key_2_last_used = ( datetime.datetime.now() - datetime.timedelta(days=100) - ).strftime("%Y-%m-%dT%H:%M:%S+00:00") + ).strftime("%Y-%m-%dT%H:%M:%SZ") raw_credential_report = rf"""user,arn,user_creation_time,password_enabled,password_last_used,password_last_changed,password_next_rotation,mfa_active,access_key_1_active,access_key_1_last_rotated,access_key_1_last_used_date,access_key_1_last_used_region,access_key_1_last_used_service,access_key_2_active,access_key_2_last_rotated,access_key_2_last_used_date,access_key_2_last_used_region,access_key_2_last_used_service,cert_1_active,cert_1_last_rotated,cert_2_active,cert_2_last_rotated -,arn:aws:iam::123456789012:,2022-04-17T14:59:38+00:00,true,no_information,not_supported,not_supported,false,true,N/A,N/A,N/A,N/A,false,N/A,{access_key_2_last_used},N/A,N/A,false,N/A,false,N/A""" +,arn:aws:iam::123456789012:,2022-04-17T14:59:38Z,true,no_information,not_supported,not_supported,false,true,N/A,N/A,N/A,N/A,false,N/A,{access_key_2_last_used},N/A,N/A,false,N/A,false,N/A""" credential_lines = raw_credential_report.split("\n") csv_reader = DictReader(credential_lines, delimiter=",") credential_list = list(csv_reader) diff --git a/tests/providers/aws/services/iam/iam_rotate_access_key_90_days/iam_rotate_access_key_90_days_test.py b/tests/providers/aws/services/iam/iam_rotate_access_key_90_days/iam_rotate_access_key_90_days_test.py index 2f9e79841e..7b20c6ec2e 100644 --- a/tests/providers/aws/services/iam/iam_rotate_access_key_90_days/iam_rotate_access_key_90_days_test.py +++ b/tests/providers/aws/services/iam/iam_rotate_access_key_90_days/iam_rotate_access_key_90_days_test.py @@ -1,6 +1,7 @@ import datetime from unittest import mock +import pytz from boto3 import client from moto import mock_aws @@ -46,7 +47,7 @@ class Test_iam_rotate_access_key_90_days_test: @mock_aws def test_user_access_key_1_not_rotated(self): credentials_last_rotated = ( - datetime.datetime.now() - datetime.timedelta(days=100) + datetime.datetime.now(pytz.utc) - datetime.timedelta(days=100) ).strftime("%Y-%m-%dT%H:%M:%S+00:00") iam_client = client("iam") user = "test-user" @@ -87,7 +88,7 @@ class Test_iam_rotate_access_key_90_days_test: @mock_aws def test_user_access_key_2_not_rotated(self): credentials_last_rotated = ( - datetime.datetime.now() - datetime.timedelta(days=100) + datetime.datetime.now(pytz.utc) - datetime.timedelta(days=100) ).strftime("%Y-%m-%dT%H:%M:%S+00:00") iam_client = client("iam") user = "test-user" @@ -128,7 +129,7 @@ class Test_iam_rotate_access_key_90_days_test: @mock_aws def test_user_both_access_keys_not_rotated(self): credentials_last_rotated = ( - datetime.datetime.now() - datetime.timedelta(days=100) + datetime.datetime.now(pytz.utc) - datetime.timedelta(days=100) ).strftime("%Y-%m-%dT%H:%M:%S+00:00") iam_client = client("iam") user = "test-user" @@ -182,7 +183,7 @@ class Test_iam_rotate_access_key_90_days_test: @mock_aws def test_user_both_access_keys_rotated(self): credentials_last_rotated = ( - datetime.datetime.now() - datetime.timedelta(days=10) + datetime.datetime.now(pytz.utc) - datetime.timedelta(days=10) ).strftime("%Y-%m-%dT%H:%M:%S+00:00") iam_client = client("iam") user = "test-user" diff --git a/tests/providers/aws/services/iam/iam_user_accesskey_unused/iam_user_accesskey_unused_test.py b/tests/providers/aws/services/iam/iam_user_accesskey_unused/iam_user_accesskey_unused_test.py index dd0b9b968e..0c96a4e2ec 100644 --- a/tests/providers/aws/services/iam/iam_user_accesskey_unused/iam_user_accesskey_unused_test.py +++ b/tests/providers/aws/services/iam/iam_user_accesskey_unused/iam_user_accesskey_unused_test.py @@ -1,6 +1,7 @@ import datetime from unittest import mock +import pytz from boto3 import client from moto import mock_aws @@ -57,8 +58,8 @@ class Test_iam_user_accesskey_unused_test: @mock_aws def test_user_access_key_1_not_used(self): credentials_last_rotated = ( - datetime.datetime.now() - datetime.timedelta(days=100) - ).strftime("%Y-%m-%dT%H:%M:%S+00:00") + datetime.datetime.now(pytz.utc) - datetime.timedelta(days=100) + ).strftime("%Y-%m-%dT%H:%M:%SZ") iam_client = client("iam") user = "test-user" arn = iam_client.create_user(UserName=user)["User"]["Arn"] @@ -101,8 +102,8 @@ class Test_iam_user_accesskey_unused_test: @mock_aws def test_user_access_key_2_not_used(self): credentials_last_rotated = ( - datetime.datetime.now() - datetime.timedelta(days=100) - ).strftime("%Y-%m-%dT%H:%M:%S+00:00") + datetime.datetime.now(pytz.utc) - datetime.timedelta(days=100) + ).strftime("%Y-%m-%dT%H:%M:%SZ") iam_client = client("iam") user = "test-user" arn = iam_client.create_user(UserName=user)["User"]["Arn"] @@ -145,8 +146,8 @@ class Test_iam_user_accesskey_unused_test: @mock_aws def test_user_both_access_keys_not_used(self): credentials_last_rotated = ( - datetime.datetime.now() - datetime.timedelta(days=100) - ).strftime("%Y-%m-%dT%H:%M:%S+00:00") + datetime.datetime.now(pytz.utc) - datetime.timedelta(days=100) + ).strftime("%Y-%m-%dT%H:%M:%SZ") iam_client = client("iam") user = "test-user" arn = iam_client.create_user(UserName=user)["User"]["Arn"] @@ -203,8 +204,8 @@ class Test_iam_user_accesskey_unused_test: @mock_aws def test_user_both_access_keys_used(self): credentials_last_rotated = ( - datetime.datetime.now() - datetime.timedelta(days=10) - ).strftime("%Y-%m-%dT%H:%M:%S+00:00") + datetime.datetime.now(pytz.utc) - datetime.timedelta(days=10) + ).strftime("%Y-%m-%dT%H:%M:%SZ") iam_client = client("iam") user = "test-user" arn = iam_client.create_user(UserName=user)["User"]["Arn"]