diff --git a/static/panels.js b/static/panels.js
index 8ebf44d6..ca7e6733 100644
--- a/static/panels.js
+++ b/static/panels.js
@@ -5569,7 +5569,7 @@ async function loadProvidersPanel(){
list.appendChild(_buildProviderCard(p));
}
}catch(e){
- list.innerHTML='
Failed to load providers: '+e.message+'
';
+ list.innerHTML='Failed to load providers: '+esc(e.message||String(e))+'
';
}
}
diff --git a/static/ui.js b/static/ui.js
index 0c716cb8..f270909f 100644
--- a/static/ui.js
+++ b/static/ui.js
@@ -1206,7 +1206,7 @@ function renderModelDropdown(){
}
}
const badgeHtml=m.badge?`${esc(badgeLabel)}`:'';
- row.innerHTML=`${esc(modelName)}${badgeHtml}
${m.id}`;
+ row.innerHTML=`${esc(modelName)}${badgeHtml}
${esc(m.id)}`;
row.onclick=()=>selectModelFromDropdown(m.value);
dd.appendChild(row);
}
@@ -1234,7 +1234,7 @@ function renderModelDropdown(){
const badgeHtml=m.badge?`${esc(m.badge.label||'Configured')}`:'';
// Inline provider chip on every row that has a group (#1425)
const providerChip=m.group?`${esc(m.group)}`:'';
- row.innerHTML=`${m.name}${badgeHtml}${providerChip}
${m.id}`;
+ row.innerHTML=`${esc(m.name)}${badgeHtml}${providerChip}
${esc(m.id)}`;
row.onclick=()=>selectModelFromDropdown(m.value);
dd.appendChild(row);
}
diff --git a/tests/test_model_picker_escaping.py b/tests/test_model_picker_escaping.py
new file mode 100644
index 00000000..76fcd44e
--- /dev/null
+++ b/tests/test_model_picker_escaping.py
@@ -0,0 +1,20 @@
+from pathlib import Path
+
+
+ROOT = Path(__file__).resolve().parents[1]
+
+
+def test_model_picker_escapes_provider_supplied_model_labels():
+ src = (ROOT / "static" / "ui.js").read_text()
+
+ assert '${esc(m.id)}' in src
+ assert '${esc(m.name)}' in src
+ assert '${m.id}' not in src
+ assert '${m.name}' not in src
+
+
+def test_providers_panel_escapes_load_error_text():
+ src = (ROOT / "static" / "panels.js").read_text()
+
+ assert "Failed to load providers: '+esc(e.message||String(e))+'" in src
+ assert "Failed to load providers: '+e.message+'" not in src