-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add S3LifecycleConfiguration rule (#188)
Co-authored-by: Oliver Crawford <oliver.crawford@skyscanner.net>
- Loading branch information
1 parent
0b46775
commit 71dcd6a
Showing
8 changed files
with
160 additions
and
1 deletion.
There are no files selected for viewing
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
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 |
---|---|---|
@@ -1,3 +1,3 @@ | ||
VERSION = (1, 0, 7) | ||
VERSION = (1, 0, 8) | ||
|
||
__version__ = ".".join(map(str, VERSION)) |
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
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,63 @@ | ||
__all__ = ["S3LifecycleConfigurationRule"] | ||
|
||
from typing import Dict, Optional | ||
|
||
from pycfmodel.model.cf_model import CFModel | ||
|
||
from cfripper.model.enums import RuleGranularity, RuleRisk | ||
from cfripper.model.result import Result | ||
from cfripper.rules.base_rules import Rule | ||
|
||
|
||
class S3LifecycleConfigurationRule(Rule): | ||
""" | ||
Checks for the presence of `LifecycleConfiguration` on S3 buckets. | ||
These rules can help with security, compliance, and reduce AWS Costs. The rule does not | ||
check the specific rules contained with the `LifecycleConfiguration` key. | ||
Fix: | ||
Add `LifecycleConfiguration` property to the S3 Bucket as defined in | ||
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-bucket-lifecycleconfig.html. | ||
Code for fix: | ||
An example rule is included within the configuration. | ||
````yml | ||
Resources: | ||
S3Bucket: | ||
Type: AWS::S3::Bucket | ||
Properties: | ||
... | ||
LifecycleConfiguration: | ||
Rules: | ||
- Status: Enabled | ||
Prefix: logs/ | ||
ExpirationInDays: 7 | ||
... | ||
```` | ||
Filters context: | ||
| Parameter | Type | Description | | ||
|:-------------:|:------------------:|:--------------------------------------------------------------:| | ||
|`config` | str | `config` variable available inside the rule | | ||
|`extras` | str | `extras` variable available inside the rule | | ||
|`logical_id` | str | ID used in Cloudformation to refer the resource being analysed | | ||
|`resource` | `S3BucketPolicy` | Resource that is being addressed | | ||
|`bucket_name` | str | Name of the S3 bucket being analysed | | ||
""" | ||
|
||
GRANULARITY = RuleGranularity.RESOURCE | ||
REASON = "S3 Bucket {} is required to contain a LifecycleConfiguration property" | ||
RISK_VALUE = RuleRisk.LOW | ||
|
||
def invoke(self, cfmodel: CFModel, extras: Optional[Dict] = None) -> Result: | ||
result = Result() | ||
for logical_id, resource in cfmodel.resources_filtered_by_type(("AWS::S3::Bucket",)).items(): | ||
if hasattr(resource, "Properties") and resource.Properties.get("LifecycleConfiguration") is None: | ||
self.add_failure_to_result( | ||
result, | ||
self.REASON.format(logical_id), | ||
resource_ids={logical_id}, | ||
context={"config": self._config, "extras": extras, "logical_id": logical_id, "resource": resource}, | ||
) | ||
return result |
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,57 @@ | ||
import pytest | ||
from pytest import fixture | ||
|
||
from cfripper.config.config import Config | ||
from cfripper.model.enums import RuleGranularity, RuleMode, RuleRisk | ||
from cfripper.model.result import Failure | ||
from cfripper.rules import S3LifecycleConfigurationRule | ||
from tests.utils import compare_lists_of_failures, get_cfmodel_from | ||
|
||
|
||
@fixture() | ||
def bad_template_no_configuration(): | ||
return get_cfmodel_from("rules/S3LifecycleConfiguration/bad_template_no_configurations.yaml").resolve() | ||
|
||
|
||
@pytest.mark.parametrize( | ||
"template_path", | ||
[ | ||
"rules/S3LifecycleConfiguration/good_template.yaml", | ||
"rules/S3LifecycleConfiguration/allowed_template_malformed_lifecycle_rules.yaml", | ||
], | ||
) | ||
def test_no_failures_are_raised(template_path): | ||
rule = S3LifecycleConfigurationRule(None) | ||
result = rule.invoke(get_cfmodel_from(template_path).resolve()) | ||
|
||
assert result.valid | ||
assert compare_lists_of_failures(result.failures, []) | ||
|
||
|
||
def test_failures_are_raised(bad_template_no_configuration): | ||
rule = S3LifecycleConfigurationRule(Config()) | ||
result = rule.invoke(bad_template_no_configuration) | ||
|
||
assert not result.valid | ||
assert compare_lists_of_failures( | ||
result.failures, | ||
[ | ||
Failure( | ||
granularity=RuleGranularity.RESOURCE, | ||
reason="S3 Bucket OutputBucket is required to contain a LifecycleConfiguration property", | ||
risk_value=RuleRisk.LOW, | ||
rule="S3LifecycleConfigurationRule", | ||
rule_mode=RuleMode.BLOCKING, | ||
actions=None, | ||
resource_ids={"OutputBucket"}, | ||
) | ||
], | ||
) | ||
|
||
|
||
def test_rule_supports_filter_config(bad_template_no_configuration, default_allow_all_config): | ||
rule = S3LifecycleConfigurationRule(default_allow_all_config) | ||
result = rule.invoke(bad_template_no_configuration) | ||
|
||
assert result.valid | ||
assert compare_lists_of_failures(result.failures, []) |
10 changes: 10 additions & 0 deletions
10
..._templates/rules/S3LifecycleConfiguration/allowed_template_malformed_lifecycle_rules.yaml
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,10 @@ | ||
Resources: | ||
OutputBucket: | ||
Type: AWS::S3::Bucket | ||
Properties: | ||
BucketName: "foo" | ||
AccessControl: BucketOwnerFullControl | ||
LifecycleConfiguration: | ||
# This is not valid for LifecycleConfiguration, but CFRipper will not parse it right now. | ||
- aa | ||
- bb |
6 changes: 6 additions & 0 deletions
6
tests/test_templates/rules/S3LifecycleConfiguration/bad_template_no_configurations.yaml
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,6 @@ | ||
Resources: | ||
OutputBucket: | ||
Type: AWS::S3::Bucket | ||
Properties: | ||
BucketName: "foo" | ||
AccessControl: BucketOwnerFullControl |
17 changes: 17 additions & 0 deletions
17
tests/test_templates/rules/S3LifecycleConfiguration/good_template.yaml
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,17 @@ | ||
Resources: | ||
OutputBucket: | ||
Type: AWS::S3::Bucket | ||
Properties: | ||
BucketName: "foo" | ||
AccessControl: BucketOwnerFullControl | ||
LifecycleConfiguration: | ||
Rules: | ||
- Status: Enabled | ||
Prefix: logs/ | ||
ExpirationInDays: !Ref LogsExpirationInDays | ||
- Status: Enabled | ||
Prefix: output/ | ||
ExpirationInDays: !Ref ModelsExpirationInDays | ||
- AbortIncompleteMultipartUpload: | ||
DaysAfterInitiation: 7 | ||
Status: Enabled |