-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(s3): add
s3_bucket_lifecycle_enabled
check (#4801)
Co-authored-by: Sergio Garcia <[email protected]>
- Loading branch information
1 parent
0b23824
commit 57f1fa5
Showing
6 changed files
with
319 additions
and
1 deletion.
There are no files selected for viewing
Empty file.
32 changes: 32 additions & 0 deletions
32
...ers/aws/services/s3/s3_bucket_lifecycle_enabled/s3_bucket_lifecycle_enabled.metadata.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
{ | ||
"Provider": "aws", | ||
"CheckID": "s3_bucket_lifecycle_enabled", | ||
"CheckTitle": "Check if S3 buckets have a Lifecycle configuration enabled", | ||
"CheckType": [ | ||
"AWS Foundational Security Best Practices" | ||
], | ||
"ServiceName": "s3", | ||
"SubServiceName": "", | ||
"ResourceIdTemplate": "arn:partition:s3:::bucket_name", | ||
"Severity": "low", | ||
"ResourceType": "AwsS3Bucket", | ||
"Description": "Check if S3 buckets have Lifecycle configuration enabled.", | ||
"Risk": "The risks of not having lifecycle management enabled for S3 buckets include higher storage costs, unmanaged data retention, and potential non-compliance with data policies.", | ||
"RelatedUrl": "https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lifecycle-mgmt.html", | ||
"Remediation": { | ||
"Code": { | ||
"CLI": "", | ||
"NativeIaC": "", | ||
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/s3-controls.html#s3-13", | ||
"Terraform": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/S3/lifecycle-configuration.html" | ||
}, | ||
"Recommendation": { | ||
"Text": "Enable lifecycle policies on your S3 buckets to automatically manage the transition and expiration of data.", | ||
"Url": "https://docs.aws.amazon.com/AmazonS3/latest/userguide/how-to-set-lifecycle-configuration-intro.html" | ||
} | ||
}, | ||
"Categories": [], | ||
"DependsOn": [], | ||
"RelatedTo": [], | ||
"Notes": "" | ||
} |
26 changes: 26 additions & 0 deletions
26
prowler/providers/aws/services/s3/s3_bucket_lifecycle_enabled/s3_bucket_lifecycle_enabled.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
from prowler.lib.check.models import Check, Check_Report_AWS | ||
from prowler.providers.aws.services.s3.s3_client import s3_client | ||
|
||
|
||
class s3_bucket_lifecycle_enabled(Check): | ||
def execute(self): | ||
findings = [] | ||
for arn, bucket in s3_client.buckets.items(): | ||
report = Check_Report_AWS(self.metadata()) | ||
report.region = bucket.region | ||
report.resource_id = bucket.name | ||
report.resource_arn = arn | ||
report.resource_tags = bucket.tags | ||
report.status = "FAIL" | ||
report.status_extended = f"S3 Bucket {bucket.name} does not have a lifecycle configuration enabled." | ||
|
||
if bucket.lifecycle: | ||
for configuration in bucket.lifecycle: | ||
if configuration.status == "Enabled": | ||
report.status = "PASS" | ||
report.status_extended = f"S3 Bucket {bucket.name} has a lifecycle configuration enabled." | ||
break | ||
|
||
findings.append(report) | ||
|
||
return findings |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
182 changes: 182 additions & 0 deletions
182
...providers/aws/services/s3/s3_bucket_lifecycle_enabled/s3_bucket_lifecycle_enabled_test.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
from unittest import mock | ||
from unittest.mock import patch | ||
|
||
from moto import mock_aws | ||
|
||
from prowler.providers.aws.services.s3.s3_service import S3, Bucket, LifeCycleRule | ||
from tests.providers.aws.utils import ( | ||
AWS_ACCOUNT_NUMBER, | ||
AWS_REGION_US_EAST_1, | ||
set_mocked_aws_provider, | ||
) | ||
|
||
|
||
class Test_s3_bucket_lifecycle_enabled: | ||
@mock_aws | ||
def test_no_buckets(self): | ||
from prowler.providers.aws.services.s3.s3_service import S3 | ||
|
||
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.s3.s3_bucket_no_mfa_delete.s3_bucket_no_mfa_delete.s3_client", | ||
new=S3(aws_provider), | ||
): | ||
# Test Check | ||
from prowler.providers.aws.services.s3.s3_bucket_no_mfa_delete.s3_bucket_no_mfa_delete import ( | ||
s3_bucket_no_mfa_delete, | ||
) | ||
|
||
check = s3_bucket_no_mfa_delete() | ||
result = check.execute() | ||
|
||
assert len(result) == 0 | ||
|
||
def test_no_lifecycle_configuration(self): | ||
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, | ||
), mock.patch( | ||
"prowler.providers.aws.services.s3.s3_bucket_lifecycle_enabled.s3_bucket_lifecycle_enabled.s3_client", | ||
new=S3(aws_provider), | ||
): | ||
# Test Check | ||
from prowler.providers.aws.services.s3.s3_bucket_lifecycle_enabled.s3_bucket_lifecycle_enabled import ( | ||
s3_bucket_lifecycle_enabled, | ||
) | ||
|
||
bucket_name = "bucket-test" | ||
bucket_arn = f"arn:aws:s3::{AWS_ACCOUNT_NUMBER}:{bucket_name}" | ||
s3_client = mock.MagicMock() | ||
s3_client.buckets = { | ||
bucket_arn: Bucket( | ||
name=bucket_name, | ||
region=AWS_REGION_US_EAST_1, | ||
) | ||
} | ||
|
||
with patch( | ||
"prowler.providers.aws.services.s3.s3_bucket_lifecycle_enabled.s3_bucket_lifecycle_enabled.s3_client", | ||
s3_client, | ||
): | ||
check = s3_bucket_lifecycle_enabled() | ||
result = check.execute() | ||
|
||
# ALL REGIONS | ||
assert len(result) == 1 | ||
|
||
# AWS_REGION_US_EAST_1 | ||
assert result[0].status == "FAIL" | ||
assert ( | ||
result[0].status_extended | ||
== f"S3 Bucket {bucket_name} does not have a lifecycle configuration enabled." | ||
) | ||
assert result[0].resource_id == bucket_name | ||
assert result[0].resource_arn == bucket_arn | ||
assert result[0].region == AWS_REGION_US_EAST_1 | ||
|
||
def test_one_valid_lifecycle_configuration(self): | ||
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, | ||
), mock.patch( | ||
"prowler.providers.aws.services.s3.s3_bucket_lifecycle_enabled.s3_bucket_lifecycle_enabled.s3_client", | ||
new=S3(aws_provider), | ||
): | ||
# Test Check | ||
from prowler.providers.aws.services.s3.s3_bucket_lifecycle_enabled.s3_bucket_lifecycle_enabled import ( | ||
s3_bucket_lifecycle_enabled, | ||
) | ||
|
||
s3_client = mock.MagicMock() | ||
bucket_name = "bucket-test" | ||
bucket_arn = f"arn:aws:s3::{AWS_ACCOUNT_NUMBER}:{bucket_name}" | ||
s3_client.buckets = { | ||
bucket_arn: Bucket( | ||
name=bucket_name, | ||
region=AWS_REGION_US_EAST_1, | ||
lifecycle=[ | ||
LifeCycleRule( | ||
id="test-rule-1", | ||
status="Enabled", | ||
), | ||
], | ||
) | ||
} | ||
|
||
with patch( | ||
"prowler.providers.aws.services.s3.s3_bucket_lifecycle_enabled.s3_bucket_lifecycle_enabled.s3_client", | ||
s3_client, | ||
): | ||
check = s3_bucket_lifecycle_enabled() | ||
result = check.execute() | ||
|
||
assert len(result) == 1 | ||
assert result[0].status == "PASS" | ||
assert ( | ||
result[0].status_extended | ||
== f"S3 Bucket {bucket_name} has a lifecycle configuration enabled." | ||
) | ||
assert result[0].resource_id == bucket_name | ||
assert result[0].resource_arn == bucket_arn | ||
assert result[0].region == AWS_REGION_US_EAST_1 | ||
|
||
def test_several_lifecycle_configurations(self): | ||
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, | ||
), mock.patch( | ||
"prowler.providers.aws.services.s3.s3_bucket_lifecycle_enabled.s3_bucket_lifecycle_enabled.s3_client", | ||
new=S3(aws_provider), | ||
): | ||
# Test Check | ||
from prowler.providers.aws.services.s3.s3_bucket_lifecycle_enabled.s3_bucket_lifecycle_enabled import ( | ||
s3_bucket_lifecycle_enabled, | ||
) | ||
|
||
s3_client = mock.MagicMock() | ||
bucket_name = "bucket-test" | ||
bucket_arn = f"arn:aws:s3::{AWS_ACCOUNT_NUMBER}:{bucket_name}" | ||
s3_client.buckets = { | ||
bucket_arn: Bucket( | ||
name=bucket_name, | ||
region=AWS_REGION_US_EAST_1, | ||
lifecycle=[ | ||
LifeCycleRule( | ||
id="test-rule-1", | ||
status="Disabled", | ||
), | ||
LifeCycleRule( | ||
id="test-rule-2", | ||
status="Enabled", | ||
), | ||
], | ||
) | ||
} | ||
|
||
with patch( | ||
"prowler.providers.aws.services.s3.s3_bucket_lifecycle_enabled.s3_bucket_lifecycle_enabled.s3_client", | ||
s3_client, | ||
): | ||
check = s3_bucket_lifecycle_enabled() | ||
result = check.execute() | ||
|
||
assert len(result) == 1 | ||
assert result[0].status == "PASS" | ||
assert ( | ||
result[0].status_extended | ||
== f"S3 Bucket {bucket_name} has a lifecycle configuration enabled." | ||
) | ||
assert result[0].resource_id == bucket_name | ||
assert result[0].resource_arn == bucket_arn | ||
assert result[0].region == AWS_REGION_US_EAST_1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters