[frontend/security] XSS via unescaped innerHTML with user-controlled character names in chat.js #788

Closed
opened 2026-06-03 00:37:00 +02:00 by sleepy · 0 comments
Owner

Finding

chat.js lines 810-814 inject the character name into the DOM via innerHTML without HTML escaping:

var roleLabel = _shortModel(modelName);
var _charNameInit = presetsModule.getCharacterName ? presetsModule.getCharacterName() : "";
if (_charNameInit) roleLabel = _charNameInit;
// ...
holder.innerHTML = `<div class="role">${roleLabel} ...</div><div class="body"></div>`;

getCharacterName() in presets.js returns custom.character_name — a user-configured string stored in preset settings. If a user sets their character name to <img src=x onerror=alert(1)>, it executes as HTML.

Same pattern at line 3026 (string concatenation variant) and line 3828.

Impact

Stored XSS — any user who can create/edit a preset can inject arbitrary JavaScript that executes for anyone viewing chat messages rendered with that character name. In a multi-user deployment, this is a privilege escalation vector.

Evidence

  • presets.js:874: return custom.character_name || ""; — no sanitization
  • chat.js:814: holder.innerHTML = \...${roleLabel}...`;` — no escaping
  • esc() function exists in ui.js but is not applied to roleLabel

Recommendation

Apply esc() to roleLabel before inserting into innerHTML, or use textContent for the role label element.

## Finding `chat.js` lines 810-814 inject the character name into the DOM via `innerHTML` without HTML escaping: ```javascript var roleLabel = _shortModel(modelName); var _charNameInit = presetsModule.getCharacterName ? presetsModule.getCharacterName() : ""; if (_charNameInit) roleLabel = _charNameInit; // ... holder.innerHTML = `<div class="role">${roleLabel} ...</div><div class="body"></div>`; ``` `getCharacterName()` in `presets.js` returns `custom.character_name` — a user-configured string stored in preset settings. If a user sets their character name to `<img src=x onerror=alert(1)>`, it executes as HTML. Same pattern at line 3026 (string concatenation variant) and line 3828. ## Impact **Stored XSS** — any user who can create/edit a preset can inject arbitrary JavaScript that executes for anyone viewing chat messages rendered with that character name. In a multi-user deployment, this is a privilege escalation vector. ## Evidence - `presets.js:874`: `return custom.character_name || "";` — no sanitization - `chat.js:814`: `holder.innerHTML = \`...${roleLabel}...\`;` — no escaping - `esc()` function exists in `ui.js` but is not applied to `roleLabel` ## Recommendation Apply `esc()` to roleLabel before inserting into innerHTML, or use `textContent` for the role label element.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
sleepy/odysseus#788
No description provided.