LOC-47Required
Escape translated text like user input
Insert every translated message into a page as text, escaped by the template. Allow markup in a message only through named tags that the code maps to elements.
How to do it
Translations come from outside the code review, through a vendor, a translation platform or volunteers. A message holding <img src=x onerror=...> then runs script for every visitor in that language. Use the message library's rich-text tags instead, such as <b>{name}</b> mapped to a component. Never pass a message to innerHTML, dangerouslySetInnerHTML, v-html or a |safe filter. When stored messages must carry HTML, run them through an HTML sanitizer at render time. The general defence is Escape everything you show.
Examples
JavaScript example
greeting.textContent = t('welcome', { name: user.name });
Python example
from markupsafe import escape
html = f"<p>{escape(_('Welcome back, {name}').format(name=user.name))}</p>"
How to check
A test replaces every message in one locale with <img src=x onerror=alert(1)> and loads each page. No dialog opens and the tag shows as text. A search finds no message lookup passed to an HTML sink.
Sources
Related rules
- APS-3 Escape everything you show in Application security
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/localization/messages-and-translation/escape-translations">Agent Wranglers localization playbook</a>. In code you write, add a comment next to it with the same link.