diff --git a/prowler/changelog.d/aws-checks-roles-unlisted.fixed.md b/prowler/changelog.d/aws-checks-roles-unlisted.fixed.md new file mode 100644 index 0000000000..dc24ad1b49 --- /dev/null +++ b/prowler/changelog.d/aws-checks-roles-unlisted.fixed.md @@ -0,0 +1 @@ +`rolesanywhere_profile_restricts_session_permissions`, `iam_role_service_trust_restricts_source_to_account` and `codebuild_project_uses_allowed_github_organizations` crashing with `TypeError` when `iam:ListRoles` is denied diff --git a/prowler/providers/aws/services/codebuild/codebuild_project_uses_allowed_github_organizations/codebuild_project_uses_allowed_github_organizations.py b/prowler/providers/aws/services/codebuild/codebuild_project_uses_allowed_github_organizations/codebuild_project_uses_allowed_github_organizations.py index 750a5a6fdd..11bb9a8b00 100644 --- a/prowler/providers/aws/services/codebuild/codebuild_project_uses_allowed_github_organizations/codebuild_project_uses_allowed_github_organizations.py +++ b/prowler/providers/aws/services/codebuild/codebuild_project_uses_allowed_github_organizations/codebuild_project_uses_allowed_github_organizations.py @@ -23,7 +23,7 @@ class codebuild_project_uses_allowed_github_organizations(Check): project_role = next( ( role - for role in iam_client.roles + for role in iam_client.roles or [] if role.arn == project.service_role_arn ), None, diff --git a/prowler/providers/aws/services/iam/iam_role_service_trust_restricts_source_to_account/iam_role_service_trust_restricts_source_to_account.py b/prowler/providers/aws/services/iam/iam_role_service_trust_restricts_source_to_account/iam_role_service_trust_restricts_source_to_account.py index 795f39a458..07aa4ac073 100644 --- a/prowler/providers/aws/services/iam/iam_role_service_trust_restricts_source_to_account/iam_role_service_trust_restricts_source_to_account.py +++ b/prowler/providers/aws/services/iam/iam_role_service_trust_restricts_source_to_account/iam_role_service_trust_restricts_source_to_account.py @@ -377,7 +377,7 @@ class iam_role_service_trust_restricts_source_to_account(Check): status. The sibling token-wildcard check carries the same note, for the same reason. """ findings = [] - for role in iam_client.roles: + for role in iam_client.roles or []: # Service-linked roles are excluded: their trust relationship is managed by # the service and cannot be edited, so a finding would not be actionable. if "aws-service-role" in role.arn: diff --git a/prowler/providers/aws/services/rolesanywhere/rolesanywhere_profile_restricts_session_permissions/rolesanywhere_profile_restricts_session_permissions.py b/prowler/providers/aws/services/rolesanywhere/rolesanywhere_profile_restricts_session_permissions/rolesanywhere_profile_restricts_session_permissions.py index 5138b74ff3..2960518a67 100644 --- a/prowler/providers/aws/services/rolesanywhere/rolesanywhere_profile_restricts_session_permissions/rolesanywhere_profile_restricts_session_permissions.py +++ b/prowler/providers/aws/services/rolesanywhere/rolesanywhere_profile_restricts_session_permissions/rolesanywhere_profile_restricts_session_permissions.py @@ -205,7 +205,9 @@ class rolesanywhere_profile_restricts_session_permissions(Check): not administrative, and disabled profiles. """ findings = [] - roles_by_arn = {role.arn: role for role in iam_client.roles} + # iam:ListRoles denied leaves roles as None: every referenced role is + # then unknown and the profile falls through to MANUAL. + roles_by_arn = {role.arn: role for role in (iam_client.roles or [])} for profile in rolesanywhere_client.profiles.values(): report = Check_Report_AWS(metadata=self.metadata(), resource=profile) role_statuses = { diff --git a/tests/providers/aws/services/codebuild/codebuild_project_uses_allowed_github_organizations/codebuild_project_uses_allowed_github_organizations_test.py b/tests/providers/aws/services/codebuild/codebuild_project_uses_allowed_github_organizations/codebuild_project_uses_allowed_github_organizations_test.py index bdabea03fa..0ac90d50d2 100644 --- a/tests/providers/aws/services/codebuild/codebuild_project_uses_allowed_github_organizations/codebuild_project_uses_allowed_github_organizations_test.py +++ b/tests/providers/aws/services/codebuild/codebuild_project_uses_allowed_github_organizations/codebuild_project_uses_allowed_github_organizations_test.py @@ -1,4 +1,4 @@ -from unittest.mock import patch +from unittest.mock import MagicMock, patch from boto3 import client from moto import mock_aws @@ -182,6 +182,59 @@ class Test_codebuild_project_uses_allowed_github_organizations: ) assert result[0].region == AWS_REGION_EU_WEST_1 + @mock_aws + def test_project_github_with_unlisted_roles(self): + # iam:ListRoles denied leaves iam_client.roles as None. + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + codebuild_client = client("codebuild", region_name=AWS_REGION_EU_WEST_1) + codebuild_client.create_project( + name="test-project-github-unlisted-roles", + source={ + "type": "GITHUB", + "location": "https://github.com/allowed-org/repo", + }, + artifacts={"type": "NO_ARTIFACTS"}, + environment={ + "type": "LINUX_CONTAINER", + "image": "aws/codebuild/standard:4.0", + "computeType": "BUILD_GENERAL1_SMALL", + "environmentVariables": [], + }, + serviceRole=f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:role/codebuild-test-role", + ) + + from prowler.providers.aws.services.codebuild.codebuild_service import Codebuild + + iam_client = MagicMock() + iam_client.roles = None + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), + patch( + "prowler.providers.aws.services.codebuild.codebuild_project_uses_allowed_github_organizations.codebuild_project_uses_allowed_github_organizations.codebuild_client", + new=Codebuild(aws_provider), + ), + patch( + "prowler.providers.aws.services.codebuild.codebuild_project_uses_allowed_github_organizations.codebuild_project_uses_allowed_github_organizations.iam_client", + new=iam_client, + ), + patch( + "prowler.providers.aws.services.codebuild.codebuild_project_uses_allowed_github_organizations.codebuild_project_uses_allowed_github_organizations.codebuild_client.audit_config", + {"codebuild_github_allowed_organizations": ["allowed-org"]}, + ), + ): + from prowler.providers.aws.services.codebuild.codebuild_project_uses_allowed_github_organizations.codebuild_project_uses_allowed_github_organizations import ( + codebuild_project_uses_allowed_github_organizations, + ) + + assert ( + len(codebuild_project_uses_allowed_github_organizations().execute()) + == 0 + ) + @mock_aws def test_project_github_no_codebuild_trusted_principal(self): aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) diff --git a/tests/providers/aws/services/iam/iam_role_service_trust_restricts_source_to_account/iam_role_service_trust_restricts_source_to_account_test.py b/tests/providers/aws/services/iam/iam_role_service_trust_restricts_source_to_account/iam_role_service_trust_restricts_source_to_account_test.py index e01bdb36ba..66e96c571d 100644 --- a/tests/providers/aws/services/iam/iam_role_service_trust_restricts_source_to_account/iam_role_service_trust_restricts_source_to_account_test.py +++ b/tests/providers/aws/services/iam/iam_role_service_trust_restricts_source_to_account/iam_role_service_trust_restricts_source_to_account_test.py @@ -93,6 +93,10 @@ class Test_iam_role_service_trust_restricts_source_to_account: """An account with no roles produces no reports at all.""" assert len(_run([])) == 0 + def test_unlisted_roles_produce_no_reports(self): + # iam:ListRoles denied leaves iam_client.roles as None. + assert len(_run(None)) == 0 + def test_service_linked_role_skipped(self): """A service-linked role is excluded even when its trust policy would FAIL. diff --git a/tests/providers/aws/services/rolesanywhere/rolesanywhere_profile_restricts_session_permissions/rolesanywhere_profile_restricts_session_permissions_test.py b/tests/providers/aws/services/rolesanywhere/rolesanywhere_profile_restricts_session_permissions/rolesanywhere_profile_restricts_session_permissions_test.py index 09cdb3ae8d..230523239d 100644 --- a/tests/providers/aws/services/rolesanywhere/rolesanywhere_profile_restricts_session_permissions/rolesanywhere_profile_restricts_session_permissions_test.py +++ b/tests/providers/aws/services/rolesanywhere/rolesanywhere_profile_restricts_session_permissions/rolesanywhere_profile_restricts_session_permissions_test.py @@ -472,6 +472,19 @@ class Test_rolesanywhere_profile_restricts_session_permissions: assert result[0].status == "MANUAL" assert "could not be evaluated" in result[0].status_extended + def test_unscoped_profile_with_unlisted_roles_is_manual(self): + # iam:ListRoles denied leaves iam_client.roles as None. + patches = _patched( + _build_client({PROFILE_ARN: _profile(role_arns=[ADMIN_ROLE_ARN])}) + ) + patches[-1].new.roles = None + with _enter(patches): + result = _run() + assert len(result) == 1 + assert result[0].status == "MANUAL" + assert ADMIN_ROLE_ARN in result[0].status_extended + assert "could not be evaluated" in result[0].status_extended + def test_unscoped_profile_without_roles_passes(self): with _enter(_patched(_build_client({PROFILE_ARN: _profile(role_arns=[])}))): result = _run()