Skip to content

Sanitize fuzzy-matched command text before rendering with @html - #1566

Open
kah-ja wants to merge 1 commit into
dbgate:masterfrom
kah-ja:fix/command-palette-html-sanitize
Open

kah-ja wants to merge 1 commit into
dbgate:masterfrom
kah-ja:fix/command-palette-html-sanitize

Conversation

@kah-ja

@kah-ja kah-ja commented Sep 12, 2026

Copy link
Copy Markdown

What

CommandPalette.svelte renders each palette entry with {@html command.text} (:243). command.text comes from fuzzy.filter(filter, ..., { extract: x => _tval(x.text), pre: '<b>', post: '</b>' }), run over the current commands or, when the palette shows database objects, over table/view/collection/procedure/function/database names loaded via database-connections/structure (metadataLoaders.ts). fuzzy inserts the pre/post markup into the matched source string without escaping it, so a name containing HTML reaches {@html} unchanged.

Why

packages/web already has the same problem solved for a different {@html} call: FontIcon.svelte passes an SVG icon string through DOMPurify.sanitize(iconValue, { USE_PROFILES: { svg: true, svgFilters: true } }) before its own {@html sanitizedSvg}. CommandPalette had no equivalent step between the fuzzy match and the render.

How

The .map() that builds the palette's rendered rows now sanitizes x.string with DOMPurify.sanitize(x.string, { ALLOWED_TAGS: ['b'], ALLOWED_ATTR: [] }) before assigning it to text, keeping only the <b> tag fuzzy itself inserts for the highlight and dropping every other tag and attribute. dompurify is already a dependency of dbgate-web (^3.3.2), no new dependency is added.

Left unchanged: fuzzy's matching and scoring logic and its pre/post markers. The plain command-list path (non-database-object commands) goes through the same .map() and so now gets sanitized too, even though its text values are static UI strings. Two other {@html} call sites in this package are also left alone: elements/DiffView.svelte:17 renders output produced by diff2html, and celldata/HtmlCellView.svelte:7-12 renders a grid cell verbatim as part of an explicit view-as-HTML mode the user selects. Both differ from the palette, where the value is rendered without the user asking for HTML, so they are out of scope here.

Testing

  • packages/web has no unit test runner configured (package.json only defines validate = svelte-check, no jest/vitest); no test was added, none was invented
  • manual check with the exact call this fix uses (dompurify@3.4.13, matching the pinned dependency, against jsdom in a scratch directory): x<b>foo</b><img src=x onerror=alert(document.cookie)> sanitizes to x<b>foo</b>; <script>alert(1)</script> sanitizes to an empty string; <b onmouseover=alert(1)>evil</b> sanitizes to <b>evil</b>; a plain highlight my<b>Table</b>Name passes through unchanged
  • svelte-check --workspace packages/web was run; it reports pre-existing errors across most files in this checkout (Cannot find module 'svelte/compiler', Cannot find module 'dbgate-datalib') unrelated to this change, plus a pre-existing hint on CommandPalette.svelte's untouched line 75 (unused filterName import); no new error appears on the changed lines

References

GHSA-qv2r-5c8w-98qq (found during penetration test by turingpoint, reported privately, unpublished at time of writing)

CommandPalette.svelte renders each entry with {@html command.text}
(:243). command.text comes from fuzzy.filter(..., { pre: '<b>', post:
'</b>' }), which inserts that markup into the raw, unescaped source
string, and the source strings include database, table, view,
collection, procedure and function names loaded verbatim from
database-connections/structure (metadataLoaders.ts). An object name
containing markup reached {@html} unescaped.

Passes the fuzzy match result through DOMPurify.sanitize before it is
stored as command.text, allowing only the <b> highlight tag the
matcher itself produces and no attributes, the same DOMPurify call
this package already uses for {@html} in FontIcon.svelte.
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