class PkgMatchTrigger(BaseTrigger): __trigger_name__ = 'blacklisted_name_version' __description__ = 'Triggers if the evaluated image has an NPM package installed that matches the name and optionally a version specified in the parameters.' name = TriggerParameter(validator=TypeValidator('string'), name='name', is_required=True, description='Npm package name to blacklist.', example_str='time_diff', sort_order=1) version = TriggerParameter(validator=TypeValidator('string'), name='version', is_required=False, description='Npm package version to blacklist specifically.', example_str='0.2.9', sort_order=2) def evaluate(self, image_obj, context): """ Fire for any npm that is on the blacklist with a full name + version match :param image_obj: :param context: :return: """ npms = image_obj.npms if not npms: return pkgs = context.data.get(NPM_LISTING_KEY) if not pkgs: return name = self.name.value() version = self.version.value(default_if_none=None) if name in pkgs: pkg_versions = pkgs.get(name) if not pkg_versions: pkg_versions = [] if version and version in pkg_versions: self._fire(msg='NPM Package is blacklisted: ' + name + "-" + version) elif version is None: self._fire(msg='NPM Package is blacklisted: ' + name)
class BlacklistedGemTrigger(BaseTrigger): __trigger_name__ = 'blacklist' __description__ = 'Triggers if the evaluated image has a GEM package installed that matches the specified name and version.' name = TriggerParameter(validator=TypeValidator('string'), name='name', is_required=True, description='Gem name to blacklist.', example_str='time_diff', sort_order=1) version = TriggerParameter(validator=TypeValidator('string'), name='version', is_required=False, description='Optional version to blacklist specifically.', example_str='0.2.9', sort_order=2) def evaluate(self, image_obj, context): """ Fire for any gem that is on the blacklist with a full name + version match :param image_obj: :param context: :return: """ gems = image_obj.get_packages_by_type('gem') if not gems: return pkgs = context.data.get(GEM_LIST_KEY) if not pkgs: return name = self.name.value() version = self.version.value(default_if_none=None) if name in pkgs: if version and version in pkgs.get(name, []): self._fire(msg='Gem Package is blacklisted: ' + name + "-" + version) elif version is None: self._fire(msg='Gem Package is blacklisted: ' + name)
class SecretContentChecksTrigger(BaseTrigger): __trigger_name__ = 'content_regex_checks' __description__ = 'Triggers if the content search analyzer has found any matches with the configured and named regexes. 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 should trigger if found in the image, instead of triggering for any match. Names available by default are: {}.' .format(default_included_regex_names)) name_regexps = TriggerParameter( name='filename_regex', validator=TypeValidator('string'), example_str='/etc/.*', description='Regexp to filter the content matched files by.') def evaluate(self, image_obj, context): match_filter = self.secret_contentregexp.value(default_if_none=[]) name_re = re.compile( self.name_regexps.value()) if self.name_regexps.value() else None if match_filter: matches = [x.encode('base64') for x in match_filter] matches_decoded = match_filter else: matches = [] matches_decoded = [] for thefile, regexps in context.data.get('secret_content_regexp', {}).items(): thefile = thefile.encode('ascii', errors='replace') if not regexps: continue if regexps and (not name_re or name_re.match(thefile)): for regexp in regexps.keys(): try: regexp_name, theregexp = regexp.decode('base64').split( "=", 1) except: regexp_name = None theregexp = regexp.decode('base64') if not matches: self._fire( msg= 'Secret search analyzer found regexp match in container: file={} regexp={}' .format(thefile, regexp.decode('base64'))) elif regexp in matches or theregexp in matches_decoded: self._fire( msg= 'Secret search analyzer found regexp match in container: file={} regexp={}' .format(thefile, regexp.decode('base64'))) elif regexp_name and regexp_name in matches_decoded: self._fire( msg= 'Secret search analyzer found regexp match in container: file={} regexp={}' .format(thefile, regexp.decode('base64')))
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 = [] onefound=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()): found=False 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=onefound=True elif regexp in matches or theregexp in matches_decoded: found=onefound=True elif regexp_name and regexp_name in matches_decoded: found=onefound=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 onefound 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))
class PkgMatchTrigger(BaseTrigger): __trigger_name__ = "blacklisted_name_version" __description__ = "Triggers if the evaluated image has an NPM package installed that matches the name and optionally a version specified in the parameters." name = TriggerParameter( validator=TypeValidator("string"), name="name", is_required=True, description="Npm package name to blacklist.", example_str="time_diff", sort_order=1, ) version = TriggerParameter( validator=TypeValidator("string"), name="version", is_required=False, description="Npm package version to blacklist specifically.", example_str="0.2.9", sort_order=2, ) def evaluate(self, image_obj, context): """ Fire for any npm that is on the blacklist with a full name + version match :param image_obj: :param context: :return: """ npms = image_obj.get_packages_by_type("npm") if not npms: return pkgs = context.data.get(NPM_LISTING_KEY) if not pkgs: return name = self.name.value() version = self.version.value(default_if_none=None) if name in pkgs: pkg_versions = pkgs.get(name) if not pkg_versions: pkg_versions = [] if version and version in pkg_versions: self._fire(msg="NPM Package is blacklisted: " + name + "-" + version) elif version is None: self._fire(msg="NPM Package is blacklisted: " + name)
class ContentMatchTrigger(BaseTrigger): __trigger_name__ = 'content_regex_match' __description__ = 'Triggers for each file where the content search analyzer has found a match using configured regexes in the analyzer_config.yaml "content_search" section. If the parameter is set, the trigger will only fire for files that matched the named regex. Refer to your analyzer_config.yaml for the regex values.' regex_name = TriggerParameter(validator=TypeValidator('string'), name='regex_name', example_str='.*password.*', description='Regex string that also appears in the FILECHECK_CONTENTMATCH analyzer parameter in analyzer configuration, to limit the check to. If set, will only fire trigger when the specific named regex was found in a file.', is_required=False) def evaluate(self, image_obj, context): match_filter = self.regex_name.value() if match_filter: match_decoded = ensure_str(base64.b64encode(ensure_bytes(match_filter))) else: return for thefile, regexps in list(context.data.get('content_regexp', {}).items()): thefile = ensure_str(thefile) if not regexps: continue for regexp in 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 match_filter: self._fire(msg='File content analyzer found regexp match in container: file={} regexp={}'.format(thefile, decoded_regexp)) elif regexp == match_filter or theregexp == match_decoded: self._fire(msg='File content analyzer found regexp match in container: file={} regexp={}'.format(thefile, decoded_regexp)) elif regexp_name and regexp_name == match_decoded: self._fire(msg='File content analyzer found regexp match in container: file={} regexp={}'.format(thefile, decoded_regexp))
class FilenameMatchTrigger(BaseTrigger): __trigger_name__ = "name_match" __description__ = "Triggers if a file exists in the container that has a filename that matches the provided regex. This does have a performance impact on policy evaluation." regex = TriggerParameter( validator=TypeValidator("string"), name="regex", example_str=".*\.pem", description="Regex to apply to file names for match.", is_required=True, ) def evaluate(self, image_obj, context): # decode the param regexes from b64 regex_param = self.regex.value() files = [] if hasattr(context, "data"): files = context.data.get("filenames") for thefile in files: thefile = ensure_str(thefile) if re.match(regex_param, thefile): self._fire( msg="Application of regex matched file found in container: file={} regexp={}".format( thefile, regex_param ) )
class PEntryMatchTrigger(BaseTrigger, PentryBlacklistMixin): __trigger_name__ = 'blacklist_full_entry' __description__ = 'Triggers if entire specified passwd entry is found in the /etc/passwd file.' pentry_blacklist = TriggerParameter( name='entry', example_str='ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin', description='Full entry to match in /etc/passwd.', validator=TypeValidator('string'), is_required=True) def evaluate(self, image_obj, context): if not context.data.get('passwd_entries'): return user_entries = context.data.get('passwd_entries') blacklisted = [self.pentry_blacklist.value().strip()] for pentry, pentry in self.exec_blacklist(blacklisted, None, user_entries): self._fire( msg= "Blacklisted pentry '{}' found in image's /etc/passwd: pentry={}" .format(pentry, str(pentry))) return
class FilenameMatchTrigger(BaseTrigger): __trigger_name__ = 'name_match' __description__ = 'Triggers if a file exists in the container that has a filename that matches the provided regex. This does have a performance impact on policy evaluation.' regex = TriggerParameter( validator=TypeValidator('string'), name='regex', example_str='.*\.pem', description='Regex to apply to file names for match.', is_required=True) def evaluate(self, image_obj, context): # decode the param regexes from b64 regex_param = self.regex.value() files = [] if hasattr(context, 'data'): files = context.data.get('filenames') for thefile in files: thefile = ensure_str(thefile) if re.match(regex_param, thefile): self._fire( msg= 'Application of regex matched file found in container: file={} regexp={}' .format(thefile, regex_param))
def test_object(self): matrix = [('blah', False), (1, False), (['a'], False), ({}, True), ({ 'a': 'b' }, True)] self.run_matrix_test(value_matrix=matrix, validator=TypeValidator('object'))
def test_string(self): matrix = [('blah', True), ('', True), (1, False), (['a'], False), ({}, False), ({ 'a': 'b' }, False)] self.run_matrix_test(value_matrix=matrix, validator=TypeValidator('string'))
def test_array(self): matrix = [('blah', False), (1, False), (['a'], True), ([], True), ({ 'a': 'b' }, False), ('null', False)] self.run_matrix_test(value_matrix=matrix, validator=TypeValidator('array'))
def test_number(self): matrix = [('blah', False), (1, True), (1.0, True), (['a'], False), ({}, False), ({ 'a': 'b' }, False)] self.run_matrix_test(value_matrix=matrix, validator=TypeValidator('number'))
def test_boolean(self): matrix = [(True, True), (False, True), ('true', False), ('True', False), ('false', False), ('False', False), ('abc', False), (1, False), (['a'], False), ({ 'a': 'b' }, False)] self.run_matrix_test(value_matrix=matrix, validator=TypeValidator("boolean"))
class EffectiveUserTrigger(DockerfileModeCheckedBaseTrigger): __trigger_name__ = "effective_user" __description__ = "Checks if the effective user matches the provided user names, either as a whitelist or blacklist depending on the type parameter setting." user = CommaDelimitedStringListParameter( name="users", example_str="root,docker", description= "User names to check against as the effective user (last user entry) in the images history.", is_required=True, validator=TypeValidator("string"), sort_order=1, ) allowed_type = EnumStringParameter( name="type", enum_values=["whitelist", "blacklist"], example_str="blacklist", description="How to treat the provided user names.", is_required=True, sort_order=2, ) _sanitize_regex = "\s*USER\s+\[?([^\]]+)\]?\s*" def _evaluate(self, image_obj, context): rule_users = self.user.value() is_allowed = self.allowed_type.value().lower() == "whitelist" user_lines = context.data.get("prepared_dockerfile").get("USER", []) # If not overt, make it so if not user_lines: user_lines = ["USER root"] user = user_lines[-1].strip( ) # The last USER line is the determining entry match = re.search(self._sanitize_regex, user) if match and match.groups(): user = match.groups()[0] else: logger.warn( "Found USER line in dockerfile that does not match expected regex: {}, Line: {}" .format(self._sanitize_regex, user)) return if is_allowed and user not in rule_users: self._fire( msg= "User {} found as effective user, which is not on the allowed list" .format(user)) elif not is_allowed and user in rule_users: self._fire( msg= "User {} found as effective user, which is explicity not allowed" .format(user))
class EffectiveUserTrigger(DockerfileModeCheckedBaseTrigger): __trigger_name__ = 'effective_user' __description__ = 'Checks if the effective user matches the provided user names and fires based on the allowed parameter. If allowed=true, the rule behaves as a whitelist, otherwise acts as a blacklist.' user = CommaDelimitedStringListParameter( name='users', example_str='root,docker', description= 'User names to check against as the effective user (last user entry) in the images history.', is_required=True, validator=TypeValidator('string'), sort_order=1) allowed_type = EnumStringParameter( name='type', enum_values=['whitelist', 'blacklist'], description='How to treat the provided user names.', is_required=True, sort_order=2) _sanitize_regex = '\s*USER\s+\[?([^\]]+)\]?\s*' def _evaluate(self, image_obj, context): rule_users = self.user.value() is_allowed = self.allowed_type.value().lower() == 'whitelist' user_lines = context.data.get('prepared_dockerfile').get('USER', []) # If not overt, make it so if not user_lines: user_lines = ['USER root'] user = user_lines[-1].strip( ) # The last USER line is the determining entry match = re.search(self._sanitize_regex, user) if match and match.groups(): user = match.groups()[0] else: log.warn( 'Found USER line in dockerfile that does not match expected regex: {}, Line: {}' .format(self._sanitize_regex, user)) return if is_allowed and user not in rule_users: self._fire( msg= 'User {} found as effective user, which is not on the allowed list' .format(user)) elif not is_allowed and user in rule_users: self._fire( msg= 'User {} found as effective user, which is explicity not allowed list' .format(user))
def test_object(self): matrix = [ ("blah", False), (1, False), (["a"], False), ({}, True), ({ "a": "b" }, True), ] self.run_matrix_test(value_matrix=matrix, validator=TypeValidator("object"))
class BlackListTrigger(BaseTrigger): __trigger_name__ = "blacklist" __description__ = "Triggers if the evaluated image has a package installed that matches the named package optionally with a specific version as well." pkg_name = TriggerParameter( name="name", example_str="openssh-server", description="Package name to blacklist.", sort_order=1, validator=TypeValidator("string"), is_required=True, ) pkg_version = TriggerParameter( name="version", example_str="1.0.1", description="Specific version of package to blacklist.", validator=TypeValidator("string"), sort_order=2, is_required=False, ) def evaluate(self, image_obj, context): pkg = self.pkg_name.value() vers = self.pkg_version.value() try: if vers: matches = image_obj.packages.filter( ImagePackage.name == pkg, ImagePackage.version == vers) for m in matches: self._fire(msg="Package is blacklisted: " + m.name + "-" + m.version) else: matches = image_obj.packages.filter(ImagePackage.name == pkg) for m in matches: self._fire(msg="Package is blacklisted: " + m.name) except Exception as e: logger.exception("Error filtering packages for full match") pass
class FakeTrigger(gate.BaseTrigger): __trigger_name__ = 'TestingTrigger' __description__ = 'Not real' __trigger_id__ = 'Blah123' param1 = params.TriggerParameter(name='param_test', example_str='somevalue', description='Test parameter', validator=TypeValidator("string"), is_required=False) def test1(self): print((type(self.param1)))
def test_array(self): matrix = [ ("blah", False), (1, False), (["a"], True), ([], True), ({ "a": "b" }, False), ("null", False), ] self.run_matrix_test(value_matrix=matrix, validator=TypeValidator("array"))
def test_number(self): matrix = [ ("blah", False), (1, True), (1.0, True), (["a"], False), ({}, False), ({ "a": "b" }, False), ] self.run_matrix_test(value_matrix=matrix, validator=TypeValidator("number"))
def test_string(self): matrix = [ ("blah", True), ("", True), (1, False), (["a"], False), ({}, False), ({ "a": "b" }, False), ] self.run_matrix_test(value_matrix=matrix, validator=TypeValidator("string"))
class BlackListTrigger(BaseTrigger): __trigger_name__ = 'blacklist' __description__ = 'Triggers if the evaluated image has a package installed that matches the named package optionally with a specific version as well.' pkg_name = TriggerParameter(name='name', example_str='openssh-server', description="Package name to blacklist.", sort_order=1, validator=TypeValidator('string'), is_required=True) pkg_version = TriggerParameter( name='version', example_str='1.0.1', description='Specific version of package to blacklist.', validator=TypeValidator('string'), sort_order=2, is_required=False) def evaluate(self, image_obj, context): pkg = self.pkg_name.value() vers = self.pkg_version.value() try: if vers: matches = image_obj.packages.filter( ImagePackage.name == pkg, ImagePackage.version == vers) for m in matches: self._fire(msg='Package is blacklisted: ' + m.name + "-" + m.version) else: matches = image_obj.packages.filter(ImagePackage.name == pkg) for m in matches: self._fire(msg='Package is blacklisted: ' + m.name) except Exception as e: logger.exception('Error filtering packages for full match') pass
class FakeTrigger(gate.BaseTrigger): __trigger_name__ = "TestingTrigger" __description__ = "Not real" __trigger_id__ = "Blah123" param1 = params.TriggerParameter( name="param_test", example_str="somevalue", description="Test parameter", validator=TypeValidator("string"), is_required=False, ) def test1(self): print((type(self.param1)))
def test_linked(self): p1 = params.EnumStringParameter( name="attribute", description="Testing123", enum_values=["a", "b"], is_required=True, ) p2 = params.SimpleStringParameter( name="downstream", validator=LinkedValidator( discriminator_parameter="attribute", default_validator=TypeValidator("string"), value_map={ "a": BooleanStringValidator(), "b": IntegerValidator() }, ), description="test123", ) print(p2.validator.validation_criteria()) # p1.set_value('a') p2.validator.inject_discriminator(None) test_matrix = [ ("true", "true"), ("blah", "blah"), # p1 not set, so uses default ] self.run_matrix_test(test_matrix, p2) p1._param_value = None p2._param_value = None p2.validator.inject_discriminator("a") p1.set_value("a") test_matrix = [ ("true", "true"), ("blah", False), # should fail now that p1 has a value ] self.run_matrix_test(test_matrix, p2) p1._param_value = None p2._param_value = None p1.set_value("b") p2.validator.inject_discriminator("b") test_matrix = [("true", False), ("blah", False), ("123", "123")] self.run_matrix_test(test_matrix, p2)
def test_boolean(self): matrix = [ (True, True), (False, True), ("true", False), ("True", False), ("false", False), ("False", False), ("abc", False), (1, False), (["a"], False), ({ "a": "b" }, False), ] self.run_matrix_test(value_matrix=matrix, validator=TypeValidator("boolean"))
def test_param_basics(self): p = params.TriggerParameter( 'TestParam1', description='Param for testing basic strings', validator=TypeValidator("string"), related_to='ThisOtherParam') print('Trying string that should pass validation') # Should pass validation print((p.set_value('somestring'))) print(('Got value: {}'.format(p.value()))) print('Trying an int that should fail validation') # Should fail validation with self.assertRaises(ValidationError) as ex: print((p.set_value(10))) print(('Correctly got exception {}'.format(ex.exception)))
def test_param_basics(self): p = params.TriggerParameter( "TestParam1", description="Param for testing basic strings", validator=TypeValidator("string"), related_to="ThisOtherParam", ) print("Trying string that should pass validation") # Should pass validation print((p.set_value("somestring"))) print(("Got value: {}".format(p.value()))) print("Trying an int that should fail validation") # Should fail validation with self.assertRaises(ValidationError) as ex: print((p.set_value(10))) print(("Correctly got exception {}".format(ex.exception)))
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)
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))