예제 #1
0
def it_condition_have_proto_protocol_and_port_port_for_cidr(
        _step_obj, condition, proto, port, cidr):
    searching_for = dict(port=port, protocol=proto, cidr_blocks=cidr)

    for sg in _step_obj.context.stash:
        if sg['type'] != 'aws_security_group':
            raise TerraformComplianceInternalFailure(
                'This method can only be used for aws_security_group resources '
                'for now. You tried to used it on {}'.format(sg['type']))

        sg_obj = SecurityGroup(searching_for,
                               sg['values'],
                               address=sg['address'])
        if condition == 'must only':
            sg_obj.must_only_have()
        elif condition == 'must':
            sg_obj.must_have()
        elif condition == 'must not':
            sg_obj.must_not_have()
        else:
            raise TerraformComplianceInternalFailure(
                'You can only use "must have", "must not have" and "must only have"'
                'conditions on this step for now.'
                'You tried to use "{}"'.format(condition))
        result, message = sg_obj.validate()

        if result is False:
            Error(_step_obj, message)

    return True
예제 #2
0
파일: helper.py 프로젝트: timgates42/cli
    def issubset(self, set1, set2):
        if not isinstance(set1, Iterable) or isinstance(set1, str):
            raise TerraformComplianceInternalFailure('{} should be a non-str iterable'.format(set1))

        if not isinstance(set2, Iterable) or isinstance(set2, str):
            raise TerraformComplianceInternalFailure('{} should be a non-str iterable'.format(set2))

        if not self.case_sensitive:
            set1 = set(str(e).lower() for e in set1)
            set2 = set(str(e).lower() for e in set2)

        return set1 <= set2
예제 #3
0
def it_condition_have_proto_protocol_and_port_port_for_cidr(
        _step_obj, condition, proto, port, cidr):
    proto = str(proto)
    cidr = str(cidr)

    # Set to True only if the condition is 'only'
    condition = condition == 'only'

    # In case we have a range
    if '-' in port:
        if condition:
            raise Failure(
                '"must only" scenario cases must be used either with individual port '
                'or multiple ports separated with comma.')

        from_port, to_port = port.split('-')
        ports = [from_port, to_port]

    # In case we have comma delimited ports
    elif ',' in port:
        ports = [port for port in port.split(',')]
        from_port = min(ports)
        to_port = max(ports)

    else:
        from_port = to_port = int(port)
        ports = list(set([str(from_port), str(to_port)]))

    from_port = int(from_port) if int(from_port) > 0 else 1
    to_port = int(to_port) if int(to_port) > 0 else 1
    ports[0] = ports[0] if int(ports[0]) > 0 else '1'

    looking_for = dict(proto=proto,
                       from_port=int(from_port),
                       to_port=int(to_port),
                       ports=ports,
                       cidr=cidr)

    for security_group in _step_obj.context.stash:
        if type(security_group['values']) is list:
            for sg in security_group['values']:
                check_sg_rules(plan_data=sg,
                               security_group=looking_for,
                               condition=condition)

        elif type(security_group['values']) is dict:
            check_sg_rules(plan_data=security_group['values'],
                           security_group=looking_for,
                           condition=condition)
        else:
            raise TerraformComplianceInternalFailure(
                'Unexpected Security Group, '
                'must be either list or a dict: '
                '{}'.format(security_group['values']))
    return True
예제 #4
0
    def __call__(self, parser, namespace, values, option_string=None):
        # Check if the given path is a file
        if not os.path.isfile(values):
            print('ERROR: {} is not a file.'.format(values))
            sys.exit(1)

        # Check if the given file is a native terraform plan file
        given_file = filetype.guess(values)

        if given_file is not None:
            terraform_executable = getattr(namespace, 'terraform_file', None)
            values = convert_terraform_plan_to_json(os.path.abspath(values),
                                                    terraform_executable)

        # Check if the given file is a json file.
        try:
            with open(values, 'r') as plan_file:
                data = json.load(plan_file)

        except json.decoder.JSONDecodeError:
            print('ERROR: {} is not a valid JSON file'.format(values))
            sys.exit(1)
        except UnicodeDecodeError:
            print('ERROR: {} is not a valid JSON file.'.format(values))
            print(
                '       Did you try to convert the binary plan file to json with '
                '"terraform show -json {} > {}.json" ?'.format(values, values))
            sys.exit(1)
        except:
            raise TerraformComplianceInternalFailure('Invalid file type.')

        # Check if this is a correct terraform plan file
        try:
            assert data['format_version']
            assert data['terraform_version']

            # Check if this is a state file
            if 'values' in data:
                assert data['values']['root_module']['resources']

            # Then it must be a terraform plan file
            else:
                assert data['planned_values']
                assert data['configuration']

        except KeyError:
            print(
                'ERROR: {} is not a valid terraform plan json output.'.format(
                    values))
            sys.exit(1)

        setattr(namespace, self.dest, os.path.abspath(values))
        return True
예제 #5
0
def it_must_have_reference_address_referenced(_step_obj, reference_address):
    if _step_obj.context.stash:
        for resource in _step_obj.context.stash:
            if isinstance(resource, dict):
                if Defaults.address_pointer in resource and search_regex_in_list(reference_address,
                                                                                 resource[Defaults.address_pointer]):
                    return True
            else:
                raise TerraformComplianceInternalFailure('Unexpected resource structure: {}'.format(resource))

            Error(_step_obj, '{} is not referenced within {}.'.format(reference_address, resource.get('address')))
    else:
        Error(_step_obj, 'No entities found for this step to process. Check your filtering steps in this scenario.')
예제 #6
0
파일: helper.py 프로젝트: timgates42/cli
def python_version_check():
    python_version = sys.version.split(' ')[0]

    if not python_version:
        raise TerraformComplianceInternalFailure('Could not determine python version. '
                                                 'Please post this to issues: '.format(sys.version))

    python_version = VersionInfo.parse(python_version)

    if compare(str(python_version), Defaults.supported_min_python_versions) < 0:
        console_write('ERROR: Python version {} is not supported. '
                      'You must have minimum {} version.'.format(python_version,
                                                                 Defaults.supported_min_python_versions[0]))
        sys.exit(1)

    return True
예제 #7
0
def _its_value_condition_contain(_step_obj, condition, value, values):
    if isinstance(values, list):
        values = [str(v) for v in values]
        _fail_text = 'did not contain' if condition == 'must' else 'contains'
        fail_message = '{} property in {} {} {}, it is set to {}'.format(
            _step_obj.context.property_name,
            _step_obj.context.name,
            _fail_text,
            value,
            values,
        )
        if condition == 'must':
            assert value in values, fail_message
        else:
            assert value not in values, fail_message
    else:
        raise TerraformComplianceInternalFailure('Can only check that if list contains value')
예제 #8
0
    def _expand_ports(self, port):
        if '-' in port:
            self.from_port, self.to_port = [int(port_number) for port_number in port.split('-')]
            self.from_port = self.from_port if self.from_port > 0 else 1
            self.to_port = self.to_port if self.to_port > 0 else 1
            self.ports = self._get_port_range(self.from_port, self.to_port)
            self.port_is_single = False

        elif ',' in port:
            self.from_port = None
            self.to_port = None
            self.port_is_single = False
            self.ports = set([int(port_number) for port_number in port.split(',') if int(port_number) > 0])

        elif port.isnumeric():
            self.from_port = self.to_port = int(port)
            self.ports = self._get_port_range(self.from_port, self.to_port)
            self.port_is_single = True

        else:
            raise TerraformComplianceInternalFailure('Unexpected situation. '
                                                     'Please report this port structure: {}'.format(vars(self)))
예제 #9
0
def it_condition_contain_something(_step_obj, something):
    prop_list = []

    if _step_obj.context.type in ('resource', 'data'):
        for resource in _step_obj.context.stash:
            if type(resource) is not dict:
                resource = {
                    'values': resource,
                    'address': resource,
                    'type': _step_obj.context.name
                }

            values = resource.get('values', resource.get('expressions', {}))

            found_value = Null
            found_key = Null
            if type(values) is dict:
                found_key = values.get(something,
                                       seek_key_in_dict(values, something))
                if type(found_key) is not list:
                    found_key = [{something: found_key}]

                if len(found_key):
                    found_key = found_key[0]

                    if type(found_key) is dict:
                        found_value = jsonify(found_key.get(something, {}))
            elif type(values) is list:
                for value in values:

                    if type(value) is dict:
                        # First search in the keys
                        found_key = seek_key_in_dict(value, something)

                        # Then search in the values with 'key'
                        if not found_key:
                            found_key = seek_regex_key_in_dict_values(
                                value, 'key', something)

                            if found_key:
                                found_key = found_key[0]
                                found_value = value.get('value')
                                break

                    if found_key is not Null and len(found_key):
                        found_key = found_key[0]

                        if type(found_key) is dict:
                            found_value = jsonify(found_key.get(something, {}))
                    else:
                        raise TerraformComplianceInternalFailure(
                            'Unexpected value type {}. {}'.format(
                                type(value), value))

            if type(found_value) is dict and 'constant_value' in found_value:
                found_value = found_value['constant_value']

            if found_key is not Null and found_key != []:
                prop_list.append({
                    'address': resource['address'],
                    'values': found_value,
                    'type': _step_obj.context.name
                })

            elif 'must' in _step_obj.context_sensitive_sentence:
                raise Failure('{} ({}) does not have {} property.'.format(
                    resource['address'], resource.get('type', ''), something))

        if prop_list:
            _step_obj.context.stash = prop_list
            _step_obj.context.property_name = something
            return True

        skip_step(_step_obj,
                  resource=_step_obj.context.name,
                  message='Can not find any {} property for {} resource in '
                  'terraform plan.'.format(something, _step_obj.context.name))

    elif _step_obj.context.type == 'provider':
        values = seek_key_in_dict(_step_obj.context.stash, something)

        if values:
            _step_obj.context.stash = values
            _step_obj.context.property_name = something
            return True

    skip_step(
        _step_obj,
        resource=_step_obj.context.name,
        message='Skipping the step since {} type does not have {} property.'.
        format(_step_obj.context.type, something))
예제 #10
0
    def __call__(self, parser, namespace, values, option_string=None):
        # Check if the given path is a file
        if not os.path.isfile(values):
            print('ERROR: {} is not a file.'.format(values))
            sys.exit(1)

        # Check if the given file is a native terraform plan file
        given_file = filetype.guess(values)

        if given_file is not None:
            terraform_executable = getattr(namespace, 'terraform_file', None)
            values = convert_terraform_plan_to_json(os.path.abspath(values),
                                                    terraform_executable)

        # Check if the given file is a json file.
        try:
            plan_lines = [line for line in open(values, 'r', encoding='utf-8')]

            # Some Github Actions (hashicorp/setup-terraform) has internal wrappers which is
            # breaking the json file that is read by the terraform-compliance
            file_change_required = False
            if len(plan_lines) > 1:
                plan_lines = plan_lines[1]
                file_change_required = True
            else:
                plan_lines = plan_lines[0]

            data = json.loads(plan_lines)

            # Write the changed plan file to the same file, since it is used in other places.
            if file_change_required:
                with open(values, 'w', encoding='utf-8') as plan_file:
                    plan_file.write(plan_lines)

        except json.JSONDecodeError:
            print('ERROR: {} is not a valid JSON file'.format(values))
            sys.exit(1)
        except UnicodeDecodeError:
            print('ERROR: {} is not a valid JSON file.'.format(values))
            print(
                '       Did you try to convert the binary plan file to json with '
                '"terraform show -json {} > {}.json" ?'.format(values, values))
            sys.exit(1)
        except:
            raise TerraformComplianceInternalFailure('Invalid file type.')

        # Check if this is a correct terraform plan file
        try:
            assert data['format_version']
            assert data['terraform_version']

            # Check if this is a state file
            if 'values' in data:
                assert data['values']['root_module'].get(
                    'resources',
                    data['values']['root_module'].get('child_modules'))

            # Then it must be a terraform plan file
            else:
                assert data['planned_values']
                assert data['configuration']

        except KeyError:
            print(
                'ERROR: {} is not a valid terraform plan json output.'.format(
                    values))
            sys.exit(1)

        setattr(namespace, self.dest, os.path.abspath(values))
        return True