Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/improved regex matching #103

Merged
merged 4 commits into from
Jun 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# CHANGELOG

### 1.6.0 (2019-06-25)
* Improved 'contains' and 'regex' matching steps.

### 1.0.5 (2019-06-24)
* Added basic heredoc support for json strings. ([#90](https://github.com/eerkunt/terraform-compliance/issues/90))
* Added encryption property for (at rest) aws_emr_security_configuration
Expand All @@ -18,7 +21,7 @@
* Fixed an issue where `filetype` module could not be found. ([#97](https://github.com/eerkunt/terraform-compliance/issues/97))
* Upgraded python in Docker image from 3.6.8 to 3.7.3

## 1.0.0 (2019-06-20)
# 1.0.0 (2019-06-20)
**_BREAKING BACKWARD COMPATIBILITY_** for `terraform-compliance` since the parameters has changed. This is a MAJOR upgrade and a re-design of the tool.
* This version only supports `terraform` 0.12.0 and 0.12.1.
* Removed `-t` parameter.
Expand Down
2 changes: 1 addition & 1 deletion example/example_01/aws/naming_standards.feature
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Feature: Resources should have a proper naming standard
| AWS EC2 instance | name |
| AWS ELB resource | name |
| AWS RDS instance | name |
| AWS S3 Bucket | name |
| AWS S3 Bucket | bucket |
| AWS EBS volume | name |
| AWS Auto-Scaling Group | name |
| aws_key_pair | key_name |
Expand Down
2 changes: 1 addition & 1 deletion example/example_01/aws/s3_public_access_block.feature
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Feature: All AWS environments that has S3 Bucket Public Access Block
Scenario Outline: AWS Credentials should not be hardcoded
Given I have aws_s3_bucket_public_access_block resource configured
Then it must contain <key>
And its value must not match the "true" regex
And its value must match the "true" regex

Examples:
| key |
Expand Down
2 changes: 1 addition & 1 deletion terraform_compliance/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
reinstall_radish()

__app_name__ = "terraform-compliance"
__version__ = "1.0.5"
__version__ = "1.0.6"

print('{} v{} initiated\n'.format(__app_name__, __version__))

Expand Down
47 changes: 32 additions & 15 deletions terraform_compliance/steps/steps.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
import re
from terraform_compliance.common.exceptions import Failure, TerraformComplianceNotImplemented

#TODO: Figure out how the IAM policies/statements shown in the plan.out
#TODO: Implement an IAM Compliance via https://github.com/Netflix-Skunkworks/policyuniverse

# TODO: Figure out how the IAM policies/statements shown in the plan.out
# TODO: Implement an IAM Compliance via https://github.com/Netflix-Skunkworks/policyuniverse


@given(u'I have {name:ANY} defined')
Expand Down Expand Up @@ -78,6 +79,7 @@ def i_have_name_section_configured(_step_obj, name, type_name='resource', _terra

skip_step(_step_obj, name)


@when(u'it contain {something:ANY}')
@when(u'they have {something:ANY}')
@when(u'it has {something:ANY}')
Expand All @@ -96,10 +98,16 @@ def it_condition_contain_something(_step_obj, something):
values = resource.get('values', {})

found_value = None
found_key = None
if type(values) is dict:
found_value = jsonify(values.get(something, None))
found_key = seek_key_in_dict(values, something)
if len(found_key):
found_key = found_key[0]

if type(found_key) is dict:
found_value = jsonify(found_key[something])

if found_value:
if found_key:
prop_list.append({'address': resource['address'],
'values': found_value,
'type': _step_obj.context.name})
Expand Down Expand Up @@ -132,6 +140,7 @@ def it_condition_contain_something(_step_obj, something):
message='Skipping the step since {} type does not have {} property.'.format(_step_obj.context.type,
something))


@then(u'encryption is enabled')
@then(u'encryption must be enabled')
def encryption_is_enabled(_step_obj):
Expand All @@ -158,6 +167,7 @@ def encryption_is_enabled(_step_obj):

return True


@then(u'it must {condition:ANY} have {proto:ANY} protocol and port {port} for {cidr:ANY}')
def it_condition_have_proto_protocol_and_port_port_for_cidr(_step_obj, condition, proto, port, cidr):
proto = str(proto)
Expand Down Expand Up @@ -212,6 +222,7 @@ def i_action_them(_step_obj, action_type):
else:
raise TerraformComplianceNotImplemented("Invalid action_type in the scenario: {}".format(action_type))


@then(u'I expect the result is {operator:ANY} than {number:d}')
def i_expect_the_result_is_operator_than_number(_step_obj, operator, number):
# TODO: Maybe iterate over the stash if it is a list and do the execution per each member ?
Expand All @@ -228,24 +239,24 @@ def i_expect_the_result_is_operator_than_number(_step_obj, operator, number):
else:
raise TerraformComplianceNotImplemented('Invalid operator: {}'.format(operator))


@step(u'its value {condition:ANY} match the "{search_regex}" regex')
def its_value_condition_match_the_search_regex_regex(_step_obj, condition, search_regex, _stash=None):

def fail(condition):
text = 'matches' if condition == 'must not' else 'does not match'
raise Failure('{} property in {} {} {} with {} regex. '
'It is set to {}.'.format(_step_obj.context.property_name,
_step_obj.context.name,
_step_obj.context.type,
text,
regex,
values))
_step_obj.context.name,
_step_obj.context.type,
text,
regex,
values))

regex = r'{}'.format(search_regex)
values = _step_obj.context.stash if _stash is None else _stash

if type(values) is str or type(values) is int:
matches = re.match(regex, values)
if type(values) is str or type(values) is int or type(values) is bool:
matches = re.match(regex, str(values), flags=re.IGNORECASE)

if (condition == 'must' and matches is None) or (condition == "must not" and matches is not None):
fail(condition)
Expand All @@ -255,7 +266,13 @@ def fail(condition):
its_value_condition_match_the_search_regex_regex(_step_obj, condition, search_regex, value)

elif type(values) is dict:
values = seek_regex_key_in_dict_values(values, _step_obj.context.property_name, search_regex)

if (condition == 'must' and values is None) or (condition == "must not" and values is not None):
if values.get('values') is not None:
values = its_value_condition_match_the_search_regex_regex(_step_obj,
condition,
search_regex,
values['values'])
else:
values = seek_regex_key_in_dict_values(values, _step_obj.context.property_name, search_regex)

if (condition == 'must' and values == []) or (condition == "must not" and values != []):
fail(condition)