diff --git a/changedetectionio/blueprint/ui/views.py b/changedetectionio/blueprint/ui/views.py index 1cc54d6d0..032b30fc9 100644 --- a/changedetectionio/blueprint/ui/views.py +++ b/changedetectionio/blueprint/ui/views.py @@ -38,20 +38,26 @@ def construct_blueprint(datastore: ChangeDetectionStore, update_q, queuedWatchMe # Step 1: Escape everything like Jinja2 would (this makes it XSS-safe) escaped_content = escape(str(content)) - # Step 2: Simple regex to unescape only our exact diff spans - # Unescape outer span opening tags with exact styles (with title attribute) - # This matches the styles used in DIFF_HTML_LABEL_REMOVED, DIFF_HTML_LABEL_ADDED, etc. + # Step 2: Unescape only our exact diff spans generated by apply_html_color_to_body() + # Pattern matches the exact structure: + # + + # Unescape outer span opening tags with full attributes (role, aria-label, title) + # Matches removed/added/changed/changed_into spans result = re.sub( - rf'<span style="({REMOVED_STYLE}|{ADDED_STYLE})" title="([A-Za-z0-9]+)">', - r'', + rf'<span style="({re.escape(REMOVED_STYLE)}|{re.escape(ADDED_STYLE)})" ' + rf'role="(deletion|insertion|note)" ' + rf'aria-label="([^&]+?)" ' + rf'title="([^&]+?)">', + r'', str(escaped_content), flags=re.IGNORECASE ) - # Unescape inner span opening tags (without title attribute) + # Unescape inner span opening tags (without additional attributes) # This matches the darker background styles for changed parts within lines result = re.sub( - rf'<span style="({REMOVED_INNER_STYLE}|{ADDED_INNER_STYLE})">', + rf'<span style="({re.escape(REMOVED_INNER_STYLE)}|{re.escape(ADDED_INNER_STYLE)})">', r'', result, flags=re.IGNORECASE @@ -65,6 +71,8 @@ def construct_blueprint(datastore: ChangeDetectionStore, update_q, queuedWatchMe for _ in range(min(open_count, close_count)): result = result.replace('</span>', '', 1) + # Not necessary because the CSS/HTML will lay it out by linefeed + result = result.replace(CUSTOM_LINEBREAK_PLACEHOLDER, '') return Markup(result) @views_blueprint.route("/preview/", methods=['GET']) @@ -260,7 +268,6 @@ def construct_blueprint(datastore: ChangeDetectionStore, update_q, queuedWatchMe word_diff=diff_prefs.get('diff_type') == 'diffWords', context_lines=5 ) - content = content.replace(CUSTOM_LINEBREAK_PLACEHOLDER, '
') content = apply_html_color_to_body(n_body=content) output = render_template("diff.html",