From 004f794c759620793f1b7a49ad17f1dcaf687e65 Mon Sep 17 00:00:00 2001 From: Adriano Date: Sat, 7 Feb 2026 19:31:49 +0100 Subject: [PATCH] fix: Alpine.js JSON parsing error in x-data HTML attributes Add tojson_attr Jinja2 filter that escapes double quotes to " for safe embedding in HTML attributes. The browser decodes entities before Alpine.js evaluates, so JSON parses correctly. Replaces |tojson with |tojson_attr in x-data attributes (select_recipe, recipe_list, base flash messages). Script tag usages are unaffected. Co-Authored-By: Claude Opus 4.6 --- client/app.py | 20 ++++++++++++++++++++ client/templates/base.html | 2 +- client/templates/maker/recipe_list.html | 2 +- client/templates/measure/select_recipe.html | 2 +- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/client/app.py b/client/app.py index 48ceb01..3b79869 100644 --- a/client/app.py +++ b/client/app.py @@ -1,9 +1,11 @@ """TieMeasureFlow Client - Flask Entry Point.""" +import json import os from flask import Flask, redirect, url_for, session, request from flask_babel import Babel from flask_wtf.csrf import CSRFProtect +from markupsafe import Markup from config import Config @@ -55,6 +57,24 @@ def create_app() -> Flask: session["language"] = lang return redirect(request.referrer or url_for("auth.login")) + @app.template_filter("tojson_attr") + def tojson_attr_filter(value): + """JSON encode safe for HTML attributes (x-data, etc.). + + Unlike |tojson, this escapes double quotes to " so the output + can be safely embedded inside double-quoted HTML attributes. + The browser decodes the entities before Alpine.js evaluates them. + """ + rv = json.dumps(value, ensure_ascii=False) + rv = ( + rv.replace("&", "\\u0026") + .replace("<", "\\u003c") + .replace(">", "\\u003e") + .replace("'", "\\u0027") + .replace('"', """) + ) + return Markup(rv) + @app.context_processor def inject_globals(): """Inject global variables into all templates.""" diff --git a/client/templates/base.html b/client/templates/base.html index e6f21a6..fcf45fd 100644 --- a/client/templates/base.html +++ b/client/templates/base.html @@ -90,7 +90,7 @@ {% with messages = get_flashed_messages(with_categories=true) %} {% if messages %}
{% for category, message in messages %}