def scan_resource_conf(self, conf: Dict[str, List[Any]]) -> CheckResult: handle_dynamic_values(conf) excluded_key = self.get_excluded_key() if excluded_key is not None: if dpath.search(conf, excluded_key) != {}: value = dpath.get(conf, excluded_key) if isinstance(value, list) and len(value) == 1: value = value[0] if self.check_excluded_condition(value): return CheckResult.PASSED inspected_key = self.get_inspected_key() bad_values = self.get_forbidden_values() if dpath.search(conf, inspected_key) != {}: value = dpath.get(conf, inspected_key) if isinstance(value, list) and len(value) == 1: value = value[0] if get_referenced_vertices_in_value(value=value, aliases={}, resources_types=[]): # we don't provide resources_types as we want to stay provider agnostic return CheckResult.UNKNOWN if value in bad_values or ANY_VALUE in bad_values: return CheckResult.FAILED else: return CheckResult.PASSED return self.missing_attribute_result
def my_path(my_dictionary: dict = None, path=None, value=None): items = path.split('/') # if the last character is a digit if items[-1].isdigit(): items.pop() new_path = "/".join(items) # Check the element before the digit is already in the dictionary if dpath.search(my_dictionary, new_path): # Is it a list ? if isinstance(dpath.get(my_dictionary, new_path), list): dpath.new(my_dictionary, path, value) else: raise ValueError("The following entry can not be added: {}. " "This is not a list".format( my_dictionary[items[-2]])) # Not in the dictionary else: dpath.new(my_dictionary, new_path, []) dpath.new(my_dictionary, path, value) else: if dpath.search(my_dictionary, path): # Set the new value dpath.set(my_dictionary, path, value) else: dpath.new(my_dictionary, path, value) return my_dictionary
def scan_spec_conf(self, conf): metadata = {} if conf['kind'] == 'Pod': security_profile = dpath.search( conf, 'spec/securityContext/seccompProfile/type') if security_profile: security_profile = dpath.get( conf, 'spec/securityContext/seccompProfile/type') return CheckResult.PASSED if security_profile == 'RuntimeDefault' else CheckResult.FAILED if "metadata" in conf: metadata = conf["metadata"] if conf['kind'] == 'Deployment': security_profile = dpath.search( conf, 'spec/template/spec/securityContext/seccompProfile/type') if security_profile: security_profile = dpath.get( conf, 'spec/template/spec/securityContext/seccompProfile/type') return CheckResult.PASSED if security_profile == 'RuntimeDefault' else CheckResult.FAILED if "metadata" in conf: metadata = conf["metadata"] if conf['kind'] == 'StatefulSet': security_profile = dpath.search( conf, 'spec/template/spec/securityContext/seccompProfile/type') if security_profile: security_profile = dpath.get( conf, 'spec/template/spec/securityContext/seccompProfile/type') return CheckResult.PASSED if security_profile == 'RuntimeDefault' else CheckResult.FAILED if "metadata" in conf: metadata = conf["metadata"] elif conf['kind'] == 'CronJob': if "spec" in conf: if "jobTemplate" in conf["spec"]: if "spec" in conf["spec"]["jobTemplate"]: if "template" in conf["spec"]["jobTemplate"]["spec"]: if "metadata" in conf["spec"]["jobTemplate"][ "spec"]["template"]: metadata = conf["spec"]["jobTemplate"]["spec"][ "template"]["metadata"] else: if "spec" in conf: if "template" in conf["spec"]: if "metadata" in conf["spec"]["template"]: metadata = conf["spec"]["template"]["metadata"] if metadata: if "annotations" in metadata and isinstance( metadata['annotations'], dict): if "seccomp.security.alpha.kubernetes.io/pod" in metadata[ "annotations"]: if ("docker/default" in metadata["annotations"] ["seccomp.security.alpha.kubernetes.io/pod"] or "runtime/default" in metadata["annotations"] ["seccomp.security.alpha.kubernetes.io/pod"]): return CheckResult.PASSED return CheckResult.FAILED
def scan_spec_conf(self, conf): metadata = {} if conf['kind'] == 'Pod': security_profile = dpath.search( conf, 'spec/securityContext/seccompProfile/type') if security_profile: security_profile = dpath.get( conf, 'spec/securityContext/seccompProfile/type') return CheckResult.PASSED if security_profile == 'RuntimeDefault' else CheckResult.FAILED if "metadata" in conf: metadata = conf["metadata"] if conf['kind'] == 'Deployment': security_profile = dpath.search( conf, 'spec/template/spec/securityContext/seccompProfile/type') if security_profile: security_profile = dpath.get( conf, 'spec/template/spec/securityContext/seccompProfile/type') return CheckResult.PASSED if security_profile == 'RuntimeDefault' else CheckResult.FAILED if "metadata" in conf: metadata = conf["metadata"] if conf['kind'] == 'StatefulSet': security_profile = dpath.search( conf, 'spec/template/spec/securityContext/seccompProfile/type') if security_profile: security_profile = dpath.get( conf, 'spec/template/spec/securityContext/seccompProfile/type') return CheckResult.PASSED if security_profile == 'RuntimeDefault' else CheckResult.FAILED if "metadata" in conf: metadata = conf["metadata"] elif conf['kind'] == 'CronJob': if "spec" in conf: if "jobTemplate" in conf["spec"]: if "spec" in conf["spec"]["jobTemplate"]: if "template" in conf["spec"]["jobTemplate"]["spec"]: if "metadata" in conf["spec"]["jobTemplate"][ "spec"]["template"]: metadata = conf["spec"]["jobTemplate"]["spec"][ "template"]["metadata"] else: inner_metadata = self.get_inner_entry(conf, "metadata") metadata = inner_metadata if inner_metadata else metadata if metadata: if metadata.get('annotations'): for annotation in force_list(metadata["annotations"]): for key in annotation: if "seccomp.security.alpha.kubernetes.io/pod" in key: if "docker/default" in annotation[ key] or "runtime/default" in annotation[ key]: return CheckResult.PASSED return CheckResult.FAILED
def _bulk_meta_error(cls, error): try: _, err_type = next(dpath.search(error, "*/error/type", yielded=True)) _, reason = next(dpath.search(error, "*/error/reason", yielded=True)) if err_type == "cluster_block_exception": raise errors.server_error.LowDiskSpace( "metrics, logs and all indexed data is in read-only mode!", reason=re.sub(r"^index\s\[.*?\]\s", "", reason) if reason else "" ) return except StopIteration: pass
def finalize_variables_init(self): for variable_name, holder in self._holders.items(): periods = holder.buffer.keys() # We need to handle small periods first for set_input to work sorted_periods = sorted(periods, key=key_period_size) for period in sorted_periods: array = holder.buffer[period] try: holder.set_input(period, array) except PeriodMismatchError as e: # This errors happens when we try to set a variable value for a period that doesn't match its definition period # It is only raised when we consume the buffer. We thus don't know which exact key caused the error. # We do a basic research to find the culprit path culprit_path = next( dpath.search(self.entities_json, "*/{}/{}".format(e.variable_name, str(e.period)), yielded=True), None) if culprit_path: path = [self.plural] + culprit_path[0].split('/') else: path = [ self.plural ] # Fallback: if we can't find the culprit, just set the error at the entities level raise SituationParsingError(path, e.message)
def _locate_variables_assignments(self, definition_type, folder, var_name): var_assignments_paths = {} assignment_regex = self._generate_evaluation_regex( definition_type, var_name) for source_file in [ file for file in os.listdir(folder) if file.endswith('.tf') and self.tf_definitions.get(os.path.join(folder, file)) ]: file_path = os.path.join(folder, source_file) var_entries = dpath.search( self.tf_definitions[file_path], '**', afilter=lambda x: re.findall(assignment_regex, str(x)), yielded=True) var_assignments_paths[file_path] = [] for definition_path, expression in var_entries: context_path, definition_name = self.extract_context_path( definition_path) var_assignments_paths[file_path].append({ 'definition_name': definition_name, 'definition_expression': expression, 'definition_path': definition_path }) var_assignments_paths = { k: v for (k, v) in var_assignments_paths.items() if len(v) > 0 } return var_assignments_paths
def _search(doc_cls, obj, path, only_values=True): """ Call dpath.search with yielded=True, collect result values """ norm_path = doc_cls.get_dpath_translated_path(path) return [ v if only_values else (k, v) for k, v in dpath.search( obj, norm_path, separator='.', yielded=True) ]
def bulk_error(cls, e, _, **__): if not e.errors: return # Else try returning a better error string for _, reason in dpath.search(e.errors[0], "*/error/reason", yielded=True): return reason
def _evaluate_folder_variables(self, folder): assignment_files = dpath.search(self.definitions_context, f'**.assignments', separator='.') variable_file_object = {k: v for k, v in assignment_files.items() if folder in k} if variable_file_object: for var_file, variable_assignments in variable_file_object.items(): relative_var_file = f'/{os.path.relpath(var_file, self.root_folder)}' for var_name, var_value in variable_assignments['variable']['assignments'].items(): evaluated_definitions = self._locate_assignments(folder, var_name) var_assignments = {'definitions': evaluated_definitions, 'var_file': relative_var_file} self._assign_definition_value(var_name, var_value, var_assignments)
def scan_resource_conf(self, conf): excluded_key = self.get_excluded_key() if excluded_key is not None: if dpath.search(conf, excluded_key) != {}: value = dpath.get(conf, excluded_key) if isinstance(value, list) and len(value) == 1: value = value[0] if self.check_excluded_condition(value): return CheckResult.PASSED inspected_key = self.get_inspected_key() bad_values = self.get_forbidden_values() if dpath.search(conf, inspected_key) != {}: value = dpath.get(conf, inspected_key) if isinstance(value, list) and len(value) == 1: value = value[0] if value in bad_values or ANY_VALUE in bad_values: return CheckResult.FAILED return CheckResult.PASSED
def plot(self, view_settings={}, build_label=build_label_default, build_marker=build_marker_default, build_color=build_color_default ): probe_view_settings = {k: {} for k in self.probes} logging.debug(probe_view_settings) master_tag_dict = {p.master_tag: p for p in self.probes} # TODO: can the following nested loop be recast into a single loop? for pattern, settings in view_settings.items(): for p in dpath.search(master_tag_dict, "/" + pattern).values(): probe_view_settings[p].update(settings) logging.debug(probe_view_settings) fig = plt.figure(figsize=(10, 10)) plt.xlabel(self.x_label) plt.ylabel(self.y_label) plt.title(self.title) plt.grid(True) plt.gca().ticklabel_format(useOffset=False, style='sci') tickFormat = mtick.FormatStrFormatter("%.4e") plt.gca().get_yaxis().set_major_formatter(tickFormat) if len(self.x_range) > 0: plt.xlim(self.x_range[0], self.x_range[1]) if len(self.y_range) > 0: plt.ylim(self.y_range[0], self.y_range[1]) for probe in self.probes: logging.debug(probe.file_name + " " + probe.variable_name) x, var = probe.data # TODO : can we write this if-then in a more pythonist way? if build_label == None: probe_label = None else: probe_label = build_label(probe) if build_color == None: probe_color = None else: probe_color = build_color(probe) plot_args = probe_view_settings[probe] # plotArgs.update({'label' : probe_label}) plot_args['label'] = probe_label # logging.debug(plotArgs) plt.plot(x, var, **plot_args) plt.legend(loc='best', frameon=False) plt.show(fig)
def _evaluate_folder_variables(self, folder): """ Locate all assignments extracted by the context parser, and assigns them to corresponding variables found in tf_definitions :param folder: folder to assign variables in :return: """ assignment_files = dpath.search(self.definitions_context, f'**.assignments', separator='.') assignment_files = {k: v for k, v in assignment_files.items() if folder in k} if assignment_files: self._assign_definitions(assignment_files, folder)
def bulk_error(cls, e, _, **__): if not e.errors: return # Currently we only handle the first error error = e.errors[0] cls._bulk_meta_error(error) # Else try returning a better error string for _, reason in dpath.search(e.errors[0], "*/error/reason", yielded=True): return reason
def raise_period_mismatch(self, entity, json, e): # This error happens when we try to set a variable value for a period that doesn't match its definition period # It is only raised when we consume the buffer. We thus don't know which exact key caused the error. # We do a basic research to find the culprit path culprit_path = next( dpath.search(json, "*/{}/{}".format(e.variable_name, str(e.period)), yielded = True), None) if culprit_path: path = [entity.plural] + culprit_path[0].split('/') else: path = [entity.plural] # Fallback: if we can't find the culprit, just set the error at the entities level raise SituationParsingError(path, e.message)
def _get_single(item_): values_ = sorted( list( dpath.search(_ProjectGlobalConfig.config, item_, yielded=True))) if not values_: return None if len(values_) > 1: values_ = [v[1] for v in values_] else: values_ = values_[0][1] return values_
def plot(self, view_settings={}, build_label=build_label_default, build_marker=build_marker_default, build_color=build_color_default): probe_view_settings = {k: {} for k in self.probes} logging.debug(probe_view_settings) master_tag_dict = {p.master_tag: p for p in self.probes} # TODO: can the following nested loop be recast into a single loop? for pattern, settings in view_settings.items(): for p in dpath.search(master_tag_dict, "/" + pattern).values(): probe_view_settings[p].update(settings) logging.debug(probe_view_settings) fig = plt.figure(figsize=(10, 10)) plt.xlabel(self.x_label) plt.ylabel(self.y_label) plt.title(self.title) plt.grid(True) plt.gca().ticklabel_format(useOffset=False, style='sci') tickFormat = mtick.FormatStrFormatter("%.4e") plt.gca().get_yaxis().set_major_formatter(tickFormat) if len(self.x_range) > 0: plt.xlim(self.x_range[0], self.x_range[1]) if len(self.y_range) > 0: plt.ylim(self.y_range[0], self.y_range[1]) for probe in self.probes: logging.debug(probe.file_name + " " + probe.variable_name) x, var = probe.data # TODO : can we write this if-then in a more pythonist way? if build_label == None: probe_label = None else: probe_label = build_label(probe) if build_color == None: probe_color = None else: probe_color = build_color(probe) plot_args = probe_view_settings[probe] # plotArgs.update({'label' : probe_label}) plot_args['label'] = probe_label # logging.debug(plotArgs) plt.plot(x, var, **plot_args) plt.legend(loc='best', frameon=False) plt.show(fig)
def scan_resource_conf(self, conf: Dict[str, List[Any]]) -> CheckResult: self.handle_dynamic_values(conf) excluded_key = self.get_excluded_key() if excluded_key is not None: if dpath.search(conf, excluded_key) != {}: value = dpath.get(conf, excluded_key) if isinstance(value, list) and len(value) == 1: value = value[0] if self.check_excluded_condition(value): return CheckResult.PASSED inspected_key = self.get_inspected_key() bad_values = self.get_forbidden_values() if dpath.search(conf, inspected_key) != {}: value = dpath.get(conf, inspected_key) if isinstance(value, list) and len(value) == 1: value = value[0] if value in bad_values or ANY_VALUE in bad_values: return CheckResult.FAILED else: return CheckResult.PASSED return self.missing_attribute_result
def parse_section_title(self, cell, current_section=None): if not isinstance(cell.internal_value, str): raise SheetParsingError( "Enable to parse summary: Unexpected value in cell '{}'". format(cell.coordinate)) description = ''.join(cell.internal_value.split('.')[1:]).strip() key = slugify(description, stopwords=True) path = f"subparams/{current_section}/subparams/{key}" if current_section else f"subparams/{key}" if dpath.search(self.sections, path): raise SheetParsingError( "Name collision: section '{}' alredy exists.".format(path)) dpath.new(self.sections, path, { 'description': description, 'metadata': { 'order': [] } }) # Keep track of the order order_path = f'subparams/{current_section}/metadata/order' if current_section else 'metadata/order' dpath.get(self.sections, order_path).append(key) return path
def execute_postponed_actions(context, is_data: bool = True, actions_switcher: dict = None): """ Execute postponed actions :param actions_switcher: the automaton list of actions which can be postponed :param context: the current context :param is_data: boolean, True means set only data, False means execute actions (use models) :return: None """ log.debug("postponed_action") if is_data: # Do settings log.debug("is data statement") key_list = set(ATTRIBUTE_LIST).intersection( context.pre_conditions["postponed"].keys()) for key in key_list: log.debug("Key : {}".format(str(key))) while context.pre_conditions["postponed"][key]: log.debug("Context.pre_conditions[postponed][key]: {} ".format( context.pre_conditions["postponed"][key])) elem = context.pre_conditions["postponed"][key].pop(0) log.debug("elem contains: {}".format(elem)) try: # Check the path exist if dpath.search(context.pre_conditions[key], elem["path"]): dpath.set(context.pre_conditions[key], elem["path"], elem["value"]) else: # Create a new entry dpath.new(context.pre_conditions[key], elem["path"], elem["value"]) except AssertionError as assertion: log.error("Update data failed.\n '{}'".format( assertion.args[0])) raise_exception( AssertionError, "Update data failed.\n '{}'".format(assertion.args[0]), context.evidence_folder) log.debug("pre_conditions : {}".format( str(context.pre_conditions[key]))) else: # Do execute steps log.debug("else statement") postponed_actions = deepcopy( context.pre_conditions["postponed"]["execution"]) context.pre_conditions["postponed"]["execution"].clear() for index, action in enumerate(postponed_actions): log.debug("Index: {}, action: {}".format(index, action)) if isinstance(action, str): context.execute_steps(action) elif isinstance(action, tuple) and len(action) == 2: log.debug("action switcher: {}".format(action)) actions_switcher[action[0]](**action[1]) sleep(0.5) else: raise Exception("Unknown action to process.\n" "Get '{}'".format(repr(action))) log.debug("End of postponed_action ")