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 <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,11 @@
|
|||||||
"""TieMeasureFlow Client - Flask Entry Point."""
|
"""TieMeasureFlow Client - Flask Entry Point."""
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from flask import Flask, redirect, url_for, session, request
|
from flask import Flask, redirect, url_for, session, request
|
||||||
from flask_babel import Babel
|
from flask_babel import Babel
|
||||||
from flask_wtf.csrf import CSRFProtect
|
from flask_wtf.csrf import CSRFProtect
|
||||||
|
from markupsafe import Markup
|
||||||
|
|
||||||
from config import Config
|
from config import Config
|
||||||
|
|
||||||
@@ -55,6 +57,24 @@ def create_app() -> Flask:
|
|||||||
session["language"] = lang
|
session["language"] = lang
|
||||||
return redirect(request.referrer or url_for("auth.login"))
|
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
|
@app.context_processor
|
||||||
def inject_globals():
|
def inject_globals():
|
||||||
"""Inject global variables into all templates."""
|
"""Inject global variables into all templates."""
|
||||||
|
|||||||
@@ -90,7 +90,7 @@
|
|||||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||||
{% if messages %}
|
{% if messages %}
|
||||||
<div class="fixed top-16 right-4 z-50 flex flex-col gap-2 max-w-md w-full"
|
<div class="fixed top-16 right-4 z-50 flex flex-col gap-2 max-w-md w-full"
|
||||||
x-data="{ messages: {{ messages|tojson }} }"
|
x-data="{ messages: {{ messages|tojson_attr }} }"
|
||||||
x-init="setTimeout(() => { $el.remove() }, 8000)">
|
x-init="setTimeout(() => { $el.remove() }, 8000)">
|
||||||
{% for category, message in messages %}
|
{% for category, message in messages %}
|
||||||
<div x-data="{ show: true }"
|
<div x-data="{ show: true }"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container mx-auto px-4 sm:px-6 lg:px-8 py-8 max-w-6xl"
|
<div class="container mx-auto px-4 sm:px-6 lg:px-8 py-8 max-w-6xl"
|
||||||
x-data="{
|
x-data="{
|
||||||
recipes: {{ recipes|tojson }},
|
recipes: {{ recipes|tojson_attr }},
|
||||||
search: '{{ search or '' }}',
|
search: '{{ search or '' }}',
|
||||||
filter: 'all',
|
filter: 'all',
|
||||||
deleteModal: false,
|
deleteModal: false,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container mx-auto px-4 sm:px-6 lg:px-8 py-8 max-w-7xl"
|
<div class="container mx-auto px-4 sm:px-6 lg:px-8 py-8 max-w-7xl"
|
||||||
x-data="{
|
x-data="{
|
||||||
recipes: {{ recipes|tojson }},
|
recipes: {{ recipes|tojson_attr }},
|
||||||
search: '{{ auto_recipe_code }}',
|
search: '{{ auto_recipe_code }}',
|
||||||
lot_number: '{{ auto_lot }}',
|
lot_number: '{{ auto_lot }}',
|
||||||
serial_number: '{{ auto_serial }}',
|
serial_number: '{{ auto_serial }}',
|
||||||
|
|||||||
Reference in New Issue
Block a user