def _validate_general_host_attributes(host_attributes, new): """Check if the given attribute name exists, no type check""" all_host_attribute_names = _retrieve_host_attributes() # inventory_failed and site are no "real" host_attributes (TODO: Clean this up!) all_host_attribute_names.extend(["inventory_failed", "site"]) for name, value in host_attributes.items(): if name not in all_host_attribute_names: raise MKUserError( None, _("Unknown attribute: %s") % escaping.escape_attribute(name)) # For real host attributes validate the values try: attr = watolib.host_attribute(name) except KeyError: attr = None if attr is not None: if attr.needs_validation("host", new): attr.validate_input(value, "") # The site attribute gets an extra check if name == "site" and value not in allsites().keys(): raise MKUserError( None, _("Unknown site %s") % escaping.escape_attribute(value))
def _validate_general_host_attributes(host_attributes, extra_attrs: Sequence[str], new: bool): """Check if the given attribute name exists, no type check""" all_host_attribute_names = _retrieve_host_attributes() all_host_attribute_names.extend(extra_attrs) for name, value in host_attributes.items(): if name not in all_host_attribute_names: raise MKUserError( None, _("Unknown attribute: %s") % escaping.escape_attribute(name)) # For real host attributes validate the values try: attr: Optional[ABCHostAttribute] = host_attribute(name) except KeyError: attr = None if attr is not None: if attr.needs_validation("host", new): attr.validate_input(value, "") # The site attribute gets an extra check if name == "site" and value not in allsites().keys(): raise MKUserError( None, _("Unknown site %s") % escaping.escape_attribute(value))
def _validate_general_host_attributes(host_attributes, new): # inventory_failed and site are no "real" host_attributes (TODO: Clean this up!) all_host_attribute_names = list( host_attribute_registry.keys()) + ["inventory_failed", "site"] for name, value in host_attributes.items(): if name not in all_host_attribute_names: raise MKUserError( None, _("Unknown attribute: %s") % escaping.escape_attribute(name)) # For real host attributes validate the values try: attr: Optional[ABCHostAttribute] = watolib.host_attribute(name) except KeyError: attr = None if attr is not None: if attr.needs_validation("host", new): attr.validate_input(value, "") # The site attribute gets an extra check if name == "site" and value not in allsites().keys(): raise MKUserError( None, _("Unknown site %s") % escaping.escape_attribute(value))
def _render_attributes(**attrs: HTMLTagAttributeValue) -> Iterator[str]: css = _get_normalized_css_classes(attrs) if css: attrs["class"] = css # options such as 'selected' and 'checked' dont have a value in html tags options = [] # Links require href to be first attribute href = attrs.pop('href', None) if href: attributes = list(attrs.items()) attributes.insert(0, ("href", href)) else: attributes = list(attrs.items()) # render all attributes for key_unescaped, v in attributes: if v is None: continue key = escaping.escape_attribute(key_unescaped.rstrip('_')) if key.startswith('data_'): key = key.replace('_', '-', 1) # HTML data attribute: 'data-name' if v == '': options.append(key) continue if not isinstance(v, list): v = escaping.escape_attribute(v) else: if key == "class": sep = ' ' elif key == "style" or key.startswith('on'): sep = '; ' else: # TODO: Can we drop this special Feature? No idea what it is used for. sep = '_' joined_value = sep.join( [a for a in (escaping.escape_attribute(vi) for vi in v) if a]) # TODO: Can we drop this special feature? Find an cleanup the call sites if sep.startswith(';'): joined_value = re.sub(';+', ';', joined_value) v = joined_value yield ' %s=\"%s\"' % (key, v) for k in options: yield " %s=\'\'" % k
def _get_api_call(): action = request.var('action') for cls in api_call_collection_registry.values(): api_call = cls().get_api_calls().get(action) if api_call: return api_call raise MKUserError(None, "Unknown API action %s" % escaping.escape_attribute(action))
def _get_api_call() -> APICallDefinitionDict: action = request.get_str_input_mandatory("action") for cls in api_call_collection_registry.values(): api_call = cls().get_api_calls().get(action) if api_call: return api_call raise MKUserError(None, "Unknown API action %s" % escaping.escape_attribute(action))
def page_login() -> None: title = _("Checkmk Mobile") mobile_html_head(title) jqm_page_header(title, id_="login") html.div(_("Welcome to Checkmk Mobile."), id_="loginhead") html.begin_form("login", method='POST', add_transid=False) # Keep information about original target URL default_origtarget = "index.py" if requested_file_name(request) in ["login", "logout" ] else makeuri(request, []) origtarget = request.get_url_input("_origtarget", default_origtarget) html.hidden_field('_origtarget', escaping.escape_attribute(origtarget)) html.text_input("_username", label=_("Username:"******"username", id_="input_user") html.password_input( "_password", size=None, label=_("Password:"******"current-password", id_="input_pass", ) html.br() html.button("_login", _('Login')) html.set_focus("_username") html.end_form() html.open_div(id_="loginfoot") html.img("themes/facelift/images/logo_cmk_small.png", class_="logomk") html.div(HTML(_("© <a target=\"_blank\" href=\"https://checkmk.com\">tribe29 GmbH</a>")), class_="copyright") html.close_div() # close content-div html.close_div() html.close_div() # close page-div mobile_html_foot()
def action(self) -> ActionResult: if request.var("_reset"): if not transactions.check_transaction(): return None try: del self._current_settings[self._varname] except KeyError: pass msg = escape_to_html( _("Resetted configuration variable %s to its default.") % self._varname) else: new_value = self._valuespec.from_html_vars("ve") self._valuespec.validate_value(new_value, "ve") self._current_settings[self._varname] = new_value msg = HTML( _("Changed global configuration variable %s to %s.") % ( escaping.escape_attribute(self._varname), self._valuespec.value_to_html(new_value), )) self._save() _changes.add_change( "edit-configvar", msg, sites=self._affected_sites(), domains=[self._config_variable.domain()], need_restart=self._config_variable.need_restart(), ) return redirect(self._back_url())
def run(self) -> Iterator[ACResult]: self._executed = True try: # Do not merge results that have been gathered on one site for different sites results = list(self.execute()) num_sites = len(set(r.site_id for r in results)) if num_sites > 1: for result in results: result.from_test(self) yield result return # Merge multiple results produced for a single site total_result = ACResult.merge(*list(self.execute())) total_result.from_test(self) yield total_result except Exception: logger.exception("error executing configuration test %s", self.__class__.__name__) result = ACResultCRIT( "<pre>%s</pre>" % _("Failed to execute the test %s: %s") % (escaping.escape_attribute( self.__class__.__name__), traceback.format_exc())) result.from_test(self) yield result
def time_series_math( operator_id: Literal["+", "*", "-", "/", "MAX", "MIN", "AVERAGE", "MERGE"], operands_evaluated: List[TimeSeries], ) -> Optional[TimeSeries]: operators = time_series_operators() if operator_id not in operators: raise MKGeneralException( _("Undefined operator '%s' in graph expression") % escaping.escape_attribute(operator_id) ) # Test for correct arity on FOUND[evaluated] data if any( ( operator_id in ["-", "/"] and len(operands_evaluated) != 2, len(operands_evaluated) < 1, ) ): # raise MKGeneralException(_("Incorrect amount of data to correctly evaluate expression")) # Silently return so to get an empty graph slot return None _op_title, op_func = operators[operator_id] twindow = operands_evaluated[0].twindow return TimeSeries([op_func_wrapper(op_func, tsp) for tsp in zip(*operands_evaluated)], twindow)
def _show_output_box(title, content): html.h3(title, class_="table") html.open_div(class_="log_output") html.write_html( HTML(escaping.escape_attribute(content).replace("\n", "<br>").replace(" ", " ")) ) html.close_div()
def top_heading( writer: HTMLWriter, request: Request, title: str, breadcrumb: Breadcrumb, page_menu: Optional[PageMenu] = None, page_state: Optional[PageState] = None, *, browser_reload: float, ) -> None: writer.open_div(id_="top_heading") writer.open_div(class_="titlebar") # HTML() is needed here to prevent a double escape when we do self._escape_attribute # here and self.a() escapes the content (with permissive escaping) again. We don't want # to handle "title" permissive. html_title = HTML(escaping.escape_attribute(title)) writer.a( html_title, class_="title", href="#", onfocus="if (this.blur) this.blur();", onclick="this.innerHTML='%s'; document.location.reload();" % _("Reloading..."), ) if breadcrumb: BreadcrumbRenderer().show(breadcrumb) if page_state is None: page_state = _make_default_page_state( writer, request, browser_reload=browser_reload, ) if page_state: PageStateRenderer().show(page_state) writer.close_div() # titlebar if page_menu: PageMenuRenderer().show( page_menu, hide_suggestions=not user.get_tree_state("suggestions", "all", True), ) writer.close_div() # top_heading if page_menu: PageMenuPopupsRenderer().show(page_menu) if active_config.debug: _dump_get_vars( writer, request, )
def format_plugin_output(output: str, row: "Optional[Row]" = None, shall_escape: bool = True) -> HTML: assert not isinstance(output, dict) ok_marker = '<b class="stmark state0">OK</b>' warn_marker = '<b class="stmark state1">WARN</b>' crit_marker = '<b class="stmark state2">CRIT</b>' unknown_marker = '<b class="stmark state3">UNKN</b>' # In case we have a host or service row use the optional custom attribute # ESCAPE_PLUGIN_OUTPUT (set by host / service ruleset) to override the global # setting. if row: custom_vars = row.get("service_custom_variables", row.get("host_custom_variables", {})) if "ESCAPE_PLUGIN_OUTPUT" in custom_vars: shall_escape = custom_vars["ESCAPE_PLUGIN_OUTPUT"] == "1" if shall_escape: output = escaping.escape_attribute(output) else: output = "%s" % output output = (output.replace("(!)", warn_marker).replace("(!!)", crit_marker).replace( "(?)", unknown_marker).replace("(.)", ok_marker)) if row and "[running on" in output: a = output.index("[running on") e = output.index("]", a) hosts = output[a + 12:e].replace(" ", "").split(",") h = get_host_list_links(row["site"], hosts) output = output[:a] + "running on " + ", ".join(h) + output[e + 1:] prevent_url_icons = (row.get("service_check_command", "") == "check_mk-checkmk_agent" if row is not None else False) if shall_escape and not prevent_url_icons: http_url = r"(http[s]?://[A-Za-z0-9\-._~:/?#\[\]@!$&'()*+,;=%]+)" # (?:<A HREF="), (?: target="_blank">)? and endswith(" </A>") is a special # handling for the HTML code produced by check_http when "clickable URL" option is active. output = re.sub( "(?:<A HREF=")?" + http_url + "(?: target="_blank">)?", lambda p: str( html.render_icon_button( unescape(p.group(1).replace(""", "")), unescape(p.group(1).replace(""", "")), "link", )), output, ) if output.endswith(" </A>"): output = output[:-11] return HTML(output)
def _validate_host_tags(host_tags): for tag_group_id, tag_id in host_tags.items(): for tag_group in config.tags.tag_groups: if tag_group.id == tag_group_id: for grouped_tag in tag_group.tags: if grouped_tag.id == tag_id: break else: raise MKUserError( None, _("Unknown tag %s") % escaping.escape_attribute(tag_id)) break else: raise MKUserError( None, _("Unknown tag group %s") % escaping.escape_attribute(tag_group_id))
def _show_output_box(title: str, content: bytes) -> None: html.h3(title, class_="table") html.open_div(class_="log_output") html.write_html( HTML( escaping.escape_attribute( content.decode(errors="surrogateescape")).replace( "\n", "<br>").replace(" ", " "))) html.close_div()
def compute_output_message(effective_state, rule): output = [] if effective_state["output"]: output.append(effective_state["output"]) str_state = str(effective_state["state"]) if str_state in rule.get("state_messages", {}): output.append(escaping.escape_attribute(rule["state_messages"][str_state])) return ", ".join(output)
def _validate_host_tags(host_tags): """Check if the tag group exists and the tag value is valid""" for tag_group_id, tag_id in host_tags.items(): for tag_group in active_config.tags.tag_groups: if tag_group.id == tag_group_id: for grouped_tag in tag_group.tags: if grouped_tag.id == tag_id: break else: raise MKUserError( None, _("Unknown tag %s") % escaping.escape_attribute(tag_id)) break else: raise MKUserError( None, _("Unknown tag group %s") % escaping.escape_attribute(tag_group_id))
def check_aggregation_title_uniqueness(aggregations): known_titles: Set[Any] = set() for attrs in aggregations.values(): title = attrs["title"] if title in known_titles: raise MKConfigError( _('Duplicate BI aggregation with the title "<b>%s</b>". ' "Please check your BI configuration and make sure that within each group no aggregation has " "the same title as any other. Note: you can use arguments in the top level " "aggregation rule, like <tt>Host $HOST$</tt>.") % (escaping.escape_attribute(title))) known_titles.add(title)
def check_title_uniqueness(forest): # Legacy, will be removed any decade from now # One aggregation cannot be in mutliple groups. known_titles: Set[Any] = set() for aggrs in forest.values(): for aggr in aggrs: title = aggr["title"] if title in known_titles: raise MKConfigError( _('Duplicate BI aggregation with the title "<b>%s</b>". ' "Please check your BI configuration and make sure that within each group no aggregation has " "the same title as any other. Note: you can use arguments in the top level " "aggregation rule, like <tt>Host $HOST$</tt>.") % (escaping.escape_attribute(title))) known_titles.add(title)
def _from_vars(self) -> None: user.need_permission("wato.download_agent_output") host_name = request.var("host") if not host_name: raise MKGeneralException(_("The host is missing.")) ty = request.var("type") if ty not in ["walk", "agent"]: raise MKGeneralException(_("Invalid type specified.")) self._back_url = request.get_url_input("back_url", deflt="") or None host = watolib.Folder.current().host(host_name) if not host: raise MKGeneralException( _("Host is not managed by WATO. " 'Click <a href="%s">here</a> to go back.') % escape_attribute(self._back_url)) host.need_permission("read") self._request = FetchAgentOutputRequest(host=host, agent_type=ty)
def render(self, row: Row, cell: Cell) -> CellSpec: return "", escaping.escape_attribute(row["aggr_name"])
def _transform_builtin_dashboards() -> None: if 'builtin_dashboards_transformed' in g: return # Only do this once for name, dashboard in builtin_dashboards.items(): # Do not transform dashboards which are already in the new format if 'context' in dashboard: continue # Transform the dashlets for nr, dashlet in enumerate(dashboard['dashlets']): dashlet.setdefault('show_title', True) if dashlet.get('url', '').startswith('dashlet_hoststats') or \ dashlet.get('url', '').startswith('dashlet_servicestats'): # hoststats and servicestats dashlet['type'] = dashlet['url'][8:].split('.', 1)[0] if '?' in dashlet['url']: # Transform old parameters: # wato_folder # host_contact_group # service_contact_group paramstr = dashlet['url'].split('?', 1)[1] dashlet['context'] = {} for key, val in [p.split('=', 1) for p in paramstr.split('&')]: if key == 'host_contact_group': dashlet['context']['opthost_contactgroup'] = { 'neg_opthost_contact_group': '', 'opthost_contact_group': val, } elif key == 'service_contact_group': dashlet['context']['optservice_contactgroup'] = { 'neg_optservice_contact_group': '', 'optservice_contact_group': val, } elif key == 'wato_folder': dashlet['context']['wato_folder'] = { 'wato_folder': val, } del dashlet['url'] elif dashlet.get('urlfunc') and not isinstance(dashlet['urlfunc'], str): raise MKGeneralException( _('Unable to transform dashlet %d of dashboard %s: ' 'the dashlet is using "urlfunc" which can not be ' 'converted automatically.') % (nr, name)) elif dashlet.get('url', '') != '' or dashlet.get('urlfunc') or dashlet.get('iframe'): # Normal URL based dashlet dashlet['type'] = 'url' if dashlet.get('iframe'): dashlet['url'] = dashlet['iframe'] del dashlet['iframe'] elif dashlet.get('view', '') != '': # Transform views # There might be more than the name in the view definition view_name = dashlet['view'].split('&')[0] # Copy the view definition into the dashlet copy_view_into_dashlet(dashlet, nr, view_name, load_from_all_views=True) del dashlet['view'] else: raise MKGeneralException( _('Unable to transform dashlet %d of dashboard %s. ' 'You will need to migrate it on your own. Definition: %r') % (nr, name, escaping.escape_attribute(dashlet))) dashlet.setdefault('context', {}) dashlet.setdefault('single_infos', []) # the modification time of builtin dashboards can not be checked as on user specific # dashboards. Set it to 0 to disable the modification chech. dashboard.setdefault('mtime', 0) dashboard.setdefault('show_title', True) if dashboard['title'] is None: dashboard['title'] = _('No title') dashboard['show_title'] = False dashboard.setdefault('single_infos', []) dashboard.setdefault('context', {}) dashboard.setdefault('topic', _('Overview')) dashboard.setdefault('description', dashboard.get('title', '')) g.builtin_dashboards_transformed = True
def _show_test_row(self, table, test_id, test_results_by_site, site_ids): table.row() table.cell(_("Actions"), css="buttons", sortable=False) html.icon_button(None, _("Toggle result details"), "toggle_details", onclick="cmk.wato.toggle_container('test_result_details_%s')" % test_id) worst_result = sorted(test_results_by_site["site_results"].values(), key=lambda result: result.status)[0] # Disabling of test in total is_test_disabled = self._is_test_disabled(test_id) if is_test_disabled: html.icon_button( makeactionuri(request, transactions, [ ("_do", "enable"), ("_test_id", worst_result.test_id), ]), _("Reenable this test"), "enable_test", ) else: html.icon_button( makeactionuri(request, transactions, [ ("_do", "disable"), ("_test_id", worst_result.test_id), ]), _("Disable this test"), "disable_test", ) # assume all have the same test meta information (title, help, ...) table.cell(_("Title"), css="title " + "stale" if is_test_disabled else "") html.write_text(test_results_by_site["test"]["title"]) # Now loop all sites to display their results for site_id in site_ids: if is_test_disabled: table.cell(site_id, "") table.cell("", "") continue result = test_results_by_site["site_results"].get(site_id) if result is None: table.cell(site_id, css="state state-1") table.cell("", css="buttons") continue is_acknowledged = self._is_acknowledged(result) if is_acknowledged or result.status == -1: css = "state stale" else: css = "state state%d" % result.status table.cell(site_id, css=css) html.open_div(title=result.text) html.write_text(result.status_name()) html.close_div() table.cell("", css="buttons") if result.status != 0: if is_acknowledged: html.icon_button( makeactionuri(request, transactions, [ ("_do", "unack"), ("_site_id", result.site_id), ("_status_id", result.status), ("_test_id", result.test_id), ]), _("Unacknowledge this test result for site %s") % site_id, "unacknowledge_test", ) else: html.icon_button( makeactionuri(request, transactions, [ ("_do", "ack"), ("_site_id", result.site_id), ("_status_id", result.status), ("_test_id", result.test_id), ]), _("Acknowledge this test result for site %s") % site_id, "acknowledge_test", ) else: html.write_text("") # Add toggleable notitication context table.row(class_="ac_test_details hidden", id_="test_result_details_%s" % test_id) table.cell(colspan=2 + 2 * len(site_ids)) html.write_text(test_results_by_site["test"]["help"]) if not is_test_disabled: html.open_table() for site_id in site_ids: result = test_results_by_site["site_results"].get(site_id) if result is None: continue html.open_tr() html.td(escaping.escape_attribute(site_id)) html.td("%s: %s" % (result.status_name(), result.text)) html.close_tr() html.close_table() # This dummy row is needed for not destroying the odd/even row highlighting table.row(class_="hidden")
def test_htmllib_integration(): assert escaping.escape_attribute("") == "" assert escaping.escape_text("") == ""
def test_escape_attribute(inp, out): assert escaping.escape_attribute(inp) == out
def _transform_builtin_dashboards() -> None: for name, dashboard in builtin_dashboards.items(): # Do not transform dashboards which are already in the new format if "context" in dashboard: continue # Transform the dashlets for nr, dashlet in enumerate(dashboard["dashlets"]): dashlet.setdefault("show_title", True) if dashlet.get("url", "").startswith("dashlet_hoststats") or dashlet.get( "url", "" ).startswith("dashlet_servicestats"): # hoststats and servicestats dashlet["type"] = dashlet["url"][8:].split(".", 1)[0] if "?" in dashlet["url"]: # Transform old parameters: # wato_folder # host_contact_group # service_contact_group paramstr = dashlet["url"].split("?", 1)[1] dashlet["context"] = {} for key, val in [p.split("=", 1) for p in paramstr.split("&")]: if key == "host_contact_group": dashlet["context"]["opthost_contactgroup"] = { "neg_opthost_contact_group": "", "opthost_contact_group": val, } elif key == "service_contact_group": dashlet["context"]["optservice_contactgroup"] = { "neg_optservice_contact_group": "", "optservice_contact_group": val, } elif key == "wato_folder": dashlet["context"]["wato_folder"] = { "wato_folder": val, } del dashlet["url"] elif dashlet.get("urlfunc") and not isinstance(dashlet["urlfunc"], str): raise MKGeneralException( _( "Unable to transform dashlet %d of dashboard %s: " 'the dashlet is using "urlfunc" which can not be ' "converted automatically." ) % (nr, name) ) elif dashlet.get("url", "") != "" or dashlet.get("urlfunc") or dashlet.get("iframe"): # Normal URL based dashlet dashlet["type"] = "url" if dashlet.get("iframe"): dashlet["url"] = dashlet["iframe"] del dashlet["iframe"] elif dashlet.get("view", "") != "": # Transform views # There might be more than the name in the view definition view_name = dashlet["view"].split("&")[0] # Copy the view definition into the dashlet copy_view_into_dashlet(dashlet, nr, view_name, load_from_all_views=True) del dashlet["view"] else: raise MKGeneralException( _( "Unable to transform dashlet %d of dashboard %s. " "You will need to migrate it on your own. Definition: %r" ) % (nr, name, escaping.escape_attribute(dashlet)) ) dashlet.setdefault("context", {}) dashlet.setdefault("single_infos", []) # the modification time of builtin dashboards can not be checked as on user specific # dashboards. Set it to 0 to disable the modification chech. dashboard.setdefault("mtime", 0) dashboard.setdefault("show_title", True) if dashboard["title"] is None: dashboard["title"] = _("No title") dashboard["show_title"] = False dashboard.setdefault("single_infos", []) dashboard.setdefault("context", {}) dashboard.setdefault("topic", _("Overview")) dashboard.setdefault("description", dashboard.get("title", ""))