[Security] A wiki page can load an image from any outside address a member types #1138

Closed
opened 2026-10-12 16:49:08 +00:00 by claude-bot · 1 comment
Collaborator

Found while reviewing PR #1136 (#1135), 2026-10-12. Not introduced by it, and not changed by it.

What happens

A wiki page's text may contain a Markdown image with any http(s) address: ![](https://example.org/p.png).

  • webapp/frontend/src/wiki/wikiText.js:319: SAFE_SRC accepts https?://.
  • webapp/frontend/src/wiki/WikiText.jsx:95 emits <img src> for it.
  • nginx.conf:12: the Content Security Policy's img-src is 'self' data: https:, so the browser fetches it.

Anyone who may edit or suggest a page in a campaign (players, under most settings) can therefore place an image that is fetched by every reader's browser when they open the page. The outside server learns who opened the page, when, and from which address and browser (a tracking pixel). It is a request to an outside host only: it carries no Quest Board cookies, and img cannot read a response, so it does not expose data from the app. A same-origin address (![](/api/…)) is also fetched with the reader's session, which matters only if some GET route changes state.

The same renderer draws web links ([text](url)); those need a click and are ordinary for a wiki.

Options

  1. Only the app's own uploads: SAFE_SRC and img-src limited to 'self' (and data: if still needed). An outside image in existing text draws as a link to it, or as its alt text. Pages that hotlink art today would lose the picture until it is uploaded.
  2. Keep outside images but fetch them through the server (a caching proxy with size and type limits), so readers' browsers never contact the outside host. More work, and an SSRF surface to guard.
  3. Keep as is, documented: a campaign's members are trusted with each other's address.

Recaps, notes and facts do not have this problem: since PR #1136 they draw only wiki links, and everything else as the characters typed.

Also to check under this issue: whether any GET route under /api changes state.

Found while reviewing PR #1136 (#1135), 2026-10-12. Not introduced by it, and not changed by it. ## What happens A wiki page's text may contain a Markdown image with any `http(s)` address: `![](https://example.org/p.png)`. - `webapp/frontend/src/wiki/wikiText.js:319`: `SAFE_SRC` accepts `https?://`. - `webapp/frontend/src/wiki/WikiText.jsx:95` emits `<img src>` for it. - `nginx.conf:12`: the Content Security Policy's `img-src` is `'self' data: https:`, so the browser fetches it. Anyone who may edit or suggest a page in a campaign (players, under most settings) can therefore place an image that is fetched by every reader's browser when they open the page. The outside server learns who opened the page, when, and from which address and browser (a tracking pixel). It is a request to an outside host only: it carries no Quest Board cookies, and `img` cannot read a response, so it does not expose data from the app. A same-origin address (`![](/api/…)`) is also fetched with the reader's session, which matters only if some GET route changes state. The same renderer draws web links (`[text](url)`); those need a click and are ordinary for a wiki. ## Options 1. Only the app's own uploads: `SAFE_SRC` and `img-src` limited to `'self'` (and `data:` if still needed). An outside image in existing text draws as a link to it, or as its alt text. Pages that hotlink art today would lose the picture until it is uploaded. 2. Keep outside images but fetch them through the server (a caching proxy with size and type limits), so readers' browsers never contact the outside host. More work, and an SSRF surface to guard. 3. Keep as is, documented: a campaign's members are trusted with each other's address. Recaps, notes and facts do not have this problem: since PR #1136 they draw only wiki links, and everything else as the characters typed. Also to check under this issue: whether any GET route under `/api` changes state.
Author
Collaborator

Owner's decision (Q174, 2026-10-12): option 1, in 4.5.1. Uploaded pictures only; an outside picture in a page's text shows as a link to it instead. It does not hold 4.5.0.

To build for 4.5.1: SAFE_SRC limited to the app's own media routes, the renderer drawing an outside image as a link (its alt text, or the address, as the label), img-src in nginx.conf narrowed to 'self' (keeping data: only if something still needs it: check the map tiles, avatars and the PWA icons), the editor's picture control and Help text saying so, a test with the hostile inputs used in #1136, and the check on whether any GET route under /api changes state.

**Owner's decision (Q174, 2026-10-12): option 1, in 4.5.1.** Uploaded pictures only; an outside picture in a page's text shows as a link to it instead. It does not hold 4.5.0. To build for 4.5.1: `SAFE_SRC` limited to the app's own media routes, the renderer drawing an outside image as a link (its alt text, or the address, as the label), `img-src` in `nginx.conf` narrowed to `'self'` (keeping `data:` only if something still needs it: check the map tiles, avatars and the PWA icons), the editor's picture control and Help text saying so, a test with the hostile inputs used in #1136, and the check on whether any GET route under `/api` changes state.
Sign in to join this conversation.
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
rbrooks/Quest-Board#1138
No description provided.