Skip to content

Webview drops every postMessage from the extension host — origin check never matches #55

Description

@FROWNINGdev

Summary

src/webview/graph.tsx:192 guards the message listener with:

if (event.source !== window) return;

The comment above it asserts that in a VS Code webview the source field is the webview's own window. Measured, it is not. Every message the extension host posts is dropped before the payload is read, so the ER diagram panel never updates after it is first opened.

Measurements

Instrumented the listener to log before the guard, then toggled a model's checkbox in the sidebar (which pushes a fresh index):

MSG sameWindow=false  sameParent=false  sourceNull=false  type="index"

So the message genuinely arrives, carries the right type, and source is a real object that matches neither window nor window.parent. The guard rejects it.

Worth stressing that this was measured, not reasoned about. An earlier attempt to fix it by widening the check to event.source !== window && event.source !== window.parent was a guess, and the numbers above show it would not have worked either.

Why the panel still renders at all

Initial state does not come over the message channel. renderInto embeds it directly in the HTML at src/graphWebview.ts:147:

window.__INITIAL_INDEX__ = ${serialised};

GraphApp seeds useState from that (graph.tsx:178). So the first paint is always correct and the bug is invisible until something tries to update an open panel.

What is broken as a result

Two paths, both postMessage and both dead:

  1. graphWebview.ts:235 — the index push when showGraph is called on an already-open panel.
  2. graphWebview.ts:258 — the reply to the webview's ready handshake.
  3. The theme message handled at graph.tsx:197, so a theme switch does not reach an open panel.

The sidebar visibility toggles hit this first. They are currently worked around at extension.ts:148 by re-assigning the panel HTML on every checkbox change — correct, but it re-renders the whole diagram and visibly flashes. That workaround is the only reason the feature works at all today.

Where the check came from

It was added to satisfy CodeQL's js/missing-origin-check. Any fix has to keep that alert quiet, so simply deleting the line is not a solution.

Directions worth investigating

Not yet verified — listing so the next person does not start from zero:

  • Find out what event.source actually is. Log event.source === window.top, its location, and event.origin. The panel runs inside nested frames (active-frame inside the VS Code shell), so the host frame may be neither the immediate parent nor window.
  • Check whether event.origin is stable enough to check instead of source. VS Code has changed webview origins between versions, so this needs testing across a range, not just the current build.
  • Consider validating the payload shape rather than the sender. The CSP at graphWebview.ts:134 is already default-src 'none' with a script nonce, which is the real barrier to a hostile frame executing anything here; a discriminated-union check on data.type may satisfy CodeQL on its own.

Acceptance

  • An open panel updates via postMessage — no HTML reassignment.
  • The extension.ts:148 workaround is removed and toggling a checkbox no longer re-renders the whole diagram.
  • CodeQL reports no js/missing-origin-check on graph.tsx.
  • Verified by actually toggling a checkbox in a running Extension Development Host, not by reading the code.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions