mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-04 19:21:51 +00:00
fix(s3): resource metadata error in s3_bucket_shadow_resource_vulnerability (#8572)
This commit is contained in:
committed by
GitHub
parent
a5ba950627
commit
3b42eb3818
@@ -22,6 +22,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
- Improve AWS Security Hub region check using multiple threads [(#8365)](https://github.com/prowler-cloud/prowler/pull/8365)
|
||||
|
||||
### Fixed
|
||||
- Resource metadata error in `s3_bucket_shadow_resource_vulnerability` check [(#8572)](https://github.com/prowler-cloud/prowler/pull/8572)
|
||||
|
||||
---
|
||||
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
"CheckID": "s3_bucket_shadow_resource_vulnerability",
|
||||
"CheckTitle": "Check for S3 buckets vulnerable to Shadow Resource Hijacking (Bucket Monopoly)",
|
||||
"CheckType": [
|
||||
""
|
||||
"Effects/Data Exposure"
|
||||
],
|
||||
"ServiceName": "s3",
|
||||
"SubServiceName": "",
|
||||
|
||||
+4
-14
@@ -70,22 +70,12 @@ class s3_bucket_shadow_resource_vulnerability(Check):
|
||||
)
|
||||
# Check if this bucket exists in another account
|
||||
if s3_client._head_bucket(bucket_name):
|
||||
# Create a virtual bucket object for reporting
|
||||
virtual_bucket = type(
|
||||
"obj",
|
||||
(object,),
|
||||
{
|
||||
"name": bucket_name,
|
||||
"region": region,
|
||||
"arn": f"arn:{s3_client.audited_partition}:s3:::{bucket_name}",
|
||||
"tags": [],
|
||||
},
|
||||
)()
|
||||
|
||||
report = Check_Report_AWS(self.metadata(), resource=virtual_bucket)
|
||||
report = Check_Report_AWS(self.metadata(), resource={})
|
||||
report.region = region
|
||||
report.resource_id = bucket_name
|
||||
report.resource_arn = virtual_bucket.arn
|
||||
report.resource_arn = (
|
||||
f"arn:{s3_client.audited_partition}:s3:::{bucket_name}"
|
||||
)
|
||||
report.resource_tags = []
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"S3 bucket {bucket_name} for service {service} is a known shadow resource that exists and is owned by another account."
|
||||
|
||||
+67
-43
@@ -13,13 +13,14 @@ from tests.providers.aws.utils import (
|
||||
class Test_s3_bucket_shadow_resource_vulnerability:
|
||||
@mock_aws
|
||||
def test_no_buckets(self):
|
||||
s3_client = mock.MagicMock
|
||||
s3_client.buckets = {}
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
aws_provider.identity.identity_arn = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
s3_client = mock.MagicMock
|
||||
|
||||
s3_client = mock.MagicMock()
|
||||
s3_client.buckets = {}
|
||||
s3_client.provider = aws_provider
|
||||
s3_client._head_bucket = mock.MagicMock(return_value=False)
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
@@ -36,28 +37,31 @@ class Test_s3_bucket_shadow_resource_vulnerability:
|
||||
|
||||
check = s3_bucket_shadow_resource_vulnerability()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
@mock_aws
|
||||
def test_bucket_owned_by_account(self):
|
||||
s3_client = mock.MagicMock
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
aws_provider.identity.identity_arn = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
|
||||
bucket_name = f"sagemaker-{AWS_REGION_US_EAST_1}-{AWS_ACCOUNT_NUMBER}"
|
||||
s3_client.audited_account_id = AWS_ACCOUNT_NUMBER
|
||||
s3_client.audited_identity_arn = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
|
||||
s3_client = mock.MagicMock()
|
||||
s3_client.audited_canonical_id = AWS_ACCOUNT_NUMBER
|
||||
s3_client.audited_partition = "aws"
|
||||
s3_client.buckets = {
|
||||
bucket_name: Bucket(
|
||||
name=bucket_name,
|
||||
arn=f"arn:aws:s3:::{bucket_name}",
|
||||
region=AWS_REGION_US_EAST_1,
|
||||
owner_id=AWS_ACCOUNT_NUMBER,
|
||||
tags=[{"Key": "Environment", "Value": "test"}],
|
||||
)
|
||||
}
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
aws_provider.identity.identity_arn = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
s3_client = mock.MagicMock
|
||||
s3_client.provider = aws_provider
|
||||
s3_client._head_bucket = mock.MagicMock(return_value=False)
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
@@ -74,32 +78,41 @@ class Test_s3_bucket_shadow_resource_vulnerability:
|
||||
|
||||
check = s3_bucket_shadow_resource_vulnerability()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
"is correctly owned by the audited account" in result[0].status_extended
|
||||
)
|
||||
report = result[0]
|
||||
|
||||
# Test all report attributes
|
||||
assert report.status == "PASS"
|
||||
assert report.region == AWS_REGION_US_EAST_1
|
||||
assert report.resource_id == bucket_name
|
||||
assert report.resource_arn == f"arn:aws:s3:::{bucket_name}"
|
||||
assert report.resource_tags == [{"Key": "Environment", "Value": "test"}]
|
||||
assert "is correctly owned by the audited account" in report.status_extended
|
||||
assert "SageMaker" in report.status_extended
|
||||
|
||||
@mock_aws
|
||||
def test_bucket_not_predictable(self):
|
||||
s3_client = mock.MagicMock
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
aws_provider.identity.identity_arn = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
|
||||
bucket_name = "my-non-predictable-bucket"
|
||||
s3_client.audited_account_id = AWS_ACCOUNT_NUMBER
|
||||
s3_client.audited_identity_arn = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
|
||||
s3_client = mock.MagicMock()
|
||||
s3_client.audited_canonical_id = AWS_ACCOUNT_NUMBER
|
||||
s3_client.audited_partition = "aws"
|
||||
s3_client.buckets = {
|
||||
bucket_name: Bucket(
|
||||
name=bucket_name,
|
||||
arn=f"arn:aws:s3:::{bucket_name}",
|
||||
region=AWS_REGION_US_EAST_1,
|
||||
owner_id=AWS_ACCOUNT_NUMBER,
|
||||
tags=[{"Key": "Project", "Value": "test-project"}],
|
||||
)
|
||||
}
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
aws_provider.identity.identity_arn = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
s3_client = mock.MagicMock
|
||||
s3_client.provider = aws_provider
|
||||
s3_client._head_bucket = mock.MagicMock(return_value=False)
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
@@ -116,18 +129,25 @@ class Test_s3_bucket_shadow_resource_vulnerability:
|
||||
|
||||
check = s3_bucket_shadow_resource_vulnerability()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert "is not a known shadow resource" in result[0].status_extended
|
||||
report = result[0]
|
||||
|
||||
# Test all report attributes
|
||||
assert report.status == "PASS"
|
||||
assert report.region == AWS_REGION_US_EAST_1
|
||||
assert report.resource_id == bucket_name
|
||||
assert report.resource_arn == f"arn:aws:s3:::{bucket_name}"
|
||||
assert report.resource_tags == [{"Key": "Project", "Value": "test-project"}]
|
||||
assert "is not a known shadow resource" in report.status_extended
|
||||
|
||||
@mock_aws
|
||||
def test_shadow_resource_in_other_account(self):
|
||||
# Mock S3 client with no buckets in current account
|
||||
s3_client = mock.MagicMock()
|
||||
s3_client.buckets = {}
|
||||
s3_client.audited_account_id = AWS_ACCOUNT_NUMBER
|
||||
s3_client.audited_identity_arn = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
s3_client.audited_canonical_id = AWS_ACCOUNT_NUMBER
|
||||
s3_client.audited_identity_arn = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
s3_client.audited_partition = "aws"
|
||||
|
||||
# Mock regional clients - this is what the check uses to determine regions to test
|
||||
@@ -183,27 +203,31 @@ class Test_s3_bucket_shadow_resource_vulnerability:
|
||||
finding.status == "FAIL"
|
||||
and "shadow resource" in finding.status_extended
|
||||
):
|
||||
if (
|
||||
"aws-glue-assets" in finding.status_extended
|
||||
and "Glue" in finding.status_extended
|
||||
):
|
||||
found_services.add("Glue")
|
||||
assert "us-west-2" in finding.status_extended
|
||||
elif (
|
||||
"sagemaker" in finding.status_extended
|
||||
and "SageMaker" in finding.status_extended
|
||||
):
|
||||
found_services.add("SageMaker")
|
||||
assert "us-east-1" in finding.status_extended
|
||||
elif (
|
||||
"aws-emr-studio" in finding.status_extended
|
||||
and "EMR" in finding.status_extended
|
||||
):
|
||||
found_services.add("EMR")
|
||||
assert "eu-west-1" in finding.status_extended
|
||||
# Test all report attributes for cross-account findings
|
||||
assert finding.status == "FAIL"
|
||||
assert finding.resource_id in shadow_resources
|
||||
assert finding.resource_arn == f"arn:aws:s3:::{finding.resource_id}"
|
||||
assert finding.resource_tags == []
|
||||
|
||||
# Verify common attributes
|
||||
assert "owned by another account" in finding.status_extended
|
||||
# Determine service from resource_id and test exact status_extended
|
||||
if "aws-glue-assets" in finding.resource_id:
|
||||
service = "Glue"
|
||||
expected_status = f"S3 bucket {finding.resource_id} for service {service} is a known shadow resource that exists and is owned by another account."
|
||||
assert finding.status_extended == expected_status
|
||||
found_services.add("Glue")
|
||||
assert finding.region == "us-west-2"
|
||||
elif "sagemaker" in finding.resource_id:
|
||||
service = "SageMaker"
|
||||
expected_status = f"S3 bucket {finding.resource_id} for service {service} is a known shadow resource that exists and is owned by another account."
|
||||
assert finding.status_extended == expected_status
|
||||
found_services.add("SageMaker")
|
||||
assert finding.region == "us-east-1"
|
||||
elif "aws-emr-studio" in finding.resource_id:
|
||||
service = "EMR"
|
||||
expected_status = f"S3 bucket {finding.resource_id} for service {service} is a known shadow resource that exists and is owned by another account."
|
||||
assert finding.status_extended == expected_status
|
||||
found_services.add("EMR")
|
||||
assert finding.region == "eu-west-1"
|
||||
|
||||
# Verify we found all expected services
|
||||
expected_services = {"Glue", "SageMaker", "EMR"}
|
||||
|
||||
Reference in New Issue
Block a user