Skip to content

fix(frontend): render string dialog content as text, not HTML (XSS) - #655

Merged
lgelauff merged 1 commit into
masterfrom
fix/dialog-content-text
Oct 6, 2026
Merged

lgelauff merged 1 commit into
masterfrom
fix/dialog-content-text

Conversation

@lgelauff

@lgelauff lgelauff commented Oct 6, 2026

Copy link
Copy Markdown
Collaborator

Security fix (XSS, low–medium severity). dialogService rendered string content with innerHTML. After a round import, RoundNew.vue shows the backend's warnings (e.g. "not found" file names) and the disqualified list as string content. Those names come from user-provided CSV / Google Sheet / gist imports, so a list entry like <img src=x onerror=alert(1)> executed script in the importing coordinator's logged-in session. Category imports are not affected, because Commons titles cannot contain < or >. Exploiting this requires a coordinator to import a malicious list. No issue was filed for this.

Change: a one-line fix. String content is now passed as a text child (h('div', content)), so markup shows literally. Nothing else changes visually: the dialog body has no white-space rule, so the \n separators were already collapsed under innerHTML. All dialogService().show callers were checked: the only other string caller (the round delete confirmation) is plain text, and the rest pass components.

Testing

  • eslint src/ and npm run build pass; backend tests pass. (npm run lint fails on master independently: --ignore-path .gitignore points at a missing frontend/.gitignore.)
  • Render check with vue/server-renderer: the payload is emitted escaped (&lt;img …&gt;) instead of as a live element. There is no frontend unit-test runner yet.
  • Suggested check on beta: import a CSV/gist with a file name containing <img src=x onerror=alert(1)>. Expect literal text and no alert.

Not in this PR: VoteEdit.vue:17 renders round.name (coordinator-set) through v-html, which is the same class of issue (coordinator → juror). A separate PR will follow. Showing the warnings' line breaks, wrapping long names and moving the dialog strings to i18n will also follow separately.

Written with Claude (Claude Code) on behalf of @lgelauff.

🤖 Generated with Claude Code

dialogService rendered string `content` via innerHTML. RoundNew passes
the import warnings as a string, and those contain file names taken
from user-provided CSVs / Google Sheets / gists, so a name such as
`<img src=x onerror=...>` ran script in the importing coordinator's
session. Category imports were not affected (Commons titles cannot
contain < or >).

String content is now a text child. Display is otherwise unchanged:
the dialog body has no white-space rule, so the '\n' separators were
already collapsed under innerHTML. The only other string caller, the
round delete confirmation, is plain text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@lgelauff
lgelauff merged commit 13fe59f into master Oct 6, 2026
4 checks passed
@lgelauff
lgelauff deleted the fix/dialog-content-text branch October 6, 2026 08:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant