class ImageMetadataAttributeCheckTrigger(BaseTrigger):
    __trigger_name__ = 'ATTRIBUTECHECK'
    __description__ = 'triggers if a named image attribute matches the given condition'

    __ops__ = {
        '=':
        CheckOperation(requires_rvalue=True,
                       eval_function=lambda x, y: x == y),
        '!=':
        CheckOperation(requires_rvalue=True,
                       eval_function=lambda x, y: x != y),
        '>':
        CheckOperation(requires_rvalue=True, eval_function=lambda x, y: x > y),
        '<':
        CheckOperation(requires_rvalue=True, eval_function=lambda x, y: x < y),
        '>=':
        CheckOperation(requires_rvalue=True,
                       eval_function=lambda x, y: x >= y),
        '<=':
        CheckOperation(requires_rvalue=True,
                       eval_function=lambda x, y: x <= y),
        'exists':
        CheckOperation(requires_rvalue=False,
                       eval_function=lambda x, y: bool(x)),
        'not_exists':
        CheckOperation(requires_rvalue=False,
                       eval_function=lambda x, y: not bool(x)),
        'like':
        CheckOperation(requires_rvalue=True,
                       eval_function=lambda x, y: bool(re.match(y, x))),
        'not_like':
        CheckOperation(requires_rvalue=True,
                       eval_function=lambda x, y: not bool(re.match(y, x)))
    }

    __valid_attributes__ = {
        'size':
        lambda x: x.size,
        'architecture':
        lambda x: x.docker_data_json.get('Architecture')
        if x.docker_data_json else None,
        'os_type':
        lambda x: x.docker_data_json.get('Os') if x.docker_data_json else None,
        'distro':
        lambda x: x.distro_name,
        'distro_version':
        lambda x: x.distro_version,
        'like_distro':
        lambda x: x.like_distro,
        'layer_count':
        lambda x: len(x.layers_json) if x.layers_json else 0
    }

    __checks__ = CheckOperations(__ops__)
    __value_validator__ = lambda x: True

    #__params__ = {
    #    'ATTRIBUTES': AttributeListValidator(__valid_attributes__.keys()),
    #    'CHECK': __checks__,
    #    'CHECK_VALUE': __value_validator__
    #}
    attributes = EnumCommaDelimStringListParameter(
        name='attributes',
        description=
        'List of attribute names to apply as rvalues to the check operation',
        enum_values=__valid_attributes__)
    check = EnumStringParameter(
        name='check',
        description='The operation to perform the evaluation',
        enum_values=__ops__.keys())
    check_value = TriggerParameter(
        name='check_value',
        description='The lvalue in the check operation.',
        validator=TypeValidator('string'))

    def evaluate(self, image_obj, context):
        #attrs = delim_parser(self.eval_params.get('ATTRIBUTES', ''))
        #check = self.eval_params.get('CHECK')
        #rval = self.eval_params.get('CHECK_VALUE')
        attrs = self.attributes.value()
        check = self.check.value()
        rval = self.check_value.value()

        if not attrs or not check:
            return

        if self.__checks__.get_op(check).requires_rvalue and not rval:
            # Raise exception or fall thru
            return

        for attr in attrs:
            img_val = self.__valid_attributes__[attr](image_obj)
            # Make consistent types (specifically for int/float/str)
            if type(img_val) in [str, int, float, unicode]:
                rval = type(img_val)(rval)

            if self.__checks__.get_op(check).eval_function(img_val, rval):
                self._fire(
                    msg=
                    "Attribute check for attribute: '{}' check: '{}' check_value: '{}' matched image value: '{}'"
                    .format(attr, check, (
                        str(rval) if rval is not None else ''), img_val))
class ImageMetadataAttributeCheckTrigger(BaseTrigger):
    __trigger_name__ = 'attributecheck'
    __description__ = 'triggers if a named image attribute matches the given condition'

    __ops__ = {
        '=':
        CheckOperation(requires_rvalue=True,
                       eval_function=lambda x, y: x == y),
        '!=':
        CheckOperation(requires_rvalue=True,
                       eval_function=lambda x, y: x != y),
        '>':
        CheckOperation(requires_rvalue=True, eval_function=lambda x, y: x > y),
        '<':
        CheckOperation(requires_rvalue=True, eval_function=lambda x, y: x < y),
        '>=':
        CheckOperation(requires_rvalue=True,
                       eval_function=lambda x, y: x >= y),
        '<=':
        CheckOperation(requires_rvalue=True,
                       eval_function=lambda x, y: x <= y),
        'exists':
        CheckOperation(requires_rvalue=False,
                       eval_function=lambda x, y: bool(x)),
        'not_exists':
        CheckOperation(requires_rvalue=False,
                       eval_function=lambda x, y: not bool(x)),
        'like':
        CheckOperation(requires_rvalue=True,
                       eval_function=lambda x, y: bool(re.match(y, x))),
        'not_like':
        CheckOperation(requires_rvalue=True,
                       eval_function=lambda x, y: not bool(re.match(y, x))),
        'in':
        CheckOperation(
            requires_rvalue=True,
            eval_function=lambda x, y: x in [z.strip() for z in y.split(',')]),
        'not_in':
        CheckOperation(requires_rvalue=True,
                       eval_function=lambda x, y: x not in
                       [z.strip() for z in y.split(',')])
    }

    __valid_attributes__ = {
        'size':
        lambda x: x.size,
        'architecture':
        lambda x: x.docker_data_json.get('Architecture')
        if x.docker_data_json else None,
        'os_type':
        lambda x: x.docker_data_json.get('Os') if x.docker_data_json else None,
        'distro':
        lambda x: x.distro_name,
        'distro_version':
        lambda x: x.distro_version,
        'like_distro':
        lambda x: x.like_distro,
        'layer_count':
        lambda x: len(x.layers_json) if x.layers_json else 0
    }

    __checks__ = CheckOperations(__ops__)
    __value_validator__ = lambda x: True

    attribute = EnumStringParameter(
        name='attributes',
        description='Attribute name to apply as rvalue to the check operation',
        enum_values=__valid_attributes__.keys(),
        is_required=True,
        sort_order=1)
    check = EnumStringParameter(
        name='check',
        description='The operation to perform the evaluation',
        enum_values=__ops__.keys(),
        is_required=True,
        sort_order=2)
    check_value = TriggerParameter(
        name='check_value',
        description='The lvalue in the check operation.',
        validator=TypeValidator('string'),
        sort_order=3)

    def evaluate(self, image_obj, context):
        attr = self.attribute.value()
        check = self.check.value()
        rval = self.check_value.value()

        if not attr or not check:
            return

        if self.__checks__.get_op(check).requires_rvalue and not rval:
            # Raise exception or fall thru
            return

        img_val = self.__valid_attributes__[attr](image_obj)
        # Make consistent types (specifically for int/float/str)
        if type(img_val) in [str, int, float, unicode]:
            rval = type(img_val)(rval)

        if self.__checks__.get_op(check).eval_function(img_val, rval):
            self._fire(
                msg=
                "Attribute check for attribute: '{}' check: '{}' check_value: '{}' matched image value: '{}'"
                .format(attr, check, (
                    str(rval) if rval is not None else ''), img_val))
class InstructionCheckTrigger(ParameterizedDockerfileModeBaseTrigger):
    __trigger_name__ = "instruction"
    __description__ = "Triggers if any directives in the list are found to match the described condition in the dockerfile."

    instruction = EnumStringParameter(
        name="instruction",
        example_str="from",
        description="The Dockerfile instruction to check.",
        enum_values=DIRECTIVES,
        is_required=True,
        related_to="check",
        sort_order=1,
    )
    operator = EnumStringParameter(
        name="check",
        example_str="=",
        description="The type of check to perform.",
        enum_values=CONDITIONS,
        is_required=True,
        related_to="directive, check_value",
        sort_order=2,
    )
    compare_to = TriggerParameter(
        name="value",
        example_str="scratch",
        description="The value to check the dockerfile instruction against.",
        is_required=False,
        related_to="directive, check",
        validator=TypeValidator("string"),
        sort_order=3,
    )

    _operations_requiring_check_val = [
        "=", "!=", "like", "not_like", "in", "not_in"
    ]

    ops = {
        "=": lambda x, y: x == y,
        "!=": lambda x, y: x != y,
        "exists": lambda x, y: True,
        "not_exists": lambda x, y: False,
        "like": lambda x, y: bool(re.match(y, x)),
        "not_like": lambda x, y: not bool(re.match(y, x)),
        "in": lambda x, y: x in [z.strip() for z in y.split(",")],
        "not_in": lambda x, y: x not in [z.strip() for z in y.split(",")],
    }

    def _evaluate(self, image_obj, context):
        directive = (self.instruction.value()
                     )  # Note: change from multiple values to a single value
        condition = self.operator.value(default_if_none="")
        check_value = self.compare_to.value()
        operation = self.ops.get(condition)

        if condition in self._operations_requiring_check_val and check_value is None:
            return

        if not condition or not directive:
            return

        df = context.data.get("prepared_dockerfile")

        for directive_name, lines in [
                x for x in list(df.items()) if x[0] == directive
        ]:
            for l in lines:
                l = l[len(directive_name):].strip()
                if operation(l, check_value):
                    self._fire(
                        msg=
                        "Dockerfile directive '{}' check '{}' matched against '{}' for line '{}'"
                        .format(
                            directive_name,
                            condition,
                            check_value if check_value else "",
                            l,
                        ))

        upper_keys = set([x.upper() for x in list(df.keys())])
        if condition == "not_exists" and directive not in upper_keys:
            self._fire(
                msg=
                "Dockerfile directive '{}' not found, matching condition '{}' check"
                .format(directive, condition))
Beispiel #4
0
class RequiredPackageTrigger(BaseTrigger):
    __trigger_name__ = 'required_package'
    __description__ = 'Triggers if the specified package and optionally a specific version is not found in the image.'

    pkg_name = TriggerParameter(
        name='name',
        example_str='libssl',
        description='Name of package that must be found installed in image.',
        is_required=True,
        validator=TypeValidator('string'),
        sort_order=1)
    pkg_version = TriggerParameter(
        name='version',
        example_str='1.10.3rc3',
        description='Optional version of package for exact version match.',
        is_required=False,
        validator=TypeValidator('string'),
        sort_order=2)
    version_comparison = EnumStringParameter(
        name='version_match_type',
        example_str='exact',
        enum_values=['exact', 'minimum'],
        is_required=False,
        description=
        'The type of comparison to use for version if a version is provided.',
        sort_order=3)

    def evaluate(self, image_obj, context):
        name = self.pkg_name.value()
        version = self.pkg_version.value()
        comparison = self.version_comparison.value(default_if_none='exact')

        found = False

        # Filter is possible since the lazy='dynamic' is set on the packages relationship in Image.
        for img_pkg in image_obj.packages.filter(
                ImagePackage.name == name).all():
            if version is None:
                found = True
                break
            elif comparison == 'exact':
                if img_pkg.fullversion != version:
                    self._fire(
                        msg="Required input package (" + str(img_pkg.name) +
                        ") is present (" + str(img_pkg.fullversion) +
                        "), but not at the version specified in policy (" +
                        str(name) + ")")

                found = True
                break
            elif comparison == 'minimum':
                if img_pkg.fullversion != version:
                    # Check if version is less than param value
                    if compare_package_versions(
                            img_pkg.distro_namespace_meta.flavor, img_pkg.name,
                            img_pkg.version, img_pkg.name, version) < 0:
                        self._fire(
                            msg="Required min-version input package (" +
                            str(img_pkg.name) + ") is present (" +
                            str(img_pkg.fullversion) +
                            "), but is lower version than what is specified in policy ("
                            + str(version) + ")")

                # >=, so ok
                found = True
                break

        if not found:
            if version and comparison != 'name_only':
                self._fire(
                    msg=
                    "Required input package ({},{}) is not present in container image"
                    .format(str(name), str(version)))
            else:
                self._fire(
                    msg=
                    "Required input package ({}) is not present in container image"
                    .format(str(name)))
Beispiel #5
0
class FileAttributeMatchTrigger(BaseTrigger):
    __trigger_name__ = "attribute_match"
    __description__ = "Triggers if a filename exists in the container that has attributes that match those which are provided . This check has a performance impact on policy evaluation."

    filename = TriggerParameter(
        validator=TypeValidator("string"),
        name="filename",
        example_str="/etc/passwd",
        description="Filename to check against provided checksum.",
        is_required=True,
        sort_order=1,
    )

    checksum_algo = EnumStringParameter(
        name="checksum_algorithm",
        enum_values=["sha256"],
        example_str="sha256",
        description="Checksum algorithm",
        is_required=False,
        sort_order=2,
    )
    checksum = TriggerParameter(
        validator=TypeValidator("string"),
        name="checksum",
        example_str="832cd0f75b227d13aac82b1f70b7f90191a4186c151f9db50851d209c45ede11",
        description="Checksum of file.",
        is_required=False,
        sort_order=3,
    )

    checksum_op = EnumStringParameter(
        name="checksum_match",
        enum_values=["equals", "not_equals"],
        example_str="equals",
        description="Checksum operation to perform.",
        is_required=False,
        sort_order=4,
    )

    mode = TriggerParameter(
        validator=TypeValidator("string"),
        name="mode",
        example_str="00644",
        description="File mode of file.",
        is_required=False,
        sort_order=5,
    )
    mode_op = EnumStringParameter(
        name="mode_op",
        enum_values=["equals", "not_equals"],
        example_str="equals",
        description="File mode operation to perform.",
        is_required=False,
        sort_order=6,
    )

    skip_if_file_missing = BooleanStringParameter(
        name="skip_missing",
        example_str="true",
        description="If set to true, do not fire this trigger if the file is not present.  If set to false, fire this trigger ignoring the other parameter settings.",
        is_required=False,
        sort_order=7,
    )

    def evaluate(self, image_obj, context):
        filename = self.filename.value()

        checksum_algo = self.checksum_algo.value(default_if_none="sha256")
        checksum = self.checksum.value()
        checksum_op = self.checksum_op.value(default_if_none="equals")

        mode = self.mode.value()
        mode_op = self.mode_op.value(default_if_none="equals")

        skip_if_file_missing = self.skip_if_file_missing.value(default_if_none=True)

        filedetails = {}
        if hasattr(context, "data"):
            filedetails = context.data.get("filedetail")

        fire_params = {}
        filedetail = filedetails.get(filename, None)

        if filedetail:

            # checksum checks

            if checksum and checksum_op and checksum_algo:
                file_checksum = None
                if checksum_algo == "sha256":
                    file_checksum = filedetail.get("sha256_checksum", "")

                if checksum_op == "equals" and file_checksum == checksum:
                    fire_params[
                        "checksum"
                    ] = "checksum={} op={} specified_checksum={}".format(
                        file_checksum, checksum_op, checksum
                    )
                elif checksum_op == "not_equals" and file_checksum != checksum:
                    fire_params[
                        "checksum"
                    ] = "checksum={} op={} specified_checksum={}".format(
                        file_checksum, checksum_op, checksum
                    )
                else:
                    return

            # mode checks

            if mode and mode_op:
                file_mode = filedetail.get("mode", 0)

                file_mode_cmp = oct(stat.S_IMODE(file_mode))
                input_mode_cmp = oct(int(mode, 8))

                if mode_op == "equals" and file_mode_cmp == input_mode_cmp:
                    fire_params["mode"] = "mode={} op={} specified_mode={}".format(
                        file_mode_cmp, mode_op, input_mode_cmp
                    )
                elif mode_op == "not_equals" and file_mode_cmp != input_mode_cmp:
                    fire_params["mode"] = "mode={} op={} specified_mode={}".format(
                        file_mode_cmp, mode_op, input_mode_cmp
                    )
                else:
                    return
        else:
            # case where file doesn't exist
            if skip_if_file_missing:
                return
            fire_params["skip"] = "skip_missing=False"

        if fire_params:
            msg = "filename={}".format(filename)
            for k in fire_params.keys():
                msg += " and {}".format(fire_params[k])

            self._fire(msg=msg)
Beispiel #6
0
class FileAttributeMatchTrigger(BaseTrigger):
    __trigger_name__ = 'attribute_match'
    __description__ = 'Triggers if a filename exists in the container that has attributes that match those which are provided . This check has a performance impact on policy evaluation.'

    filename = TriggerParameter(
        validator=TypeValidator('string'),
        name='filename',
        example_str='/etc/passwd',
        description='Filename to check against provided checksum.',
        is_required=True,
        sort_order=1)

    checksum_algo = EnumStringParameter(name='checksum_algorithm',
                                        enum_values=['sha256'],
                                        example_str='sha256',
                                        description='Checksum algorithm',
                                        is_required=False,
                                        sort_order=2)
    checksum = TriggerParameter(
        validator=TypeValidator('string'),
        name='checksum',
        example_str=
        '832cd0f75b227d13aac82b1f70b7f90191a4186c151f9db50851d209c45ede11',
        description='Checksum of file.',
        is_required=False,
        sort_order=3)

    checksum_op = EnumStringParameter(
        name='checksum_match',
        enum_values=['equals', 'not_equals'],
        example_str='equals',
        description='Checksum operation to perform.',
        is_required=False,
        sort_order=4)

    mode = TriggerParameter(validator=TypeValidator('string'),
                            name='mode',
                            example_str='00644',
                            description='File mode of file.',
                            is_required=False,
                            sort_order=5)
    mode_op = EnumStringParameter(
        name='mode_op',
        enum_values=['equals', 'not_equals'],
        example_str='equals',
        description='File mode operation to perform.',
        is_required=False,
        sort_order=6)

    skip_if_file_missing = BooleanStringParameter(
        name='skip_missing',
        example_str='true',
        description=
        'If set to true, do not fire this trigger if the file is not present.  If set to false, fire this trigger ignoring the other parameter settings.',
        is_required=False,
        sort_order=7)

    def evaluate(self, image_obj, context):
        filename = self.filename.value()

        checksum_algo = self.checksum_algo.value(default_if_none='sha256')
        checksum = self.checksum.value()
        checksum_op = self.checksum_op.value(default_if_none='equals')

        mode = self.mode.value()
        mode_op = self.mode_op.value(default_if_none='equals')

        skip_if_file_missing = self.skip_if_file_missing.value(
            default_if_none=True)

        files = []
        if hasattr(context, 'data'):
            filedetails = context.data.get('filedetail')

        fire_params = {}
        filedetail = filedetails.get(filename, None)

        if filedetail:

            # checksum checks

            if checksum and checksum_op and checksum_algo:
                file_checksum = None
                if checksum_algo == 'sha256':
                    file_checksum = filedetail.get('sha256_checksum', "")

                if checksum_op == 'equals' and file_checksum == checksum:
                    fire_params[
                        'checksum'] = "checksum={} op={} specified_checksum={}".format(
                            file_checksum, checksum_op, checksum)
                elif checksum_op == 'not_equals' and file_checksum != checksum:
                    fire_params[
                        'checksum'] = "checksum={} op={} specified_checksum={}".format(
                            file_checksum, checksum_op, checksum)
                else:
                    return

            # mode checks

            if mode and mode_op:
                file_mode = filedetail.get('mode', 0)

                file_mode_cmp = oct(stat.S_IMODE(file_mode))
                input_mode_cmp = oct(int(mode, 8))

                if mode_op == 'equals' and file_mode_cmp == input_mode_cmp:
                    fire_params[
                        'mode'] = "mode={} op={} specified_mode={}".format(
                            file_mode_cmp, mode_op, input_mode_cmp)
                elif mode_op == 'not_equals' and file_mode_cmp != input_mode_cmp:
                    fire_params[
                        'mode'] = "mode={} op={} specified_mode={}".format(
                            file_mode_cmp, mode_op, input_mode_cmp)
                else:
                    return
        else:
            # case where file doesn't exist
            if skip_if_file_missing:
                return
            fire_params['skip'] = "skip_missing=False"

        if fire_params:
            msg = "filename={}".format(filename)
            for k in fire_params.keys():
                msg += " and {}".format(fire_params[k])

            self._fire(msg=msg)
Beispiel #7
0
class DirectiveCheckTrigger(BaseTrigger):
    __trigger_name__ = 'DIRECTIVECHECK'
    __description__ = 'Triggers if any directives in the list are found to match the described condition in the dockerfile'

    directives = EnumCommaDelimStringListParameter(
        name='directives',
        description='The Dockerfile instruction to check',
        enum_values=DIRECTIVES,
        is_required=True,
        related_to='check')
    check = EnumStringParameter(name='check',
                                description='The type of check to perform',
                                enum_values=CONDITIONS,
                                is_required=True,
                                related_to='directive, check_value')
    check_value = TriggerParameter(
        name='check_value',
        description='The value to check the dockerfile instruction against',
        is_required=False,
        related_to='directive, check',
        validator=TypeValidator("string"))

    _conditions_requiring_check_val = ['=', '!=', 'like', 'not_like']

    ops = {
        '=': lambda x, y: x == y,
        '!=': lambda x, y: x != y,
        'exists': lambda x, y: True,
        'not_exists': lambda x, y: False,
        'like': lambda x, y: bool(re.match(y, x)),
        'not_like': lambda x, y: not bool(re.match(y, x))
    }

    def evaluate(self, image_obj, context):
        if not context.data.get('prepared_dockerfile'):
            return  # Prep step blocked this eval due to condition on the dockerfile, so skip

        directives = set(
            self.directives.value(default_if_none=[])
        )  # Note: change from multiple values to a single value
        condition = self.check.value(default_if_none='')
        check_value = self.check_value.value(default_if_none=[])
        operation = self.ops.get(condition)

        if not condition or not directives:
            return

        df = context.data.get('prepared_dockerfile')

        for directive, lines in filter(lambda x: x[0] in directives,
                                       df.items()):
            for l in lines:
                l = l[len(directive):].strip()
                if operation(l, check_value):
                    self._fire(
                        msg=
                        "Dockerfile directive '{}' check '{}' matched against '{}' for line '{}'"
                        .format(directive, condition,
                                check_value if check_value else '', l))

        if condition == 'not_exists':
            for match in directives.difference(
                    directives.intersection(
                        set(map(lambda x: x.upper(), df.keys())))):
                self._fire(
                    msg=
                    "Dockerfile directive '{}' not found, matching condition '{}' check"
                    .format(match, condition))
class ImageMetadataAttributeCheckTrigger(BaseTrigger):
    __trigger_name__ = 'attribute'
    __description__ = 'Triggers if a named image metadata value matches the given condition.'

    __ops__ = {
        '=': CheckOperation(requires_rvalue=True, eval_function=lambda x, y: x == y),
        '!=': CheckOperation(requires_rvalue=True, eval_function=lambda x, y: x != y),
        '>': CheckOperation(requires_rvalue=True, eval_function=lambda x, y: x > y),
        '<': CheckOperation(requires_rvalue=True, eval_function=lambda x, y: x < y),
        '>=': CheckOperation(requires_rvalue=True, eval_function=lambda x, y: x >= y),
        '<=': CheckOperation(requires_rvalue=True, eval_function=lambda x, y: x <= y),
        'exists': CheckOperation(requires_rvalue=False, eval_function=lambda x, y: bool(x)),
        'not_exists': CheckOperation(requires_rvalue=False, eval_function=lambda x, y: not bool(x)),
        'like': CheckOperation(requires_rvalue=True, eval_function=lambda x, y: bool(re.match(y, x))),
        'not_like': CheckOperation(requires_rvalue=True, eval_function=lambda x, y: not bool(re.match(y, x))),
        'in': CheckOperation(requires_rvalue=True, eval_function=lambda x, y: x in [z.strip() for z in y.split(',')]),
        'not_in': CheckOperation(requires_rvalue=True, eval_function=lambda x, y: x not in [z.strip() for z in y.split(',')])
    }

    __valid_attributes__ = {
        'size': (lambda x: x.size, RegexParamValidator(regex=BYTES_REGEX.pattern)),
        'architecture': (lambda x: x.docker_data_json.get('Architecture') if x.docker_data_json else None, TypeValidator('string')),
        'os_type': (lambda x: x.docker_data_json.get('Os', x.docker_data_json.get('os')) if x.docker_data_json else None, TypeValidator('string')),
        'distro': (lambda x: x.distro_name, TypeValidator('string')),
        'distro_version': (lambda x: x.distro_version, TypeValidator('string')),
        'like_distro': (lambda x: x.like_distro, TypeValidator('string')),
        'layer_count': (lambda x: len(x.layers_json) if x.layers_json else 0, IntegerValidator())
    }

    attribute = EnumStringParameter(name='attribute', example_str='size', description='Attribute name to be checked.', enum_values=list(__valid_attributes__.keys()), is_required=True, sort_order=1)
    check = EnumStringParameter(name='check', example_str='>', description='The operation to perform the evaluation.', enum_values=list(__ops__.keys()), is_required=True, sort_order=2)

    _check_value_validator = LinkedValidator(discriminator_parameter='attribute', default_validator=TypeValidator('string'), value_map={k: v[1] for k, v in __valid_attributes__.items()})
    check_value = TriggerParameter(name='value',
                                   example_str='1073741824',
                                   description='Value used in comparison.',
                                   validator=_check_value_validator, is_required=False, sort_order=3)

    def evaluate(self, image_obj, context):
        attr = self.attribute.value()
        check = self.check.value()
        rval = self.check_value.value()

        if not attr or not check:
            return

        op = self.__ops__.get(check)
        if op is None or op.requires_rvalue and not rval:
            # Raise exception or fall thru
            return

        img_val = self.__valid_attributes__[attr][0](image_obj)
        # Make consistent types (specifically for int/float/str)
        if type(img_val) in [int, float, str]:
            if attr == 'size':
                rval = convert_bytes_size(rval)
            else:
                rval = type(img_val)(rval)

        if op.eval_function(img_val, rval):
            self._fire(msg="Attribute check for attribute: '{}' check: '{}' check_value: '{}' matched image value: '{}'".format(attr, check, (str(rval) if rval is not None else ''), img_val))
Beispiel #9
0
class SecretContentChecksTrigger(BaseTrigger):
    __trigger_name__ = 'content_regex_checks'
    __description__ = 'Triggers if the secret content search analyzer has found any matches with the configured and named regexes. Checks can be configured to trigger if a match is found or is not found (selected using match_type parameter).  Matches are filtered by the content_regex_name and filename_regex if they are set. The content_regex_name shoud be a value from the "secret_search" section of the analyzer_config.yaml.'

    secret_contentregexp = TriggerParameter(
        name='content_regex_name',
        validator=TypeValidator('string'),
        example_str=default_included_regex_names[0],
        description=
        'Name of content regexps configured in the analyzer that match if found in the image, instead of matching all. Names available by default are: {}.'
        .format(default_included_regex_names),
        sort_order=1)
    name_regexps = TriggerParameter(
        name='filename_regex',
        validator=TypeValidator('string'),
        example_str='/etc/.*',
        description='Regexp to filter the content matched files by.',
        sort_order=2)
    match_type = EnumStringParameter(
        name='match_type',
        enum_values=['notfound', 'found'],
        example_str='found',
        description=
        'Set to define the type of match - trigger if match is found (default) or not found.',
        is_required=False,
        sort_order=3)

    def evaluate(self, image_obj, context):
        match_filter = self.secret_contentregexp.value(default_if_none=[])
        name_filter = self.name_regexps.value()
        name_re = re.compile(
            name_filter) if self.name_regexps.value() else None
        match_type = self.match_type.value(default_if_none="found")

        if match_filter:
            matches = [base64.b64encode(ensure_bytes(x)) for x in match_filter]
            matches_decoded = match_filter
        else:
            matches = []
            matches_decoded = []

        found = False
        for thefile, regexps in list(
                context.data.get('secret_content_regexp', {}).items()):
            thefile = ensure_str(thefile)

            if not regexps:
                continue

            if regexps and (not name_re or name_re.match(thefile)):
                for regexp in list(regexps.keys()):
                    decoded_regexp = ensure_str(
                        base64.b64decode(ensure_bytes(regexp)))

                    try:
                        regexp_name, theregexp = decoded_regexp.split("=", 1)
                    except:
                        regexp_name = None
                        theregexp = decoded_regexp

                    if not matches:
                        found = True
                    elif regexp in matches or theregexp in matches_decoded:
                        found = True
                    elif regexp_name and regexp_name in matches_decoded:
                        found = True
                    if found and match_type == 'found':
                        self._fire(
                            msg=
                            'Secret content search analyzer found regexp match in container: file={} regexp={}'
                            .format(thefile, decoded_regexp))

        if not found and match_type == 'notfound':
            f_filter = name_filter
            if not f_filter:
                f_filter = '*'

            m_filter = match_filter
            if not m_filter:
                m_filter = 'all'
            self._fire(
                msg=
                'Secret content search analyzer did not find regexp match in container: filename_regex={} content_regex_name={}'
                .format(f_filter, m_filter))
Beispiel #10
0
class DirectiveCheckTrigger(BaseTrigger):
    __lifecycle_state__ = LifecycleStates.deprecated
    __trigger_name__ = 'directivecheck'
    __description__ = 'Triggers if any directives in the list are found to match the described condition in the dockerfile'

    directive = EnumStringParameter(
        name='directives',
        example_str='COPY',
        description='The Dockerfile instruction to check',
        enum_values=DIRECTIVES,
        is_required=True,
        related_to='check',
        sort_order=1)
    check = EnumStringParameter(name='check',
                                example_str='=',
                                description='The type of check to perform',
                                enum_values=CONDITIONS,
                                is_required=True,
                                related_to='directive, check_value',
                                sort_order=2)
    check_value = TriggerParameter(
        name='check_value',
        example_str='./app /app',
        description='The value to check the dockerfile instruction against',
        is_required=False,
        related_to='directive, check',
        validator=TypeValidator("string"),
        sort_order=3)

    _conditions_requiring_check_val = [
        '=', '!=', 'like', 'not_like', 'in', 'not_in'
    ]

    ops = {
        '=': lambda x, y: x == y,
        '!=': lambda x, y: x != y,
        'exists': lambda x, y: True,
        'not_exists': lambda x, y: False,
        'like': lambda x, y: bool(re.match(y, x)),
        'not_like': lambda x, y: not bool(re.match(y, x)),
        'in': lambda x, y: x in [z.strip() for z in y.split(',')],
        'not_in': lambda x, y: x not in [z.strip() for z in y.split(',')]
    }

    def evaluate(self, image_obj, context):
        if not context.data.get('prepared_dockerfile'):
            return  # Prep step blocked this eval due to condition on the dockerfile, so skip

        directive = self.directive.value(
        )  # Note: change from multiple values to a single value
        condition = self.check.value(default_if_none='')
        check_value = self.check_value.value(default_if_none=[])
        operation = self.ops.get(condition)

        if not condition or not directive:
            return

        df = context.data.get('prepared_dockerfile')

        for directive_name, lines in filter(lambda x: x[0] == directive,
                                            df.items()):
            for l in lines:
                l = l[len(directive_name):].strip()
                if operation(l, check_value):
                    self._fire(
                        msg=
                        "Dockerfile directive '{}' check '{}' matched against '{}' for line '{}'"
                        .format(directive_name, condition,
                                check_value if check_value else '', l))

        upper_keys = set(map(lambda x: x.upper(), df.keys()))
        if condition == 'not_exists' and directive not in upper_keys:
            self._fire(
                msg=
                "Dockerfile directive '{}' not found, matching condition '{}' check"
                .format(directive, condition))
Beispiel #11
0
class InstructionCheckTrigger(ParameterizedDockerfileModeBaseTrigger):
    __trigger_name__ = 'instruction'
    __description__ = 'Triggers if any directives in the list are found to match the described condition in the dockerfile.'

    instruction = EnumStringParameter(
        name='instruction',
        example_str='from',
        description='The Dockerfile instruction to check.',
        enum_values=DIRECTIVES,
        is_required=True,
        related_to='check',
        sort_order=1)
    operator = EnumStringParameter(name='check',
                                   example_str='=',
                                   description='The type of check to perform.',
                                   enum_values=CONDITIONS,
                                   is_required=True,
                                   related_to='directive, check_value',
                                   sort_order=2)
    compare_to = TriggerParameter(
        name='value',
        example_str='scratch',
        description='The value to check the dockerfile instruction against.',
        is_required=False,
        related_to='directive, check',
        validator=TypeValidator("string"),
        sort_order=3)

    _operations_requiring_check_val = [
        '=', '!=', 'like', 'not_like', 'in', 'not_in'
    ]

    ops = {
        '=': lambda x, y: x == y,
        '!=': lambda x, y: x != y,
        'exists': lambda x, y: True,
        'not_exists': lambda x, y: False,
        'like': lambda x, y: bool(re.match(y, x)),
        'not_like': lambda x, y: not bool(re.match(y, x)),
        'in': lambda x, y: x in [z.strip() for z in y.split(',')],
        'not_in': lambda x, y: x not in [z.strip() for z in y.split(',')]
    }

    def _evaluate(self, image_obj, context):
        directive = self.instruction.value(
        )  # Note: change from multiple values to a single value
        condition = self.operator.value(default_if_none='')
        check_value = self.compare_to.value()
        operation = self.ops.get(condition)

        if condition in self._operations_requiring_check_val and check_value is None:
            return

        if not condition or not directive:
            return

        df = context.data.get('prepared_dockerfile')

        for directive_name, lines in [
                x for x in list(df.items()) if x[0] == directive
        ]:
            for l in lines:
                l = l[len(directive_name):].strip()
                if operation(l, check_value):
                    self._fire(
                        msg=
                        "Dockerfile directive '{}' check '{}' matched against '{}' for line '{}'"
                        .format(directive_name, condition,
                                check_value if check_value else '', l))

        upper_keys = set([x.upper() for x in list(df.keys())])
        if condition == 'not_exists' and directive not in upper_keys:
            self._fire(
                msg=
                "Dockerfile directive '{}' not found, matching condition '{}' check"
                .format(directive, condition))