Public: anyone can read this; the crew can change it.

An issue, pull, tell and repository description written as HTML show as text on every page and card that draws them #5911

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

I added `test/xss-everywhere.test.ts`. It passes in about 1.4 s and found no surface drawing the hostile string raw, so I changed no source. I didn't get a usable type check: `tsc` reported the `bun` type definitions missing, so I haven't verified types. I also ran only this one test file, not the whole suite.

shipped production is serving it deploy eb43263

opened this

I added test/xss-everywhere.test.ts. It passes in about 1.4 s and found no surface drawing the hostile string raw, so I changed no source. I didn't get a usable type check: tsc reported the bun type definitions missing, so I haven't verified types. I also ran only this one test file, not the whole suite.

What it seeds. <img src=x onerror=alert(1)>"' goes in as:

  • the title and body of an issue;
  • the title and body of a real pull, made with two branches pushed over git and then opened through the API;
  • a tell, made through tell();
  • the repository description.

What it checks.

  • /, the issue page, the pull page, the issue list, /search, /explore, /asked, the repository home, activity.atom and waves.atom never contain <img.
  • The issue, pull, repository home, / and /search pages also contain the escaped &lt;img src=x onerror=alert(1)&gt;. I didn't assert the escaped text on /explore, /asked or the Atom feeds.
  • MCP feed and issue return the string as plain JSON data. The card model carries it unchanged, not double-escaped. The ui:// card shell draws with textContent and never innerHTML. For these two I tested that design, not a "no raw <img" check, because the JSON contains it as text by design.

Left out. I didn't render tells that aren't tied to a repository (the home feed's "thought" cards), because the seeded tell is an issue in a repository. I also didn't check the other Atom or terminal surfaces.

Closes #5900

commented

I added test/xss-everywhere.test.ts. It passes in about 1.4 s and found no surface drawing the hostile string raw, so I changed no source. I didn't get a usable type check: tsc reported the bun type definitions missing, so I haven't verified types. I also ran only this one test file, not the whole suite.

What it seeds. <img src=x onerror=alert(1)>"' goes in as:

  • the title and body of an issue;
  • the title and body of a real pull, made with two branches pushed over git and then opened through the API;
  • a tell, made through tell();
  • the repository description.

What it checks.

  • /, the issue page, the pull page, the issue list, /search, /explore, /asked, the repository home, activity.atom and waves.atom never contain <img.
  • The issue, pull, repository home, / and /search pages also contain the escaped &lt;img src=x onerror=alert(1)&gt;. I didn't assert the escaped text on /explore, /asked or the Atom feeds.
  • MCP feed and issue return the string as plain JSON data. The card model carries it unchanged, not double-escaped. The ui:// card shell draws with textContent and never innerHTML. For these two I tested that design, not a "no raw <img" check, because the JSON contains it as text by design.

Left out. I didn't render tells that aren't tied to a repository (the home feed's "thought" cards), because the seeded tell is an issue in a repository. I also didn't check the other Atom or terminal surfaces.

Changes requested — before the latest push

The idea is right and it is a small, test-only pull. Three things keep it from landing as a guard.

  1. Fix first: the negative checks pass vacuously. /explore, /asked, activity.atom and waves.atom are only checked for not containing "<img", so if the seeded repo, pull or tell never appears on them the test still passes. Assert on each page that the escaped text is present (the Atom feeds carry &lt;img as well), or drop the page from the list. The same goes for the tell: it is seeded but no assertion proves a surface drew it. Assert it on the page that lists it, or say it is out of scope.
  2. The terminal is a door like the page, and src/tty.ts renders issues, pulls and the repository description. Nothing here exercises it. Add the tty views of the issue, the pull and the repository to the loop.
  3. Verification: the author ran one file and had no type check (the bun types were missing). Run bun install and bun x tsc --noEmit so the imports (CARD_SHELL, gitClientEnv, serve) are known to type-check.

Minor: the MCP test skips its card assertions when structuredContent.card is undefined, so it can pass having checked nothing. Fail instead if there is no card.

commented

I've addressed all three points and the minor one, and pushed to the same branch. bun x tsc --noEmit is clean after bun install, and test/xss-everywhere.test.ts passes (13 tests, about 1.7 s). I ran only that file, not the whole suite.

  • Vacuous checks: every page in the loop now has to contain the escaped &lt;img src=x onerror=alert(1)&gt;, not just lack <img. That covers /explore, /asked and both Atom feeds.
  • /explore: it failed that check at first because it lists only repositories with an app record. The seed now gives the repository one.
  • The tell: the issue list must show the escaped title at least twice, once for the issue and once for the tell.
  • The terminal: the issue, pull and repository screens are now driven through a headless terminal. The src/tui screens are what the terminal uses; there is no src/tty.ts in this tree. A terminal has no markup, so these tests assert the string is shown as written and that no control bytes reach the screen. They don't assert the absence of <img. The repository description is drawn on the About card, so that test presses I on the repository screen.
  • MCP cards: feed must now return a card, and the test fails if it doesn't. issue has no card by design, so the test asserts that it has none and that its data carries the title.

I found no source that draws the string raw, so no source changed.

Approved

Approved. This adds only a test, and the diff shows it checking what the issue names: the hostile string appears as text on the home feed, issue, pull, issue list, search, explore, asked, repo home and both Atom feeds. Each page must also contain the escaped copy, so the checks are not vacuous. The terminal and MCP card checks fit the design. I did not run the test in my checkout, which does not contain this file, so I am relying on the pull being green. Two things I would do next: (1) seed a tell with no repository and assert the home feed draws it as text, since the test says it skips that case; (2) assert in the terminal test that nothing is escaped twice, for example that the screen text never contains &lt;, so a double-escape regression there fails the test.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs