## Summary

NoteGen before 0.32.0 rendered AI chat responses in the chat preview with `markdown-it` configured as `html: true` and then inserted the generated HTML into the DOM with React `dangerouslySetInnerHTML`. Attacker-controlled prompt material, such as a malicious Skill `REFERENCE.md` that induces the model to emit HTML, can therefore become executable markup in the NoteGen webview/chat UI. The current reproduction drives the real NoteGen `ChatPreview` component in vulnerable and fixed builds and shows that the vulnerable parent of the v0.32.0 fix executes attacker-supplied iframe `srcdoc` JavaScript while the fixed commit reaches the same chat-preview path without executing the payload.

## Impact

- Package/component affected: NoteGen chat UI, specifically `src/app/core/main/chat/chat-preview.tsx` and the chat-preview rendering path used for AI/model responses.
- Affected versions: NoteGen before 0.32.0. The reproduction anchors the vulnerable build to fixed commit parent `70dfd0042f56f24dc29df710f6dd2e009731e6a4` and the fixed negative control to `ae3ba948c41d8a74b4a20f4c6f26fcdda2002298`.
- Risk level and consequences: high. If attacker-controlled content reaches the model prompt and the model emits executable HTML, the chat preview can execute script in the privileged NoteGen webview/UI context. The proof uses an iframe `srcdoc` payload that writes a unique marker into the page, demonstrating XSS-style script execution.

## Impact Parity

- Disclosed/claimed maximum impact: chat-preview XSS from unsanitized HTML rendering of AI chat responses induced by attacker-controlled content.
- Reproduced impact from this run: XSS-style JavaScript execution in the NoteGen chat-preview DOM path. Two vulnerable attempts executed unique attacker-controlled markers, and two fixed attempts reached the same prompt/model/chat-preview path without marker execution.
- Parity: `full` for the claimed XSS behavior.
- Not demonstrated: no OS command execution, persistence, credential theft, or privilege escalation was attempted or claimed for this CVE.

## Root Cause

In the vulnerable parent commit, `src/app/core/main/chat/chat-preview.tsx` constructs a `MarkdownIt` renderer with `html: true`, renders the AI response text, stores the rendered HTML in component state, and inserts it into the DOM with `dangerouslySetInnerHTML`. This permits raw HTML emitted by the model to become live DOM nodes instead of escaped text. The script verifies this source path before runtime execution by checking that the vulnerable commit contains `new MarkdownIt({ html: true, ... })` and `dangerouslySetInnerHTML` in `chat-preview.tsx`.

The fixed commit `ae3ba948c41d8a74b4a20f4c6f26fcdda2002298` replaces this renderer path with `StreamdownRenderer`. In the fixed runtime, the same model-output text and chat-preview route are reached, but the iframe/srcdoc payload is not inserted as executable DOM and no XSS marker is created.

## Reproduction Steps

1. Run `bundle/repro/reproduction_steps.sh` from any directory. The script honors `PRUVA_ROOT` and otherwise discovers the bundle root.
2. The script reuses the prepared checkout at `/pruva/project-cache/repo` when available, resolves fixed commit `ae3ba948c41d8a74b4a20f4c6f26fcdda2002298` and vulnerable parent `70dfd0042f56f24dc29df710f6dd2e009731e6a4`, verifies the vulnerable/fixed chat-preview source diff, installs pnpm/Puppeteer in a user-writable scratch directory, and installs NoteGen frontend dependencies in isolated worktrees.
3. For each build, it starts the real NoteGen Next UI, opens `/pruva-chat-preview-xss` in headless Chromium, and renders the unmodified production `ChatPreview` component with a model response induced by attacker-controlled Skill `REFERENCE.md` text.
4. Expected evidence: vulnerable attempts record `browser_window_marker_observed=true`, `document_xss_attr_observed=true`, and `xss_node_observed=true`; fixed attempts record the prompt/model/chat-preview path as reached but all XSS marker fields remain false.

## Evidence

Primary evidence files from the successful second run:

- `bundle/logs/reproduction_steps.log` - top-level transcript showing both vulnerable and fixed UI runs completed and the final proof summary was printed.
- `bundle/logs/repro/source_diff_chat_preview.log` - source evidence that the vulnerable commit uses `markdown-it` with `html: true` and `dangerouslySetInnerHTML`, while the fixed commit uses `StreamdownRenderer`.
- `bundle/logs/repro/vulnerable_attempt_1.log` and `bundle/logs/repro/vulnerable_attempt_2.log` - browser runtime evidence for two vulnerable attempts.
- `bundle/logs/repro/fixed_attempt_1.log` and `bundle/logs/repro/fixed_attempt_2.log` - same-path fixed negative controls.
- `bundle/artifacts/notegen-chat-preview-xss/proof_summary.json` - structured summary: `vulnerable_xss_count: 2`, `fixed_xss_count: 0`.
- `bundle/repro/runtime_manifest.json` - runtime manifest with `entrypoint_kind: open_document`, `service_started: true`, `healthcheck_passed: true`, and `target_path_reached: true`.

Key excerpts:

- Vulnerable attempt 1: `prompt_reached_model_path=true`, `model_output_recorded=true`, `chat_preview_dom_reached=true`, `browser_window_marker_observed=true`, `document_xss_attr_observed=true`, `xss_node_observed=true`, and `xss_observed=true`.
- Vulnerable attempt 2: same successful marker execution with a distinct marker.
- Fixed attempt 1: `prompt_reached_model_path=true`, `model_output_recorded=true`, `chat_preview_dom_reached=true`, but `browser_window_marker_observed=false`, `document_xss_attr_observed=false`, `xss_node_observed=false`, and no raw iframe/srcdoc remained in the rendered preview.
- Fixed attempt 2: same no-XSS fixed negative control with a distinct marker.
- HTTP response headers captured in attempt logs did not include a Content-Security-Policy header.

## Recommendations / Next Steps

Upgrade NoteGen to v0.32.0 or later. Keep AI/model output rendering on a renderer that treats raw HTML as inert text or sanitizes it with a strict allow-list. Add regression tests around chat-preview rendering that feed model-output strings containing event handlers, iframes, `srcdoc`, SVG/event attributes, and script-like URLs, and assert that no executable DOM or event handler remains. If raw HTML is ever intentionally supported, enforce a restrictive sanitizer and a defensive Content-Security-Policy in the Tauri/webview context.

## Additional Notes

The final reproduction script was executed twice consecutively and passed both times. It is idempotent: worktrees and package/browser caches are kept under `/tmp/pruva-notegen-chat-preview-xss`, while current-run evidence is written under `bundle/logs/`, `bundle/artifacts/notegen-chat-preview-xss/`, and `bundle/repro/`. The route used by the script is a minimal current-run UI route added to each worktree solely to drive the unmodified production `ChatPreview` component and compare the vulnerable/fixed rendering behavior through a real browser DOM runtime.