fix(iam): detect wildcarded ARNs in sts:AssumeRole policy resources (#8185)

Co-authored-by: Kay Agahd <kagahd@users.noreply.github.com>
Co-authored-by: Sergio Garcia <hello@mistercloudsec.com>
This commit is contained in:
Prowler Bot
2025-07-04 10:48:28 +02:00
committed by GitHub
parent 39306e47f2
commit 2fa8d7c351
3 changed files with 198 additions and 19 deletions
+1
View File
@@ -9,6 +9,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
### Changed
### Fixed
- fix(iam): detect wildcarded ARNs in sts:AssumeRole policy resources [(#8164)](https://github.com/prowler-cloud/prowler/pull/8164)
---
@@ -5,6 +5,14 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
class iam_no_custom_policy_permissive_role_assumption(Check):
def execute(self) -> Check_Report_AWS:
findings = []
def resource_has_wildcard(resource):
if isinstance(resource, str):
return "*" in resource
if isinstance(resource, list):
return any("*" in r for r in resource)
return False
for policy in iam_client.policies:
# Check only custom policies
if policy.type == "Custom":
@@ -12,6 +20,7 @@ class iam_no_custom_policy_permissive_role_assumption(Check):
report.region = iam_client.region
report.status = "PASS"
report.status_extended = f"Custom Policy {policy.name} does not allow permissive STS Role assumption."
if policy.document:
if not isinstance(policy.document["Statement"], list):
policy_statements = [policy.document["Statement"]]
@@ -19,30 +28,23 @@ class iam_no_custom_policy_permissive_role_assumption(Check):
policy_statements = policy.document["Statement"]
for statement in policy_statements:
if (
statement["Effect"] == "Allow"
statement.get("Effect") == "Allow"
and "Action" in statement
and "Resource" in statement
and "*" in statement["Resource"]
and resource_has_wildcard(statement["Resource"])
):
if isinstance(statement["Action"], list):
for action in statement["Action"]:
if (
action == "sts:AssumeRole"
or action == "sts:*"
or action == "*"
):
report.status = "FAIL"
report.status_extended = f"Custom Policy {policy.name} allows permissive STS Role assumption."
break
else:
if (
statement["Action"] == "sts:AssumeRole"
or statement["Action"] == "sts:*"
or statement["Action"] == "*"
):
actions = (
statement["Action"]
if isinstance(statement["Action"], list)
else [statement["Action"]]
)
for action in actions:
if action in ["sts:AssumeRole", "sts:*", "*"]:
report.status = "FAIL"
report.status_extended = f"Custom Policy {policy.name} allows permissive STS Role assumption."
break
break
if report.status == "FAIL":
break
findings.append(report)
@@ -232,3 +232,179 @@ class Test_iam_no_custom_policy_permissive_role_assumption:
result[1].status_extended,
)
assert result[1].resource_id == policy_name_permissive
@mock_aws
def test_policy_resource_with_embedded_wildcard_in_arn(self):
iam_client = client("iam")
policy_name = "policy_with_wildcard_in_arn"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::*:role/eks-terraform-*",
}
],
}
arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
from prowler.providers.aws.services.iam.iam_service import IAM
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
):
with mock.patch(
"prowler.providers.aws.services.iam.iam_no_custom_policy_permissive_role_assumption.iam_no_custom_policy_permissive_role_assumption.iam_client",
new=IAM(aws_provider),
):
from prowler.providers.aws.services.iam.iam_no_custom_policy_permissive_role_assumption.iam_no_custom_policy_permissive_role_assumption import (
iam_no_custom_policy_permissive_role_assumption,
)
check = iam_no_custom_policy_permissive_role_assumption()
result = check.execute()
assert result[0].status == "FAIL"
assert result[0].resource_arn == arn
assert search(
"allows permissive STS Role assumption", result[0].status_extended
)
assert result[0].resource_id == policy_name
@mock_aws
def test_policy_resource_list_containing_wildcard(self):
iam_client = client("iam")
policy_name = "policy_with_resource_list"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": ["arn:aws:iam::123456789012:role/SomeRole", "*"],
}
],
}
arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
from prowler.providers.aws.services.iam.iam_service import IAM
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
):
with mock.patch(
"prowler.providers.aws.services.iam.iam_no_custom_policy_permissive_role_assumption.iam_no_custom_policy_permissive_role_assumption.iam_client",
new=IAM(aws_provider),
):
from prowler.providers.aws.services.iam.iam_no_custom_policy_permissive_role_assumption.iam_no_custom_policy_permissive_role_assumption import (
iam_no_custom_policy_permissive_role_assumption,
)
check = iam_no_custom_policy_permissive_role_assumption()
result = check.execute()
assert result[0].status == "FAIL"
assert result[0].resource_arn == arn
assert search(
"allows permissive STS Role assumption", result[0].status_extended
)
assert result[0].resource_id == policy_name
@mock_aws
def test_policy_resource_list_with_arn_wildcards_should_fail(self):
iam_client = client("iam")
policy_name = "policy_list_wildcard_arn"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": [
"arn:aws:iam::123456789012:role/eks-admin",
"arn:aws:iam::*:role/eks-*",
],
}
],
}
arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
from prowler.providers.aws.services.iam.iam_service import IAM
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
):
with mock.patch(
"prowler.providers.aws.services.iam.iam_no_custom_policy_permissive_role_assumption.iam_no_custom_policy_permissive_role_assumption.iam_client",
new=IAM(aws_provider),
):
from prowler.providers.aws.services.iam.iam_no_custom_policy_permissive_role_assumption.iam_no_custom_policy_permissive_role_assumption import (
iam_no_custom_policy_permissive_role_assumption,
)
check = iam_no_custom_policy_permissive_role_assumption()
result = check.execute()
assert result[0].status == "FAIL"
assert result[0].resource_arn == arn
assert search(
"allows permissive STS Role assumption", result[0].status_extended
)
@mock_aws
def test_policy_resource_list_with_only_wildcarded_arns_should_fail(self):
iam_client = client("iam")
policy_name = "policy_list_scoped_wildcards_only"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": [
"arn:aws:iam::*:role/team-*",
"arn:aws:iam::*:role/dev-*",
],
}
],
}
arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
from prowler.providers.aws.services.iam.iam_service import IAM
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
):
with mock.patch(
"prowler.providers.aws.services.iam.iam_no_custom_policy_permissive_role_assumption.iam_no_custom_policy_permissive_role_assumption.iam_client",
new=IAM(aws_provider),
):
from prowler.providers.aws.services.iam.iam_no_custom_policy_permissive_role_assumption.iam_no_custom_policy_permissive_role_assumption import (
iam_no_custom_policy_permissive_role_assumption,
)
check = iam_no_custom_policy_permissive_role_assumption()
result = check.execute()
assert result[0].status == "FAIL"
assert result[0].resource_arn == arn
assert search(
"allows permissive STS Role assumption", result[0].status_extended
)