Ajoute 4 mécanismes moteur manquants pour le thème sécurité incendie : étiquette, carte, image SVG inline, bouton avec pièce jointe

Contenu et mécanisme uniquement, aucun style ajouté (voir consigne du
24/09/2026) : deux nouveaux kinds de contenu (badge/carte, rendu en
div brutes sans CSS), un mode SVG inline pour l'image (svg_markup,
nettoyé par un nouveau sanitizer allow-list avant chaque rendu) et un
fichier téléchargeable joignable à un bouton (upload/download routes,
stockage sous db.support_dir). Le futur système de templates portera
l'habillage visuel de ces éléments.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
william
2026-09-24 08:12:29 +02:00
co-authored by Claude Sonnet 5
parent e7c6ed7159
commit a34bcf4159
15 changed files with 668 additions and 18 deletions
+122
View File
@@ -225,3 +225,125 @@ def test_liste_puces_with_no_items_renders_an_empty_list(tmp_support_slug_cleanu
html = doc_engine.render_document(doc_engine.list_document_elements(slug, page_id))
assert html == f'<ul class="docList" data-element-id="{element_id}" data-kind="liste_puces"></ul>'
def test_badge_default_attributes_and_render(tmp_support_slug_cleanup: Any) -> None:
slug, page_id = _new_support(tmp_support_slug_cleanup)
element_id = doc_engine.add_document_element(slug, "badge", page_id=page_id)
element = doc_engine.get_document_element(slug, element_id)
assert element is not None
assert element["attributes"] == {"content": "Étiquette"}
html = doc_engine.render_document(doc_engine.list_document_elements(slug, page_id))
assert html == f'<div class="docBadge" data-element-id="{element_id}" data-kind="badge">Étiquette</div>'
def test_badge_escapes_content(tmp_support_slug_cleanup: Any) -> None:
slug, page_id = _new_support(tmp_support_slug_cleanup)
element_id = doc_engine.add_document_element(slug, "badge", page_id=page_id)
doc_engine.update_document_element_attributes(slug, element_id, {"content": "<script>alert(1)</script>"})
html = doc_engine.render_document(doc_engine.list_document_elements(slug, page_id))
assert "<script>" not in html
assert "&lt;script&gt;" in html
def test_carte_default_attributes_and_render(tmp_support_slug_cleanup: Any) -> None:
slug, page_id = _new_support(tmp_support_slug_cleanup)
element_id = doc_engine.add_document_element(slug, "carte", page_id=page_id)
element = doc_engine.get_document_element(slug, element_id)
assert element is not None
assert element["attributes"] == {
"label": "A",
"title": "Titre de la carte",
"description": "Description de la carte.",
}
html = doc_engine.render_document(doc_engine.list_document_elements(slug, page_id))
assert html == (
f'<div class="docCard" data-element-id="{element_id}" data-kind="carte">'
f'<div class="docCardLabel">A</div>'
f'<div class="docCardTitle">Titre de la carte</div>'
f'<div class="docCardDescription">Description de la carte.</div>'
f"</div>"
)
def test_carte_escapes_all_fields(tmp_support_slug_cleanup: Any) -> None:
slug, page_id = _new_support(tmp_support_slug_cleanup)
element_id = doc_engine.add_document_element(slug, "carte", page_id=page_id)
doc_engine.update_document_element_attributes(
slug, element_id, {"label": "<b>", "title": "<i>", "description": "<script>alert(1)</script>"}
)
html = doc_engine.render_document(doc_engine.list_document_elements(slug, page_id))
assert "<script>" not in html
assert "<b>" not in html
assert "<i>" not in html
def test_image_default_attributes_include_empty_svg_markup(tmp_support_slug_cleanup: Any) -> None:
slug, page_id = _new_support(tmp_support_slug_cleanup)
element_id = doc_engine.add_document_element(slug, "image", page_id=page_id)
element = doc_engine.get_document_element(slug, element_id)
assert element is not None
assert element["attributes"] == {"src": "", "alt": "", "svg_markup": ""}
def test_image_with_svg_markup_takes_priority_over_src(tmp_support_slug_cleanup: Any) -> None:
slug, page_id = _new_support(tmp_support_slug_cleanup)
element_id = doc_engine.add_document_element(slug, "image", page_id=page_id)
doc_engine.update_document_element_attributes(
slug,
element_id,
{"src": "https://exemple.test/photo.png", "alt": "", "svg_markup": '<svg><circle cx="5" cy="5" r="3"/></svg>'},
)
html = doc_engine.render_document(doc_engine.list_document_elements(slug, page_id))
assert "<img" not in html
assert f'<div class="docImage" data-element-id="{element_id}" data-kind="image">' in html
assert '<circle cx="5" cy="5" r="3">' in html or '<circle cx="5" cy="5" r="3"/>' in html
def test_image_svg_markup_strips_script_tag(tmp_support_slug_cleanup: Any) -> None:
slug, page_id = _new_support(tmp_support_slug_cleanup)
element_id = doc_engine.add_document_element(slug, "image", page_id=page_id)
malicious = '<svg onload="alert(1)"><script>alert(2)</script><circle cx="1" cy="1" r="1" onclick="alert(3)"/></svg>'
doc_engine.update_document_element_attributes(slug, element_id, {"src": "", "alt": "", "svg_markup": malicious})
html = doc_engine.render_document(doc_engine.list_document_elements(slug, page_id))
assert "<script>" not in html
assert "onload" not in html
assert "onclick" not in html
assert "alert(" not in html
def test_bouton_default_attributes_include_empty_attachment_fields(tmp_support_slug_cleanup: Any) -> None:
slug, page_id = _new_support(tmp_support_slug_cleanup)
element_id = doc_engine.add_document_element(slug, "bouton", page_id=page_id)
element = doc_engine.get_document_element(slug, element_id)
assert element is not None
assert element["attributes"] == {
"label": "Bouton",
"target": "",
"attachment_stored_name": "",
"attachment_filename": "",
}
def test_bouton_with_attachment_filename_renders_data_attribute(tmp_support_slug_cleanup: Any) -> None:
slug, page_id = _new_support(tmp_support_slug_cleanup)
element_id = doc_engine.add_document_element(slug, "bouton", page_id=page_id)
doc_engine.update_document_element_attributes(
slug,
element_id,
{
"label": "Télécharger",
"target": "",
"attachment_stored_name": "abc123.pdf",
"attachment_filename": "fiche-consignes.pdf",
},
)
html = doc_engine.render_document(doc_engine.list_document_elements(slug, page_id))
assert 'data-attachment-filename="fiche-consignes.pdf"' in html