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( _prepare_button_url(p), _prepare_button_url(p), "link", ) ), output, ) if output.endswith(" </A>"): output = output[:-11] return HTML(output)
assert str(escaping.escape_to_html_permissive("<script>")) == "<script>" assert str(escaping.escape_to_html_permissive("<b>")) == "<b>" def test_htmllib_integration(): assert escaping.escape_attribute("") == "" assert escaping.escape_text("") == "" @pytest.mark.parametrize( "inp,out", [ ('">alert(1)', "">alert(1)"), (None, ""), (1, "1"), (HTML('">alert(1)'), '">alert(1)'), (1.1, "1.1"), ("<", "<"), ("'", "'"), (LazyString(str, "'"), "'"), ], ) def test_escape_attribute(inp, out): assert escaping.escape_attribute(inp) == out @pytest.mark.parametrize( "inp,out", [ ("<script>alert(1)</script>", "<script>alert(1)</script>"), ("<h1>abc</h1>", None),
def test_render_help_text(register_builtin_html): assert compare_html( html.render_help(u"äbc"), HTML(u"<div style=\"display:none\" class=\"help\">äbc</div>"))
def render_job_row(cls, job_id, job_status, odd, job_details_back_url=None): html.open_tr(css="data %s0" % odd) # Actions html.open_td(css="job_actions") if job_status.get("may_stop"): html.icon_button( makeactionuri(request, transactions, [(ActionHandler.stop_job_var, job_id)]), _("Stop this job"), "disable_test", ) if job_status.get("may_delete"): html.icon_button( makeactionuri(request, transactions, [(ActionHandler.delete_job_var, job_id)]), _("Delete this job"), "delete", ) html.close_td() # Job ID html.open_td(css="job_id") uri = makeuri_contextless( request, [ ("mode", "background_job_details"), ("back_url", job_details_back_url), ("job_id", job_id), ], filename="wato.py", ) html.a(job_id, href=uri) html.close_td() # Title html.td(job_status.get("title", _("Background Job")), css="job_title") # State html.td( HTMLWriter.render_span(job_status["state"]), css=cls.get_css_for_jobstate(job_status["state"]), ) # Started html.td(cmk.utils.render.date_and_time(job_status["started"]), css="job_started") # Owner html.td(job_status.get("user", _("Unknown user")), css="job_owner") # PID html.td(job_status["pid"] or "", css="job_pid") # Druation html.td(cmk.utils.render.timespan(job_status.get("duration", 0)), css="job_runtime") # Progress info loginfo = job_status.get("loginfo") if loginfo: if job_status.get( "state") == background_job.JobStatusStates.EXCEPTION: html.td(HTML("<br>".join(loginfo["JobException"])), css="job_last_progress") else: progress_text = "" if loginfo["JobProgressUpdate"]: progress_text += "%s" % loginfo["JobProgressUpdate"][-1] html.td(HTML(progress_text), css="job_last_progress") html.td(HTML("<br>".join(loginfo["JobResult"])), css="job_result") else: html.td("", css="job_last_progress") html.td("", css="job_result")
def _show_node(self, tree, show_host, mousecode=None, img_class=None): # Check if we have an assumed state: comparing assumed state (tree[1]) with state (tree[0]) if tree[1] and tree[0] != tree[1]: addclass = ["assumed"] effective_state = tree[1] else: addclass = [] effective_state = tree[0] class_ = [ "content", "state", "state%d" % (effective_state["state"] if effective_state["state"] is not None else -1), ] + addclass html.open_span(class_=class_) html.write_text(self._render_bi_state(effective_state["state"])) html.close_span() if mousecode: if img_class: html.img( src=theme.url("images/tree_closed.svg"), class_=["treeangle", img_class], onclick=mousecode, ) html.open_span(class_=["content", "name"]) icon_name, icon_title = None, None if tree[0]["in_downtime"] == 2: icon_name = "downtime" icon_title = _( "This element is currently in a scheduled downtime.") elif tree[0]["in_downtime"] == 1: # only display host downtime if the service has no own downtime icon_name = "derived_downtime" icon_title = _( "One of the subelements is in a scheduled downtime.") if tree[0]["acknowledged"]: icon_name = "ack" icon_title = _("This problem has been acknowledged.") if not tree[0]["in_service_period"]: icon_name = "outof_serviceperiod" icon_title = _( "This element is currently not in its service period.") if icon_name and icon_title: html.icon(icon_name, title=icon_title, class_=["icon", "bi"]) yield if mousecode: if str(effective_state["state"]) in tree[2].get( "state_messages", {}): html.b(HTML("♦"), class_="bullet") html.write_text(tree[2]["state_messages"][str( effective_state["state"])]) html.close_span() output: HTML = cmk.gui.view_utils.format_plugin_output( effective_state["output"], shall_escape=active_config.escape_plugin_output) if output: output = HTMLWriter.render_b(HTML("♦"), class_="bullet") + output else: output = HTML() html.span(output, class_=["content", "output"])
def test_flash_dont_escape_html(user_id, module_wide_request_context): with login.UserSessionContext(user_id): on_succeeded_login(user_id) # Create and activate session flash(HTML("<script>aaa</script>")) assert get_flashed_messages() == [HTML("<script>aaa</script>")]
def test_render_help_visible(request_context, monkeypatch): monkeypatch.setattr(LoggedInUser, "show_help", property(lambda s: True)) assert user.show_help is True assert compare_html( html.render_help("äbc"), HTML('<div style="display:block" class="help">äbc</div>'))
def render_nbsp() -> HTML: return HTML(" ")
def write_text(self, text: HTMLContent) -> None: """Write text. Highlighting tags such as h2|b|tt|i|br|pre|a|sup|p|li|ul|ol are not escaped.""" self.write_html(HTML(escaping.escape_text(text)))
def render_javascript(code: str) -> HTML: return HTML('<script type="text/javascript">\n%s\n</script>\n' % code)
def render_br() -> HTML: return HTML("<br />")
def test_class_HTML_value(value): assert isinstance(HTML(value).value, six.text_type) assert HTML(HTML(value)) == HTML(value)
def _show_patterns(self): import cmk.gui.logwatch as logwatch collection = SingleRulesetRecursively("logwatch_rules") collection.load() ruleset = collection.get("logwatch_rules") html.h3(_("Logfile patterns")) if ruleset.is_empty(): html.open_div(class_="info") html.write_text( "There are no logfile patterns defined. You may create " 'logfile patterns using the <a href="%s">Rule Editor</a>.' % folder_preserving_link([ ("mode", "edit_ruleset"), ("varname", "logwatch_rules"), ])) html.close_div() # Loop all rules for this ruleset already_matched = False abs_rulenr = 0 for folder, rulenr, rule in ruleset.get_rules(): # Check if this rule applies to the given host/service if self._hostname: service_desc = self._get_service_description( self._hostname, "logwatch", self._item) # If hostname (and maybe filename) try match it rule_matches = rule.matches_host_and_item( Folder.current(), self._hostname, self._item, service_desc) else: # If no host/file given match all rules rule_matches = True with foldable_container( treename="rule", id_=str(abs_rulenr), isopen=True, title=HTML("<b>Rule #%d</b>" % (abs_rulenr + 1)), indent=False, ), table_element("pattern_editor_rule_%d" % abs_rulenr, sortable=False, css="logwatch") as table: abs_rulenr += 1 # TODO: What's this? pattern_list = rule.value if isinstance(pattern_list, dict): pattern_list = pattern_list["reclassify_patterns"] # Each rule can hold no, one or several patterns. Loop them all here for state, pattern, comment in pattern_list: match_class = "" disp_match_txt = HTML("") match_img = "" if rule_matches: # Applies to the given host/service matched = re.search(pattern, self._match_txt) if matched: # Prepare highlighted search txt match_start = matched.start() match_end = matched.end() disp_match_txt = ( escape_to_html(self._match_txt[:match_start]) + HTMLWriter.render_span( self._match_txt[match_start:match_end], class_="match") + escape_to_html(self._match_txt[match_end:])) if not already_matched: # First match match_class = "match first" match_img = "match" match_title = _( "This logfile pattern matches first and will be used for " "defining the state of the given line.") already_matched = True else: # subsequent match match_class = "match" match_img = "imatch" match_title = _( "This logfile pattern matches but another matched first." ) else: match_img = "nmatch" match_title = _( "This logfile pattern does not match the given string." ) else: # rule does not match match_img = "nmatch" match_title = _("The rule conditions do not match.") table.row() table.cell(_("Match")) html.icon("rule%s" % match_img, match_title) cls = ([ "state%d" % logwatch.level_state(state), "fillbackground" ] if match_class == "match first" else []) table.cell(_("State"), HTMLWriter.render_span( logwatch.level_name(state)), css=cls) table.cell(_("Pattern"), HTMLWriter.render_tt(pattern)) table.cell(_("Comment"), comment) table.cell(_("Matched line"), disp_match_txt) table.row(fixed=True) table.cell(colspan=5) edit_url = folder_preserving_link([ ("mode", "edit_rule"), ("varname", "logwatch_rules"), ("rulenr", rulenr), ("item", mk_repr(self._item).decode()), ("rule_folder", folder.path()), ("rule_id", rule.id), ]) html.icon_button(edit_url, _("Edit this rule"), "edit")
assert isinstance(escaping.escape_html_permissive(""), HTML) assert str(escaping.escape_html_permissive("")) == "" assert str(escaping.escape_html_permissive("<script>")) == "<script>" assert str(escaping.escape_html_permissive("<b>")) == "<b>" def test_htmllib_integration(): assert escaping.escape_attribute("") == "" assert escaping.escape_text("") == "" @pytest.mark.parametrize("inp,out", [ ("\">alert(1)", "">alert(1)"), (None, ""), (1, "1"), (HTML("\">alert(1)"), "\">alert(1)"), (1.1, "1.1"), ("<", "<"), ("'", "'"), ]) def test_escape_attribute(inp, out): assert escaping.escape_attribute(inp) == out @pytest.mark.parametrize("inp,out", [ ("">alert(1)", "\">alert(1)"), ("<", "<"), ]) def test_unescape_attribute(inp, out): assert escaping.unescape_attributes(inp) == out
def test_transport_html(self, store, request_context): entry = AuditLogStore.Entry(int(time.time()), None, "user", "action", HTML("Mäss<b>ädsch</b>"), None) store.append(entry) assert list(store.read()) == [entry]
def test_render_element_do_not_escape_html(): tag = render_element("a", HTML("b<script>alert(1)</script>la"), href="ding") assert isinstance(tag, HTML) assert str(tag) == '<a href="ding">b<script>alert(1)</script>la</a>'
def test_class_HTML(): a = "Oneüლ,ᔑ•ﺪ͟͠•ᔐ.ლ" b = "two" c = "Three" d = str('u') A = HTML(a) B = HTML(b) C = HTML(c) D = HTML(d) assert HTML() == HTML('') assert HTML(HTML()) == HTML() # One day we will fix this! assert str(A) == ensure_str(a), str(A) assert "%s" % A == ensure_str(a), "%s" % A assert json.loads(json.dumps(A)) == A assert repr(A) == 'HTML(\"%s\")' % ensure_str(A.value) assert len(B) == len(b) assert str(B) == str(b) # TODO: Investigate assert "1" + B + "2" + C == "1" + b + "2" + c # type: ignore[type-var] assert (A + B) == (a + b) assert HTML().join([A, B]) == A + B assert HTML().join([a, b]) == a + b assert HTML("jo").join([A, B]) == A + "jo" + B assert HTML("jo").join([a, b]) == a + "jo" + b assert ''.join(map(str, [A, B])) == A + B assert isinstance(A, HTML), type(A) # assert isinstance(A, str), type(A) assert not isinstance(A, str), type(A) assert isinstance(u"%s" % A, str), u"%s" % A # One day we will fix this! assert isinstance(u"%s" % A, str), u"%s" % A assert isinstance(A + B, HTML), type(A + B) assert isinstance(HTML('').join([A, B]), HTML) assert isinstance(HTML().join([A, B]), HTML) assert isinstance(HTML('').join([a, b]), HTML) # TODO: Investigate assert isinstance("TEST" + HTML(), HTML) # type: ignore[type-var] assert isinstance(HTML() + "TEST", HTML) # TODO: Investigate assert isinstance("TEST" + HTML() + "TEST", HTML) # type: ignore[type-var] #assert "<div>" + HTML("content") + "</div>" == "<div>content</div>" #assert HTML().join(["<div>", HTML("</br>"), HTML("<input/>"), "</div>"]) ==\ # "<div></br><input/></div>" A += B a += b assert isinstance(A, HTML), A assert A == a, A assert a in A, A assert A.count(a) == 1 assert A.index(a) == 0 # TODO: Investigate type annotation assert isinstance(A[1:3], HTML) # type: ignore[index] assert A[1:3] == a[1:3], A[1:3] # type: ignore[index] assert A == a assert ("%s" % A) == ensure_str(a) assert B + C != C + B assert HTML(A) == A, "%s %s" % (HTML(A), A) assert HTML(a) == A, "%s %s" % (HTML(a), A) # Not supported any more! # assert (A < B) == (a < b), "%s %s" % (A < B, a < b) # assert (A > B) == (a > b) assert A != B assert isinstance(HTML(HTML(A)), HTML) assert isinstance("%s" % HTML(HTML(A)), str) assert isinstance(A, HTML) A += (" JO PICASSO! ") assert isinstance(A, HTML) assert isinstance(A + "TEST", HTML) assert isinstance("TEST%s" % A, str) assert "test" + C == "test" + c # type: ignore[type-var] assert D == d assert "%s" % D == "%s" % d assert isinstance(u"%s" % D, str) assert isinstance("%s" % D, str) E = A + B e = "%s" % E assert E.lstrip(E[0]) == e.lstrip(e[0]) assert E == e assert E.rstrip(E[0]) == e.rstrip(e[0]) assert E == e assert E.strip(E[0]) == e.strip(e[0]) assert E == e
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (C) 2020 tribe29 GmbH - License: GNU General Public License v2 # This file is part of Checkmk (https://checkmk.com). It is subject to the terms and # conditions defined in the file COPYING, which is part of this source code package. import pytest # type: ignore[import] from cmk.gui.utils.html import HTML from cmk.gui.plugins.metrics import html_render @pytest.mark.parametrize( "elements, plain_text, result", [([("first", None), ("second", "https://f.s")], True, "first / second"), ([("first", None), ("second", "https://f.s") ], False, HTML("first / <a href=\"https://f.s\">second</a>")), ([("", None), ("second", "https://f.s")], True, "second")]) def test_render_title_elements(register_builtin_html, elements, plain_text, result): assert html_render.render_title_elements(elements, plain_text=plain_text) == result
def test_render_help_text(request_context): assert compare_html( html.render_help("äbc"), HTML('<div style="display:none" class="help">äbc</div>'))
def hint(self) -> HTML: return HTML()
def show_job_details(cls, job_id, job_status): """Renders the complete job details in a single table with left headers""" html.open_table(class_=["data", "headerleft", "job_details"]) # Static info for left, right in [ (_("ID"), job_id), (_("Title"), job_status.get("title", "")), (_("Started"), cmk.utils.render.date_and_time(job_status["started"])), (_("Owner"), job_status.get("user", "")), ]: html.open_tr() html.th(left) html.td(right) html.close_tr() # Actions html.open_tr() html.th(_("Actions")) html.open_td() if job_status.get("may_stop"): html.icon_button( make_confirm_link( url=makeactionuri(request, transactions, [(ActionHandler.stop_job_var, job_id)]), message=_("Stop job %s%s?") % (job_id, cls._get_extra_info(job_status)), ), _("Stop this job"), "disable_test", ) if job_status.get("may_delete"): html.icon_button( make_confirm_link( url=makeactionuri( request, transactions, [(ActionHandler.delete_job_var, job_id)]), message=_("Delete job %s%s?") % (job_id, cls._get_extra_info(job_status)), ), _("Delete this job"), "delete", ) html.close_td() html.close_tr() # Job state html.open_tr() html.th(_("State")) html.td(job_status["state"], css=cls.get_css_for_jobstate(job_status["state"])) html.close_tr() if job_status["state"] == background_job.JobStatusStates.EXCEPTION: html.open_tr() html.th(_("Acknowledged by")) html.td(job_status.get("acknowledged_by", "")) html.close_tr() # Dynamic data loginfo = job_status.get("loginfo") runtime_info = cmk.utils.render.timespan(job_status.get("duration", 0)) if (job_status["state"] == background_job.JobStatusStates.RUNNING and job_status.get("estimated_duration") is not None): runtime_info += " (%s: %s)" % ( _("estimated duration"), cmk.utils.render.timespan(job_status["estimated_duration"]), ) for left, right in [ (_("Runtime"), runtime_info), (_("PID"), str(job_status["pid"]) or ""), (_("Result"), "<br>".join(loginfo["JobResult"])), ]: if right is None: continue html.open_tr() html.th(left) html.td(HTML(right)) html.close_tr() # Exceptions exceptions = loginfo["JobException"] if exceptions: html.open_tr() html.th(_("Exceptions")) html.open_td() if exceptions and "logfile_path" in job_status: exceptions.append( _("More information can be found in %s") % job_status["logfile_path"]) html.open_div(class_="log_output", id_="exception_log") html.pre("\n".join(exceptions)) html.close_div() html.close_td() html.close_tr() # Progress Update html.open_tr() html.th(_("Progress info")) html.open_td() html.open_div(class_="log_output", style="height: 400px;", id_="progress_log") html.pre(HTML("\n").join(loginfo["JobProgressUpdate"])) html.pre(HTML("\n".join(loginfo["JobResult"]))) html.close_div() html.close_td() html.close_tr() html.close_table()
def render_end_tag(tag_name: HTMLTagName) -> HTML: return HTML("</%s>" % (tag_name))
def render(self) -> HTML: with output_funnel.plugged(): self._show_tree() return HTML(output_funnel.drain())
# conditions defined in the file COPYING, which is part of this source code package. import pytest from cmk.gui.utils.html import HTML from cmk.gui.view_utils import format_plugin_output @pytest.mark.parametrize( "args, expected", [ pytest.param( '"http://127.0.0.1:5000/heute/che\'ck_mk"', HTML( """"<a href="http://127.0.0.1:5000/heute/che'ck_mk" title="http://127.0.0.1:5000/heute/che'ck_mk" onfocus="if (this.blur) this.blur();" target=''><img src="themes/facelift/images/icon_link.png" class="icon iconbutton png" /></a>""" ), id="single quote in url", ), pytest.param( '"http://127.0.0.1:5000/heute/check_mk\'"', HTML( """"<a href="http://127.0.0.1:5000/heute/check_mk'" title="http://127.0.0.1:5000/heute/check_mk'" onfocus="if (this.blur) this.blur();" target=''><img src="themes/facelift/images/icon_link.png" class="icon iconbutton png" /></a>""" ), id="trailing quote in url", ), pytest.param( "'http://127.0.0.1:5000/heute/check_mk'", HTML( """'<a href="http://127.0.0.1:5000/heute/check_mk" title="http://127.0.0.1:5000/heute/check_mk" onfocus="if (this.blur) this.blur();" target=''><img src="themes/facelift/images/icon_link.png" class="icon iconbutton png" /></a>""" ),
def _gen_leaf(self, tree, height, show_host): with output_funnel.plugged(): self._show_leaf(tree, show_host) content = HTML(output_funnel.drain()) return [(content, height, [])]
def escape_html(value: str) -> HTML: """Escape HTML and return as HTML object""" return HTML(html_escape(value))
def page(self): html.open_div(id_="ldap") html.open_table() html.open_tr() html.open_td() html.begin_form("connection", method="POST") html.prevent_password_auto_completion() vs = self._valuespec() vs.render_input("connection", self._connection_cfg) vs.set_focus("connection") html.hidden_fields() html.end_form() html.close_td() html.open_td(style="padding-left:10px;vertical-align:top") html.h2(_("Diagnostics")) if not request.var("_test") or not self._connection_id: html.show_message( HTML("<p>%s</p><p>%s</p>" % ( _("You can verify the single parts of your ldap configuration using this " "dialog. Simply make your configuration in the form on the left side and " 'hit the "Save & Test" button to execute the tests. After ' "the page reload, you should see the results of the test here." ), _("If you need help during configuration or experience problems, please refer " 'to the <a target="_blank" ' 'href="https://checkmk.com/checkmk_multisite_ldap_integration.html">' "LDAP Documentation</a>."), ))) else: connection = userdb.get_connection(self._connection_id) assert isinstance(connection, LDAPUserConnector) for address in connection.servers(): html.h3("%s: %s" % (_("Server"), address)) with table_element("test", searchable=False) as table: for title, test_func in self._tests(): table.row() try: state, msg = test_func(connection, address) except Exception as e: state = False msg = _("Exception: %s") % e logger.exception("error testing LDAP %s for %s", title, address) if state: img = html.render_icon("success", _("Success")) else: img = html.render_icon("failed", _("Failed")) table.cell(_("Test"), title) table.cell(_("State"), img) table.cell(_("Details"), msg) connection.disconnect() html.close_td() html.close_tr() html.close_table() html.close_div()
def escape_html_permissive(value: str) -> HTML: """Escape HTML in permissive mode (keep simple markup tags) and return as HTML object""" return HTML(escape_text(value))
def test_class_HTML_value(value): assert isinstance(HTML(value).value, str) assert HTML(HTML(value)) == HTML(value)
def paint_aggr_hosts(row, link_to_view): h = [] for site, host in row["aggr_hosts"]: url = makeuri(request, [("view_name", link_to_view), ("site", site), ("host", host)]) h.append(HTMLWriter.render_a(host, url)) return "", HTML(" ").join(h)