From 0126044ecbe1a1095274eed8873d318b644c47ca Mon Sep 17 00:00:00 2001 From: Nathan Esquenazi Date: Wed, 8 Apr 2026 19:07:00 -0700 Subject: [PATCH] fix: stray } in message row HTML + JS-escape login locale strings Agent review findings from PR #179: 1. static/ui.js line 542: extra } in ternary produced malformed HTML in message bubble div (''}} instead of ''}). Caused a literal } character to appear in the DOM. 2. api/routes.py: LOGIN_INVALID_PW and LOGIN_CONN_FAILED were inserted into JS string context without JS-string escaping. Added backslash escaping for ' and \ characters. Currently safe because locale values are hardcoded, but this prevents breakage if custom locale strings contain single quotes. Co-Authored-By: Claude Opus 4.6 (1M context) --- api/routes.py | 4 ++-- static/ui.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/routes.py b/api/routes.py index 6cc1cf5..e7b6ce6 100644 --- a/api/routes.py +++ b/api/routes.py @@ -166,8 +166,8 @@ def handle_get(handler, parsed) -> bool: .replace('{{LOGIN_SUBTITLE}}', _html.escape(_login_strings['subtitle'])) .replace('{{LOGIN_PLACEHOLDER}}', _html.escape(_login_strings['placeholder'])) .replace('{{LOGIN_BTN}}', _html.escape(_login_strings['btn'])) - .replace('{{LOGIN_INVALID_PW}}', _login_strings['invalid_pw']) # JS string, escape carefully - .replace('{{LOGIN_CONN_FAILED}}', _login_strings['conn_failed']) + .replace('{{LOGIN_INVALID_PW}}', _login_strings['invalid_pw'].replace('\\','\\\\').replace("'","\\'")) + .replace('{{LOGIN_CONN_FAILED}}', _login_strings['conn_failed'].replace('\\','\\\\').replace("'","\\'")) ) return t(handler, _page, content_type='text/html; charset=utf-8') diff --git a/static/ui.js b/static/ui.js index a3ea646..9f2b76e 100644 --- a/static/ui.js +++ b/static/ui.js @@ -539,7 +539,7 @@ function renderMessages(){ const tsVal=m._ts||m.timestamp; const tsTitle=tsVal?new Date(tsVal*1000).toLocaleString():''; const _bn=window._botName||'Hermes'; - row.innerHTML=`
${isUser?'Y':esc(_bn.charAt(0).toUpperCase())}
${isUser?t('you'):esc(_bn)}${tsTitle?`${new Date(tsVal*1000).toLocaleTimeString([],{hour:'2-digit',minute:'2-digit'})}`:''}${editBtn}${retryBtn}
${filesHtml}
${bodyHtml}
`; + row.innerHTML=`
${isUser?'Y':esc(_bn.charAt(0).toUpperCase())}
${isUser?t('you'):esc(_bn)}${tsTitle?`${new Date(tsVal*1000).toLocaleTimeString([],{hour:'2-digit',minute:'2-digit'})}`:''}${editBtn}${retryBtn}
${filesHtml}
${bodyHtml}
`; row.dataset.rawText = String(content).trim(); inner.appendChild(row); }