feat(aws): add iam_policy_no_wildcard_marketplace_subscribe and iam_inline_policy_no_wildcard_marketplace_subscribe checks (#10525)

Co-authored-by: Hugo P.Brito <hugopbrit@gmail.com>
This commit is contained in:
Daniel Barranquero
2026-04-14 12:08:40 +02:00
committed by GitHub
co-authored by Hugo P.Brito
parent 65e9593b41
commit d1ab8b8ae5
28 changed files with 1283 additions and 0 deletions
+1
View File
@@ -8,6 +8,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
- `bedrock_full_access_policy_attached` check for AWS provider [(#10577)](https://github.com/prowler-cloud/prowler/pull/10577)
- `iam_role_access_not_stale_to_bedrock` and `iam_user_access_not_stale_to_bedrock` checks for AWS provider [(#10536)](https://github.com/prowler-cloud/prowler/pull/10536)
- `iam_policy_no_wildcard_marketplace_subscribe` and `iam_inline_policy_no_wildcard_marketplace_subscribe` checks for AWS provider [(#10525)](https://github.com/prowler-cloud/prowler/pull/10525)
---
@@ -344,6 +344,8 @@
}
],
"Checks": [
"iam_inline_policy_no_wildcard_marketplace_subscribe",
"iam_policy_no_wildcard_marketplace_subscribe",
"bedrock_full_access_policy_attached",
"ec2_instance_profile_attached",
"iam_aws_attached_policy_no_administrative_privileges",
+4
View File
@@ -5130,6 +5130,8 @@
"iam_support_role_created",
"iam_user_with_temporary_credentials",
"bedrock_api_key_no_administrative_privileges",
"iam_inline_policy_no_wildcard_marketplace_subscribe",
"iam_policy_no_wildcard_marketplace_subscribe",
"fms_policy_compliant",
"iam_aws_attached_policy_no_administrative_privileges",
"iam_customer_attached_policy_no_administrative_privileges",
@@ -5200,6 +5202,8 @@
"iam_support_role_created",
"iam_user_with_temporary_credentials",
"bedrock_api_key_no_administrative_privileges",
"iam_inline_policy_no_wildcard_marketplace_subscribe",
"iam_policy_no_wildcard_marketplace_subscribe",
"fms_policy_compliant",
"iam_aws_attached_policy_no_administrative_privileges",
"iam_customer_attached_policy_no_administrative_privileges",
+2
View File
@@ -183,6 +183,8 @@
}
],
"Checks": [
"iam_inline_policy_no_wildcard_marketplace_subscribe",
"iam_policy_no_wildcard_marketplace_subscribe",
"elbv2_ssl_listeners",
"iam_no_custom_policy_permissive_role_assumption",
"iam_aws_attached_policy_no_administrative_privileges",
@@ -3319,6 +3319,8 @@
}
],
"Checks": [
"iam_inline_policy_no_wildcard_marketplace_subscribe",
"iam_policy_no_wildcard_marketplace_subscribe",
"iam_aws_attached_policy_no_administrative_privileges",
"iam_customer_attached_policy_no_administrative_privileges",
"iam_inline_policy_no_administrative_privileges",
@@ -86,6 +86,8 @@
}
],
"Checks": [
"iam_inline_policy_no_wildcard_marketplace_subscribe",
"iam_policy_no_wildcard_marketplace_subscribe",
"iam_administrator_access_with_mfa",
"iam_aws_attached_policy_no_administrative_privileges",
"iam_customer_attached_policy_no_administrative_privileges",
@@ -308,6 +308,8 @@
}
],
"Checks": [
"iam_inline_policy_no_wildcard_marketplace_subscribe",
"iam_policy_no_wildcard_marketplace_subscribe",
"ec2_ebs_public_snapshot",
"ec2_instance_public_ip",
"ec2_instance_imdsv2_enabled",
+2
View File
@@ -418,6 +418,8 @@
}
],
"Checks": [
"iam_inline_policy_no_wildcard_marketplace_subscribe",
"iam_policy_no_wildcard_marketplace_subscribe",
"ec2_instance_profile_attached",
"iam_policy_attached_only_to_group_or_roles",
"iam_aws_attached_policy_no_administrative_privileges",
+2
View File
@@ -265,6 +265,8 @@
}
],
"Checks": [
"iam_inline_policy_no_wildcard_marketplace_subscribe",
"iam_policy_no_wildcard_marketplace_subscribe",
"iam_aws_attached_policy_no_administrative_privileges",
"iam_customer_attached_policy_no_administrative_privileges",
"iam_inline_policy_no_administrative_privileges"
@@ -308,6 +308,8 @@
}
],
"Checks": [
"iam_inline_policy_no_wildcard_marketplace_subscribe",
"iam_policy_no_wildcard_marketplace_subscribe",
"iam_aws_attached_policy_no_administrative_privileges",
"iam_customer_attached_policy_no_administrative_privileges",
"iam_inline_policy_no_administrative_privileges"
@@ -972,6 +974,8 @@
}
],
"Checks": [
"iam_inline_policy_no_wildcard_marketplace_subscribe",
"iam_policy_no_wildcard_marketplace_subscribe",
"bedrock_full_access_policy_attached",
"ec2_instance_profile_attached",
"iam_aws_attached_policy_no_administrative_privileges",
@@ -1389,6 +1389,8 @@
"Checks": [
"accessanalyzer_enabled",
"accessanalyzer_enabled_without_findings",
"iam_inline_policy_no_wildcard_marketplace_subscribe",
"iam_policy_no_wildcard_marketplace_subscribe",
"bedrock_full_access_policy_attached",
"iam_administrator_access_with_mfa",
"iam_avoid_root_usage",
@@ -1389,6 +1389,8 @@
"Checks": [
"accessanalyzer_enabled",
"accessanalyzer_enabled_without_findings",
"iam_inline_policy_no_wildcard_marketplace_subscribe",
"iam_policy_no_wildcard_marketplace_subscribe",
"bedrock_full_access_policy_attached",
"iam_administrator_access_with_mfa",
"iam_avoid_root_usage",
@@ -156,6 +156,8 @@
}
],
"Checks": [
"iam_inline_policy_no_wildcard_marketplace_subscribe",
"iam_policy_no_wildcard_marketplace_subscribe",
"iam_aws_attached_policy_no_administrative_privileges",
"iam_customer_attached_policy_no_administrative_privileges",
"iam_inline_policy_no_administrative_privileges",
@@ -228,6 +228,8 @@
}
],
"Checks": [
"iam_inline_policy_no_wildcard_marketplace_subscribe",
"iam_policy_no_wildcard_marketplace_subscribe",
"ec2_ebs_public_snapshot",
"ec2_instance_public_ip",
"ec2_instance_imdsv2_enabled",
@@ -1186,6 +1186,8 @@
}
],
"Checks": [
"iam_inline_policy_no_wildcard_marketplace_subscribe",
"iam_policy_no_wildcard_marketplace_subscribe",
"ec2_ebs_public_snapshot",
"ec2_instance_public_ip",
"ec2_instance_imdsv2_enabled",
@@ -630,6 +630,8 @@
}
],
"Checks": [
"iam_inline_policy_no_wildcard_marketplace_subscribe",
"iam_policy_no_wildcard_marketplace_subscribe",
"iam_aws_attached_policy_no_administrative_privileges",
"iam_customer_attached_policy_no_administrative_privileges",
"iam_inline_policy_no_administrative_privileges",
@@ -776,6 +776,8 @@
}
],
"Checks": [
"iam_inline_policy_no_wildcard_marketplace_subscribe",
"iam_policy_no_wildcard_marketplace_subscribe",
"iam_administrator_access_with_mfa",
"iam_group_administrator_access_policy",
"iam_user_administrator_access_policy",
+2
View File
@@ -205,6 +205,8 @@
}
],
"Checks": [
"iam_inline_policy_no_wildcard_marketplace_subscribe",
"iam_policy_no_wildcard_marketplace_subscribe",
"bedrock_full_access_policy_attached",
"iam_aws_attached_policy_no_administrative_privileges",
"iam_customer_attached_policy_no_administrative_privileges",
@@ -0,0 +1,42 @@
{
"Provider": "aws",
"CheckID": "iam_inline_policy_no_wildcard_marketplace_subscribe",
"CheckTitle": "Inline IAM policy does not allow 'aws-marketplace:Subscribe' on all resources",
"CheckType": [
"Software and Configuration Checks/AWS Security Best Practices"
],
"ServiceName": "iam",
"SubServiceName": "",
"ResourceIdTemplate": "",
"Severity": "medium",
"ResourceType": "AwsIamPolicy",
"ResourceGroup": "IAM",
"Description": "**IAM inline policies** are analyzed to identify statements that grant `aws-marketplace:Subscribe` on all resources (`*`). This action controls the ability to subscribe to AWS Marketplace products, including **Amazon Bedrock foundation models**, and should be scoped to specific product ARNs to enforce least privilege.",
"Risk": "Granting `aws-marketplace:Subscribe` on all resources via inline policies allows subscribing to any Marketplace product, including expensive Bedrock foundation models, leading to uncontrolled costs, shadow AI usage, and compliance violations.",
"RelatedUrl": "",
"AdditionalURLs": [
"https://docs.aws.amazon.com/bedrock/latest/userguide/security-iam.html",
"https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege"
],
"Remediation": {
"Code": {
"CLI": "",
"NativeIaC": "```yaml\nResources:\n ExampleRole:\n Type: AWS::IAM::Role\n Properties:\n Policies:\n - PolicyName: scoped-marketplace-subscribe\n PolicyDocument:\n Version: '2012-10-17'\n Statement:\n - Effect: Allow\n Action:\n - aws-marketplace:Subscribe # FIX: scope to specific product ARNs\n Resource:\n - arn:aws:aws-marketplace::123456789012:product/approved-product-id\n```",
"Other": "1. In the AWS Console, open IAM and go to Users, Roles, or Groups where the inline policy is attached\n2. Select the entity, go to the Permissions tab, and open the inline policy\n3. Click Edit policy and switch to the JSON editor\n4. Replace \"Resource\": \"*\" with specific, approved AWS Marketplace product ARNs\n5. Save changes and re-run the check to confirm it passes",
"Terraform": "```hcl\nresource \"aws_iam_role_policy\" \"scoped_marketplace_subscribe\" {\n name = \"scoped-marketplace-subscribe\"\n role = aws_iam_role.example.id\n\n policy = jsonencode({\n Version = \"2012-10-17\"\n Statement = [{\n Effect = \"Allow\"\n Action = [\"aws-marketplace:Subscribe\"]\n Resource = [\"arn:aws:aws-marketplace::123456789012:product/approved-product-id\"] # FIX: scope to specific products\n }]\n })\n}\n```"
},
"Recommendation": {
"Text": "Replace `Resource: \"*\"` with specific, approved AWS Marketplace product ARNs. Prefer managed policies over inline and apply the principle of least privilege to `aws-marketplace:Subscribe` permissions to prevent unauthorized subscriptions to costly Bedrock models and other Marketplace products.",
"Url": "https://hub.prowler.com/check/iam_inline_policy_no_wildcard_marketplace_subscribe"
}
},
"Categories": [
"gen-ai",
"identity-access"
],
"DependsOn": [],
"RelatedTo": [
"iam_policy_no_wildcard_marketplace_subscribe"
],
"Notes": "This check only evaluates IAM inline policies. See iam_policy_no_wildcard_marketplace_subscribe for the customer-managed policy variant."
}
@@ -0,0 +1,33 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.iam.iam_client import iam_client
from prowler.providers.aws.services.iam.lib.policy import (
policy_allows_marketplace_subscribe_on_all_resources,
)
class iam_inline_policy_no_wildcard_marketplace_subscribe(Check):
def execute(self) -> list[Check_Report_AWS]:
findings = []
for policy in iam_client.policies.values():
if policy.type == "Inline":
report = Check_Report_AWS(metadata=self.metadata(), resource=policy)
report.region = iam_client.region
report.resource_id = f"{policy.entity}/{policy.name}"
report.status = "PASS"
resource_type_str = report.resource_arn.split(":")[-1].split("/")[0]
resource_attached = report.resource_arn.split("/")[-1]
report.status_extended = f"Inline policy {policy.name}{' attached to ' + resource_type_str + ' ' + resource_attached if policy.attached else ''} does not allow 'aws-marketplace:Subscribe' on all resources."
if (
policy.document
and policy_allows_marketplace_subscribe_on_all_resources(
policy.document
)
):
report.status = "FAIL"
report.status_extended = f"Inline policy {policy.name}{' attached to ' + resource_type_str + ' ' + resource_attached if policy.attached else ''} allows 'aws-marketplace:Subscribe' on all resources."
findings.append(report)
return findings
@@ -0,0 +1,42 @@
{
"Provider": "aws",
"CheckID": "iam_policy_no_wildcard_marketplace_subscribe",
"CheckTitle": "Custom IAM policy does not allow 'aws-marketplace:Subscribe' on all resources",
"CheckType": [
"Software and Configuration Checks/AWS Security Best Practices"
],
"ServiceName": "iam",
"SubServiceName": "",
"ResourceIdTemplate": "",
"Severity": "medium",
"ResourceType": "AwsIamPolicy",
"ResourceGroup": "IAM",
"Description": "**Customer-managed IAM policies** are examined for statements that grant `aws-marketplace:Subscribe` on all resources (`*`). This action controls the ability to subscribe to AWS Marketplace products, including **Amazon Bedrock foundation models**, and should be scoped to specific product ARNs to enforce least privilege.",
"Risk": "Granting `aws-marketplace:Subscribe` on all resources allows subscribing to any Marketplace product, including expensive Bedrock foundation models, leading to uncontrolled costs, shadow AI usage, and compliance violations from unapproved deployments.",
"RelatedUrl": "",
"AdditionalURLs": [
"https://docs.aws.amazon.com/bedrock/latest/userguide/security-iam.html",
"https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege"
],
"Remediation": {
"Code": {
"CLI": "aws iam create-policy-version --policy-arn <POLICY_ARN> --policy-document '{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":[\"aws-marketplace:Subscribe\"],\"Resource\":\"arn:aws:aws-marketplace::<ACCOUNT_ID>:product/<PRODUCT_ID>\"}]}' --set-as-default",
"NativeIaC": "```yaml\nResources:\n ScopedMarketplacePolicy:\n Type: AWS::IAM::ManagedPolicy\n Properties:\n PolicyDocument:\n Version: '2012-10-17'\n Statement:\n - Effect: Allow\n Action:\n - aws-marketplace:Subscribe # FIX: scope to specific product ARNs\n Resource:\n - arn:aws:aws-marketplace::123456789012:product/approved-product-id\n```",
"Other": "1. In the AWS Console, open IAM > Policies\n2. Find the custom policy that allows aws-marketplace:Subscribe on Resource: *\n3. Click Edit and switch to the JSON editor\n4. Replace \"Resource\": \"*\" with specific, approved AWS Marketplace product ARNs\n5. Save changes and re-run the check to confirm it passes",
"Terraform": "```hcl\nresource \"aws_iam_policy\" \"scoped_marketplace_subscribe\" {\n name = \"scoped-marketplace-subscribe\"\n\n policy = jsonencode({\n Version = \"2012-10-17\"\n Statement = [{\n Effect = \"Allow\"\n Action = [\"aws-marketplace:Subscribe\"]\n Resource = [\"arn:aws:aws-marketplace::123456789012:product/approved-product-id\"] # FIX: scope to specific products\n }]\n })\n}\n```"
},
"Recommendation": {
"Text": "Replace `Resource: \"*\"` with specific, approved AWS Marketplace product ARNs. Apply the principle of least privilege to `aws-marketplace:Subscribe` permissions to prevent unauthorized subscriptions to costly Bedrock models and other Marketplace products.",
"Url": "https://hub.prowler.com/check/iam_policy_no_wildcard_marketplace_subscribe"
}
},
"Categories": [
"gen-ai",
"identity-access"
],
"DependsOn": [],
"RelatedTo": [
"iam_inline_policy_no_wildcard_marketplace_subscribe"
],
"Notes": "This check only evaluates customer-managed IAM policies. AWS managed policies are maintained by AWS and cannot be modified. See iam_inline_policy_no_wildcard_marketplace_subscribe for the inline policy variant."
}
@@ -0,0 +1,28 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.iam.iam_client import iam_client
from prowler.providers.aws.services.iam.lib.policy import (
policy_allows_marketplace_subscribe_on_all_resources,
)
class iam_policy_no_wildcard_marketplace_subscribe(Check):
def execute(self) -> list[Check_Report_AWS]:
findings = []
for policy in iam_client.policies.values():
if policy.type == "Custom":
report = Check_Report_AWS(metadata=self.metadata(), resource=policy)
report.region = iam_client.region
report.status = "PASS"
report.status_extended = f"Custom Policy {policy.name} does not allow 'aws-marketplace:Subscribe' on all resources."
if (
policy.document
and policy_allows_marketplace_subscribe_on_all_resources(
policy.document
)
):
report.status = "FAIL"
report.status_extended = f"Custom Policy {policy.name} allows 'aws-marketplace:Subscribe' on all resources."
findings.append(report)
return findings
@@ -997,6 +997,93 @@ def is_codebuild_using_allowed_github_org(
return False, None
def policy_allows_marketplace_subscribe_on_all_resources(
policy_document: dict,
) -> bool:
"""Check if a policy document can allow aws-marketplace:Subscribe on Resource:*.
Inspects statements with Resource ``*`` for Allow effects that grant
``aws-marketplace:Subscribe`` via ``Action`` or ``NotAction`` (wildcard
patterns expanded through ``expand_actions``). Unconditional Deny
statements on Resource ``*`` (via either ``Action`` or ``NotAction``)
covering the same action take precedence. Conditional Deny statements
are not treated as global cancellation because the condition scope is
request-dependent and is not evaluated here. Conditional Allow
statements are still treated as potentially allowing access on
``Resource:*``, since the wildcard scope remains risky even when
gated by a condition.
Args:
policy_document: The IAM policy document to analyse.
Returns:
True if the policy can allow aws-marketplace:Subscribe on all
resources, False otherwise.
"""
if not policy_document or "Statement" not in policy_document:
return False
target_actions = set(
expand_actions(
"aws-marketplace:Subscribe",
InvalidActionHandling.REMOVE,
)
)
if not target_actions:
target_actions = {"aws-marketplace:Subscribe"}
statements = policy_document.get("Statement", [])
if not isinstance(statements, list):
statements = [statements]
allowed_on_all = set()
denied_on_all = set()
all_aws_actions = None
for statement in statements:
effect = statement.get("Effect", "")
if not isinstance(effect, str):
continue
effect_lower = effect.strip().lower()
if effect_lower not in ("allow", "deny"):
continue
resources = statement.get("Resource", [])
if isinstance(resources, str):
resources = [resources]
if "*" not in resources:
continue
if effect_lower == "deny" and "Condition" in statement:
continue
statement_actions = set()
action_patterns = _get_patterns_from_standard_value(statement.get("Action"))
for pattern in action_patterns:
statement_actions.update(
expand_actions(pattern, InvalidActionHandling.REMOVE)
)
not_action_patterns = _get_patterns_from_standard_value(
statement.get("NotAction")
)
if not_action_patterns:
if all_aws_actions is None:
all_aws_actions = set(expand_actions("*", InvalidActionHandling.REMOVE))
exclusions = set()
for pattern in not_action_patterns:
exclusions.update(expand_actions(pattern, InvalidActionHandling.REMOVE))
statement_actions.update(all_aws_actions.difference(exclusions))
if effect_lower == "allow":
allowed_on_all.update(statement_actions)
else:
denied_on_all.update(statement_actions)
effective = allowed_on_all.difference(denied_on_all)
return bool(target_actions & effective)
def has_codebuild_trusted_principal(trust_policy: dict) -> bool:
"""
Returns True if the trust policy allows codebuild.amazonaws.com as a trusted principal, otherwise False.
@@ -0,0 +1,461 @@
from json import dumps
from unittest import mock
from boto3 import client
from moto import mock_aws
from prowler.providers.aws.services.iam.iam_service import IAM
from tests.providers.aws.utils import (
ADMINISTRATOR_ROLE_ASSUME_ROLE_POLICY,
AWS_REGION_EU_WEST_1,
set_mocked_aws_provider,
)
CHECK_MODULE_PATH = "prowler.providers.aws.services.iam.iam_inline_policy_no_wildcard_marketplace_subscribe.iam_inline_policy_no_wildcard_marketplace_subscribe"
class Test_iam_inline_policy_no_wildcard_marketplace_subscribe:
@mock_aws
def test_inline_policy_allows_marketplace_subscribe_on_all_resources(self):
"""FAIL: Inline policy allows aws-marketplace:Subscribe on Resource:*."""
aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
iam_client = client("iam", region_name=AWS_REGION_EU_WEST_1)
role_name = "test_role"
role_arn = iam_client.create_role(
RoleName=role_name,
AssumeRolePolicyDocument=dumps(ADMINISTRATOR_ROLE_ASSUME_ROLE_POLICY),
)["Role"]["Arn"]
policy_name = "marketplace_subscribe_wildcard"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "aws-marketplace:Subscribe",
"Resource": "*",
},
],
}
iam_client.put_role_policy(
RoleName=role_name,
PolicyName=policy_name,
PolicyDocument=dumps(policy_document),
)
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
),
mock.patch(
f"{CHECK_MODULE_PATH}.iam_client",
new=IAM(aws_provider),
),
):
from prowler.providers.aws.services.iam.iam_inline_policy_no_wildcard_marketplace_subscribe.iam_inline_policy_no_wildcard_marketplace_subscribe import (
iam_inline_policy_no_wildcard_marketplace_subscribe,
)
check = iam_inline_policy_no_wildcard_marketplace_subscribe()
result = check.execute()
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Inline policy {policy_name} attached to role {role_name} allows 'aws-marketplace:Subscribe' on all resources."
)
assert result[0].resource_id == f"{role_name}/{policy_name}"
assert result[0].resource_arn == role_arn
assert result[0].region == "eu-west-1"
@mock_aws
def test_inline_policy_allows_marketplace_wildcard_action(self):
"""FAIL: Inline policy allows aws-marketplace:* on Resource:*."""
aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
iam_client = client("iam", region_name=AWS_REGION_EU_WEST_1)
role_name = "test_role"
role_arn = iam_client.create_role(
RoleName=role_name,
AssumeRolePolicyDocument=dumps(ADMINISTRATOR_ROLE_ASSUME_ROLE_POLICY),
)["Role"]["Arn"]
policy_name = "marketplace_all_actions"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "aws-marketplace:*",
"Resource": "*",
},
],
}
iam_client.put_role_policy(
RoleName=role_name,
PolicyName=policy_name,
PolicyDocument=dumps(policy_document),
)
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
),
mock.patch(
f"{CHECK_MODULE_PATH}.iam_client",
new=IAM(aws_provider),
),
):
from prowler.providers.aws.services.iam.iam_inline_policy_no_wildcard_marketplace_subscribe.iam_inline_policy_no_wildcard_marketplace_subscribe import (
iam_inline_policy_no_wildcard_marketplace_subscribe,
)
check = iam_inline_policy_no_wildcard_marketplace_subscribe()
result = check.execute()
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Inline policy {policy_name} attached to role {role_name} allows 'aws-marketplace:Subscribe' on all resources."
)
assert result[0].resource_id == f"{role_name}/{policy_name}"
assert result[0].resource_arn == role_arn
assert result[0].region == "eu-west-1"
@mock_aws
def test_inline_policy_scoped_resource(self):
"""PASS: Inline policy allows aws-marketplace:Subscribe on a specific resource."""
aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
iam_client = client("iam", region_name=AWS_REGION_EU_WEST_1)
role_name = "test_role"
role_arn = iam_client.create_role(
RoleName=role_name,
AssumeRolePolicyDocument=dumps(ADMINISTRATOR_ROLE_ASSUME_ROLE_POLICY),
)["Role"]["Arn"]
policy_name = "marketplace_subscribe_scoped"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "aws-marketplace:Subscribe",
"Resource": "arn:aws:aws-marketplace::123456789012:product/example-product-id",
},
],
}
iam_client.put_role_policy(
RoleName=role_name,
PolicyName=policy_name,
PolicyDocument=dumps(policy_document),
)
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
),
mock.patch(
f"{CHECK_MODULE_PATH}.iam_client",
new=IAM(aws_provider),
),
):
from prowler.providers.aws.services.iam.iam_inline_policy_no_wildcard_marketplace_subscribe.iam_inline_policy_no_wildcard_marketplace_subscribe import (
iam_inline_policy_no_wildcard_marketplace_subscribe,
)
check = iam_inline_policy_no_wildcard_marketplace_subscribe()
result = check.execute()
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Inline policy {policy_name} attached to role {role_name} does not allow 'aws-marketplace:Subscribe' on all resources."
)
assert result[0].resource_id == f"{role_name}/{policy_name}"
assert result[0].resource_arn == role_arn
assert result[0].region == "eu-west-1"
@mock_aws
def test_inline_policy_unrelated_action(self):
"""PASS: Inline policy allows an unrelated action on Resource:*."""
aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
iam_client = client("iam", region_name=AWS_REGION_EU_WEST_1)
role_name = "test_role"
role_arn = iam_client.create_role(
RoleName=role_name,
AssumeRolePolicyDocument=dumps(ADMINISTRATOR_ROLE_ASSUME_ROLE_POLICY),
)["Role"]["Arn"]
policy_name = "ec2_full_access"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ec2:*",
"Resource": "*",
},
],
}
iam_client.put_role_policy(
RoleName=role_name,
PolicyName=policy_name,
PolicyDocument=dumps(policy_document),
)
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
),
mock.patch(
f"{CHECK_MODULE_PATH}.iam_client",
new=IAM(aws_provider),
),
):
from prowler.providers.aws.services.iam.iam_inline_policy_no_wildcard_marketplace_subscribe.iam_inline_policy_no_wildcard_marketplace_subscribe import (
iam_inline_policy_no_wildcard_marketplace_subscribe,
)
check = iam_inline_policy_no_wildcard_marketplace_subscribe()
result = check.execute()
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Inline policy {policy_name} attached to role {role_name} does not allow 'aws-marketplace:Subscribe' on all resources."
)
assert result[0].resource_id == f"{role_name}/{policy_name}"
assert result[0].resource_arn == role_arn
assert result[0].region == "eu-west-1"
@mock_aws
def test_inline_policy_deny_overrides_allow(self):
"""PASS: Allow + Deny on * for same action — Deny wins."""
aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
iam_client = client("iam", region_name=AWS_REGION_EU_WEST_1)
role_name = "test_role"
role_arn = iam_client.create_role(
RoleName=role_name,
AssumeRolePolicyDocument=dumps(ADMINISTRATOR_ROLE_ASSUME_ROLE_POLICY),
)["Role"]["Arn"]
policy_name = "marketplace_subscribe_denied"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "aws-marketplace:Subscribe",
"Resource": "*",
},
{
"Effect": "Deny",
"Action": "aws-marketplace:Subscribe",
"Resource": "*",
},
],
}
iam_client.put_role_policy(
RoleName=role_name,
PolicyName=policy_name,
PolicyDocument=dumps(policy_document),
)
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
),
mock.patch(
f"{CHECK_MODULE_PATH}.iam_client",
new=IAM(aws_provider),
),
):
from prowler.providers.aws.services.iam.iam_inline_policy_no_wildcard_marketplace_subscribe.iam_inline_policy_no_wildcard_marketplace_subscribe import (
iam_inline_policy_no_wildcard_marketplace_subscribe,
)
check = iam_inline_policy_no_wildcard_marketplace_subscribe()
result = check.execute()
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Inline policy {policy_name} attached to role {role_name} does not allow 'aws-marketplace:Subscribe' on all resources."
)
assert result[0].resource_id == f"{role_name}/{policy_name}"
assert result[0].resource_arn == role_arn
assert result[0].region == "eu-west-1"
@mock_aws
def test_inline_policy_deny_specific_resource_does_not_override(self):
"""FAIL: Deny on specific resource does not negate Allow on *."""
aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
iam_client = client("iam", region_name=AWS_REGION_EU_WEST_1)
role_name = "test_role"
role_arn = iam_client.create_role(
RoleName=role_name,
AssumeRolePolicyDocument=dumps(ADMINISTRATOR_ROLE_ASSUME_ROLE_POLICY),
)["Role"]["Arn"]
policy_name = "deny_specific_only"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "aws-marketplace:Subscribe",
"Resource": "*",
},
{
"Effect": "Deny",
"Action": "aws-marketplace:Subscribe",
"Resource": "arn:aws:aws-marketplace::123456789012:product/blocked",
},
],
}
iam_client.put_role_policy(
RoleName=role_name,
PolicyName=policy_name,
PolicyDocument=dumps(policy_document),
)
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
),
mock.patch(
f"{CHECK_MODULE_PATH}.iam_client",
new=IAM(aws_provider),
),
):
from prowler.providers.aws.services.iam.iam_inline_policy_no_wildcard_marketplace_subscribe.iam_inline_policy_no_wildcard_marketplace_subscribe import (
iam_inline_policy_no_wildcard_marketplace_subscribe,
)
check = iam_inline_policy_no_wildcard_marketplace_subscribe()
result = check.execute()
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Inline policy {policy_name} attached to role {role_name} allows 'aws-marketplace:Subscribe' on all resources."
)
assert result[0].resource_id == f"{role_name}/{policy_name}"
assert result[0].resource_arn == role_arn
assert result[0].region == "eu-west-1"
@mock_aws
def test_inline_policy_not_action_allows_marketplace_subscribe(self):
"""FAIL: Allow + NotAction excludes only unrelated actions so Subscribe is granted."""
aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
iam_client = client("iam", region_name=AWS_REGION_EU_WEST_1)
role_name = "test_role"
role_arn = iam_client.create_role(
RoleName=role_name,
AssumeRolePolicyDocument=dumps(ADMINISTRATOR_ROLE_ASSUME_ROLE_POLICY),
)["Role"]["Arn"]
policy_name = "marketplace_not_action"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"NotAction": "ec2:Describe*",
"Resource": "*",
},
],
}
iam_client.put_role_policy(
RoleName=role_name,
PolicyName=policy_name,
PolicyDocument=dumps(policy_document),
)
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
),
mock.patch(
f"{CHECK_MODULE_PATH}.iam_client",
new=IAM(aws_provider),
),
):
from prowler.providers.aws.services.iam.iam_inline_policy_no_wildcard_marketplace_subscribe.iam_inline_policy_no_wildcard_marketplace_subscribe import (
iam_inline_policy_no_wildcard_marketplace_subscribe,
)
check = iam_inline_policy_no_wildcard_marketplace_subscribe()
result = check.execute()
assert result[0].status == "FAIL"
assert result[0].resource_arn == role_arn
@mock_aws
def test_inline_policy_not_action_excludes_marketplace_subscribe(self):
"""PASS: Allow + NotAction that excludes aws-marketplace:Subscribe does not grant it."""
aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
iam_client = client("iam", region_name=AWS_REGION_EU_WEST_1)
role_name = "test_role"
role_arn = iam_client.create_role(
RoleName=role_name,
AssumeRolePolicyDocument=dumps(ADMINISTRATOR_ROLE_ASSUME_ROLE_POLICY),
)["Role"]["Arn"]
policy_name = "marketplace_not_action_excluded"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"NotAction": "aws-marketplace:Subscribe",
"Resource": "*",
},
],
}
iam_client.put_role_policy(
RoleName=role_name,
PolicyName=policy_name,
PolicyDocument=dumps(policy_document),
)
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
),
mock.patch(
f"{CHECK_MODULE_PATH}.iam_client",
new=IAM(aws_provider),
),
):
from prowler.providers.aws.services.iam.iam_inline_policy_no_wildcard_marketplace_subscribe.iam_inline_policy_no_wildcard_marketplace_subscribe import (
iam_inline_policy_no_wildcard_marketplace_subscribe,
)
check = iam_inline_policy_no_wildcard_marketplace_subscribe()
result = check.execute()
assert result[0].status == "PASS"
assert result[0].resource_arn == role_arn
@mock_aws
def test_no_inline_policies(self):
"""No findings when there are no inline policies."""
aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
),
mock.patch(
f"{CHECK_MODULE_PATH}.iam_client",
new=IAM(aws_provider),
),
):
from prowler.providers.aws.services.iam.iam_inline_policy_no_wildcard_marketplace_subscribe.iam_inline_policy_no_wildcard_marketplace_subscribe import (
iam_inline_policy_no_wildcard_marketplace_subscribe,
)
check = iam_inline_policy_no_wildcard_marketplace_subscribe()
result = check.execute()
assert len(result) == 0
@@ -0,0 +1,509 @@
from json import dumps
from unittest import mock
from boto3 import client
from moto import mock_aws
from prowler.providers.aws.services.iam.iam_service import IAM
from tests.providers.aws.utils import AWS_REGION_US_EAST_1, set_mocked_aws_provider
CHECK_MODULE_PATH = "prowler.providers.aws.services.iam.iam_policy_no_wildcard_marketplace_subscribe.iam_policy_no_wildcard_marketplace_subscribe"
class Test_iam_policy_no_wildcard_marketplace_subscribe:
@mock_aws
def test_policy_allows_marketplace_subscribe_on_all_resources(self):
"""FAIL: Policy explicitly allows aws-marketplace:Subscribe on Resource:*."""
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
iam_client = client("iam")
policy_name = "marketplace_subscribe_wildcard"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "aws-marketplace:Subscribe",
"Resource": "*",
},
],
}
arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
):
with mock.patch(
f"{CHECK_MODULE_PATH}.iam_client",
new=IAM(aws_provider),
):
from prowler.providers.aws.services.iam.iam_policy_no_wildcard_marketplace_subscribe.iam_policy_no_wildcard_marketplace_subscribe import (
iam_policy_no_wildcard_marketplace_subscribe,
)
check = iam_policy_no_wildcard_marketplace_subscribe()
result = check.execute()
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Custom Policy {policy_name} allows 'aws-marketplace:Subscribe' on all resources."
)
assert result[0].resource_id == policy_name
assert result[0].resource_arn == arn
assert result[0].region == "us-east-1"
@mock_aws
def test_policy_allows_marketplace_wildcard_action_on_all_resources(self):
"""FAIL: Policy allows aws-marketplace:* on Resource:*."""
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
iam_client = client("iam")
policy_name = "marketplace_all_actions"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "aws-marketplace:*",
"Resource": "*",
},
],
}
arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
):
with mock.patch(
f"{CHECK_MODULE_PATH}.iam_client",
new=IAM(aws_provider),
):
from prowler.providers.aws.services.iam.iam_policy_no_wildcard_marketplace_subscribe.iam_policy_no_wildcard_marketplace_subscribe import (
iam_policy_no_wildcard_marketplace_subscribe,
)
check = iam_policy_no_wildcard_marketplace_subscribe()
result = check.execute()
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Custom Policy {policy_name} allows 'aws-marketplace:Subscribe' on all resources."
)
assert result[0].resource_id == policy_name
assert result[0].resource_arn == arn
assert result[0].region == "us-east-1"
@mock_aws
def test_policy_allows_full_wildcard_on_all_resources(self):
"""FAIL: Policy allows * (all actions) on Resource:*."""
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
iam_client = client("iam")
policy_name = "full_admin_access"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "*",
"Resource": "*",
},
],
}
arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
):
with mock.patch(
f"{CHECK_MODULE_PATH}.iam_client",
new=IAM(aws_provider),
):
from prowler.providers.aws.services.iam.iam_policy_no_wildcard_marketplace_subscribe.iam_policy_no_wildcard_marketplace_subscribe import (
iam_policy_no_wildcard_marketplace_subscribe,
)
check = iam_policy_no_wildcard_marketplace_subscribe()
result = check.execute()
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Custom Policy {policy_name} allows 'aws-marketplace:Subscribe' on all resources."
)
assert result[0].resource_id == policy_name
assert result[0].resource_arn == arn
assert result[0].region == "us-east-1"
@mock_aws
def test_policy_allows_marketplace_subscribe_on_specific_resource(self):
"""PASS: Policy allows aws-marketplace:Subscribe on a specific resource ARN."""
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
iam_client = client("iam")
policy_name = "marketplace_subscribe_scoped"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "aws-marketplace:Subscribe",
"Resource": "arn:aws:aws-marketplace::123456789012:product/example-product-id",
},
],
}
arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
):
with mock.patch(
f"{CHECK_MODULE_PATH}.iam_client",
new=IAM(aws_provider),
):
from prowler.providers.aws.services.iam.iam_policy_no_wildcard_marketplace_subscribe.iam_policy_no_wildcard_marketplace_subscribe import (
iam_policy_no_wildcard_marketplace_subscribe,
)
check = iam_policy_no_wildcard_marketplace_subscribe()
result = check.execute()
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Custom Policy {policy_name} does not allow 'aws-marketplace:Subscribe' on all resources."
)
assert result[0].resource_id == policy_name
assert result[0].resource_arn == arn
assert result[0].region == "us-east-1"
@mock_aws
def test_policy_unrelated_action_on_all_resources(self):
"""PASS: Policy allows an unrelated action on Resource:*."""
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
iam_client = client("iam")
policy_name = "ec2_full_access"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ec2:*",
"Resource": "*",
},
],
}
arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
):
with mock.patch(
f"{CHECK_MODULE_PATH}.iam_client",
new=IAM(aws_provider),
):
from prowler.providers.aws.services.iam.iam_policy_no_wildcard_marketplace_subscribe.iam_policy_no_wildcard_marketplace_subscribe import (
iam_policy_no_wildcard_marketplace_subscribe,
)
check = iam_policy_no_wildcard_marketplace_subscribe()
result = check.execute()
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Custom Policy {policy_name} does not allow 'aws-marketplace:Subscribe' on all resources."
)
assert result[0].resource_id == policy_name
assert result[0].resource_arn == arn
assert result[0].region == "us-east-1"
@mock_aws
def test_policy_marketplace_subscribe_denied_on_all_resources(self):
"""PASS: Allow + Deny on * for same action — Deny wins."""
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
iam_client = client("iam")
policy_name = "marketplace_subscribe_denied"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "aws-marketplace:Subscribe",
"Resource": "*",
},
{
"Effect": "Deny",
"Action": "aws-marketplace:Subscribe",
"Resource": "*",
},
],
}
arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
):
with mock.patch(
f"{CHECK_MODULE_PATH}.iam_client",
new=IAM(aws_provider),
):
from prowler.providers.aws.services.iam.iam_policy_no_wildcard_marketplace_subscribe.iam_policy_no_wildcard_marketplace_subscribe import (
iam_policy_no_wildcard_marketplace_subscribe,
)
check = iam_policy_no_wildcard_marketplace_subscribe()
result = check.execute()
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Custom Policy {policy_name} does not allow 'aws-marketplace:Subscribe' on all resources."
)
assert result[0].resource_id == policy_name
assert result[0].resource_arn == arn
assert result[0].region == "us-east-1"
@mock_aws
def test_policy_deny_specific_resource_does_not_override_allow_all(self):
"""FAIL: Deny on a specific resource does not negate Allow on Resource:*."""
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
iam_client = client("iam")
policy_name = "deny_specific_resource"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "aws-marketplace:Subscribe",
"Resource": "*",
},
{
"Effect": "Deny",
"Action": "aws-marketplace:Subscribe",
"Resource": "arn:aws:aws-marketplace::123456789012:product/blocked-product",
},
],
}
arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
):
with mock.patch(
f"{CHECK_MODULE_PATH}.iam_client",
new=IAM(aws_provider),
):
from prowler.providers.aws.services.iam.iam_policy_no_wildcard_marketplace_subscribe.iam_policy_no_wildcard_marketplace_subscribe import (
iam_policy_no_wildcard_marketplace_subscribe,
)
check = iam_policy_no_wildcard_marketplace_subscribe()
result = check.execute()
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Custom Policy {policy_name} allows 'aws-marketplace:Subscribe' on all resources."
)
assert result[0].resource_id == policy_name
assert result[0].resource_arn == arn
assert result[0].region == "us-east-1"
@mock_aws
def test_policy_deny_unrelated_action_does_not_override_allow(self):
"""FAIL: Deny for a different action does not override the Allow for Subscribe."""
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
iam_client = client("iam")
policy_name = "deny_unrelated_action"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "aws-marketplace:Subscribe",
"Resource": "*",
},
{
"Effect": "Deny",
"Action": "ec2:TerminateInstances",
"Resource": "*",
},
],
}
arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
):
with mock.patch(
f"{CHECK_MODULE_PATH}.iam_client",
new=IAM(aws_provider),
):
from prowler.providers.aws.services.iam.iam_policy_no_wildcard_marketplace_subscribe.iam_policy_no_wildcard_marketplace_subscribe import (
iam_policy_no_wildcard_marketplace_subscribe,
)
check = iam_policy_no_wildcard_marketplace_subscribe()
result = check.execute()
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Custom Policy {policy_name} allows 'aws-marketplace:Subscribe' on all resources."
)
assert result[0].resource_id == policy_name
assert result[0].resource_arn == arn
assert result[0].region == "us-east-1"
@mock_aws
def test_policy_case_insensitive_action_matching(self):
"""FAIL: Mixed-case action still matches aws-marketplace:Subscribe."""
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
iam_client = client("iam")
policy_name = "mixed_case_subscribe"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "AWS-Marketplace:SUBSCRIBE",
"Resource": "*",
},
],
}
arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
):
with mock.patch(
f"{CHECK_MODULE_PATH}.iam_client",
new=IAM(aws_provider),
):
from prowler.providers.aws.services.iam.iam_policy_no_wildcard_marketplace_subscribe.iam_policy_no_wildcard_marketplace_subscribe import (
iam_policy_no_wildcard_marketplace_subscribe,
)
check = iam_policy_no_wildcard_marketplace_subscribe()
result = check.execute()
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Custom Policy {policy_name} allows 'aws-marketplace:Subscribe' on all resources."
)
assert result[0].resource_id == policy_name
assert result[0].resource_arn == arn
assert result[0].region == "us-east-1"
@mock_aws
def test_policy_not_action_allows_marketplace_subscribe(self):
"""FAIL: Allow + NotAction excludes only unrelated actions so Subscribe is granted."""
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
iam_client = client("iam")
policy_name = "marketplace_not_action"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"NotAction": "ec2:Describe*",
"Resource": "*",
},
],
}
arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
):
with mock.patch(
f"{CHECK_MODULE_PATH}.iam_client",
new=IAM(aws_provider),
):
from prowler.providers.aws.services.iam.iam_policy_no_wildcard_marketplace_subscribe.iam_policy_no_wildcard_marketplace_subscribe import (
iam_policy_no_wildcard_marketplace_subscribe,
)
check = iam_policy_no_wildcard_marketplace_subscribe()
result = check.execute()
assert result[0].status == "FAIL"
assert result[0].resource_arn == arn
@mock_aws
def test_policy_not_action_excludes_marketplace_subscribe(self):
"""PASS: Allow + NotAction that excludes aws-marketplace:Subscribe does not grant it."""
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
iam_client = client("iam")
policy_name = "marketplace_not_action_excluded"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"NotAction": "aws-marketplace:Subscribe",
"Resource": "*",
},
],
}
arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
):
with mock.patch(
f"{CHECK_MODULE_PATH}.iam_client",
new=IAM(aws_provider),
):
from prowler.providers.aws.services.iam.iam_policy_no_wildcard_marketplace_subscribe.iam_policy_no_wildcard_marketplace_subscribe import (
iam_policy_no_wildcard_marketplace_subscribe,
)
check = iam_policy_no_wildcard_marketplace_subscribe()
result = check.execute()
assert result[0].status == "PASS"
assert result[0].resource_arn == arn
@mock_aws
def test_no_custom_policies(self):
"""No findings when there are no custom IAM policies."""
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(
f"{CHECK_MODULE_PATH}.iam_client",
new=IAM(aws_provider),
):
from prowler.providers.aws.services.iam.iam_policy_no_wildcard_marketplace_subscribe.iam_policy_no_wildcard_marketplace_subscribe import (
iam_policy_no_wildcard_marketplace_subscribe,
)
check = iam_policy_no_wildcard_marketplace_subscribe()
result = check.execute()
assert len(result) == 0
@@ -15,6 +15,7 @@ from prowler.providers.aws.services.iam.lib.policy import (
is_condition_restricting_from_private_ip,
is_condition_restricting_to_trusted_ips,
is_policy_public,
policy_allows_marketplace_subscribe_on_all_resources,
)
TRUSTED_AWS_ACCOUNT_NUMBER = "123456789012"
@@ -137,6 +138,47 @@ class Test_Policy:
assert result == {"s3:GetObject", "s3:ListBucket"}
assert "s3:PutObject" not in result
def test_policy_allows_marketplace_subscribe_conditional_deny_does_not_cancel(self):
"""Conditional deny should not globally cancel a wildcard marketplace allow."""
policy = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "aws-marketplace:Subscribe",
"Resource": "*",
},
{
"Effect": "Deny",
"Action": "aws-marketplace:Subscribe",
"Resource": "*",
"Condition": {"StringEquals": {"aws:RequestedRegion": "us-east-1"}},
},
],
}
assert policy_allows_marketplace_subscribe_on_all_resources(policy)
def test_policy_allows_marketplace_subscribe_unconditional_deny_cancels(self):
"""Unconditional deny on Resource:* should cancel the wildcard marketplace allow."""
policy = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "aws-marketplace:Subscribe",
"Resource": "*",
},
{
"Effect": "Deny",
"Action": "aws-marketplace:Subscribe",
"Resource": "*",
},
],
}
assert not policy_allows_marketplace_subscribe_on_all_resources(policy)
# Test lowercase context key name --> aws
def test_condition_parser_string_equals_aws_SourceAccount_list(self):
condition_statement = {