Skip to content

fix: KaTeX LaTeX leak (#194), image recognition (#192), ARM64 silent exit (#190), + 1000 Genomes skill docs (#191)#201

Open
KB (KB-syntheticsciences) wants to merge 7 commits into
mainfrom
fix/katex-latex-leak-194
Open

fix: KaTeX LaTeX leak (#194), image recognition (#192), ARM64 silent exit (#190), + 1000 Genomes skill docs (#191)#201
KB (KB-syntheticsciences) wants to merge 7 commits into
mainfrom
fix/katex-latex-leak-194

Conversation

@KB-syntheticsciences

@KB-syntheticsciences KB (KB-syntheticsciences) commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

Batch of four independent issue fixes, each as its own commit.

Closes #194
Closes #192
Closes #190
Closes #191
Closes #202

#194 — KaTeX shows raw LaTeX beside rendered math

Root cause: KaTeX's default htmlAndMathml output emits <math><semantics>…<annotation encoding="application/x-tex">RAW TEX</annotation></semantics></math>. The DOMPurify config in frontend/ui/src/components/markdown.tsx had no ADD_TAGS, so <semantics>/<annotation> were stripped; with DOMPurify's default KEEP_CONTENT: true, the raw TeX survived as a bare text node in <math> and rendered visibly.

Fix: ADD_TAGS: ["semantics","annotation","annotation-xml"] + ADD_ATTR: ["encoding"]. The annotation stays nested (non-rendered metadata). FORBID_TAGS/FORBID_CONTENTS unchanged — no XSS widening (verified: annotation-xml encoding="text/html" mXSS payloads are still stripped, with a committed test asserting it).

Test harness note: the behavioral test uses jsdom, not happy-dom (see #202).

#192 — Uploaded images rejected as "this model doesn't support image input"

Root cause: the image gate in backend/cli/src/provider/transform.ts keys on model.capabilities.input.image, which defaults to false in paths users hit — notably _syntheticOpenRouterModel (provider.ts) hardcodes image:false for managed/OpenRouter models missing from the local catalog. Vision-capable models had their image swapped for a harness-injected "does not support image input" note, which the model then paraphrases.

Fix: (a) the gate falls back to the coarser attachment capability for image/pdf only (audio/video unchanged); (b) _syntheticOpenRouterModel defaults to permissive attachment:true + input.image/pdf:true for synthesized unknown models — a real provider error beats a fabricated one.

#190 — ARM64 hosts: launcher exits silently with no output

Root cause: backend/cli/bin/openscience run() computed code = typeof result.status === "number" ? result.status : 0, so a binary killed by a signal (status: null — e.g. SIGILL/SIGSEGV on 64KB-page ARM64 hosts) was treated as exit 0 and exited silently.

Fix: a result.signal branch prints an actionable diagnostic (signal, platform/arch, the 64KB-page ARM64 hint, recovery/report links) and exits non-zero. The normal numeric-exit path is unchanged. (Native arm64 binaries are already built + published — this is only the silent-signal bug.)

#191 — 1000 Genomes individual-genotype skill

Made the community skill discoverable via the existing openscience skill add flow (docs entry in skills.mdx) rather than bundling a third-party skill that depends on an external endpoint (db.dnaerys.org) the maintainers don't control. Verified the install command works end-to-end (skill add gh:dnaerys/onekgpd-skill → fetch → safety review pass → installed).

#202 — happy-dom unsound as a DOMPurify test harness

Audit folded in: DOMPurify is used in exactly one place (markdown.tsx), and the only test exercising it (frontend/ui/markdown.test.ts) is already on jsdom here. No frontend/workspace test does DOMPurify / mid-walk DOM mutation, so nothing needed migrating. Added a guard comment on frontend/workspace/happydom.ts so future sanitizer tests aren't written against happy-dom — its NodeIterator lacks the DOM spec's live-mutation adjustment, so it silently under-sanitizes and a broken sanitizer can false-pass under it.

Verification (local)

  • Backend suite: 1149 pass / 0 fail (1 pre-existing env-gated skip)
  • frontend/ui suite: 18 pass / 0 fail (incl. jsdom sanitizer tests + mXSS guards)
  • Typecheck: 7/7 packages
  • Each fix independently reviewed; final whole-branch review clean (0 Critical / 0 Important).

CI caveat

The Test (bun test) check will be red for an unrelated external reason — the current ubuntu-latest / ubuntu-24.04 runner image (≥20260714.240, azure kernel -1020) breaks Bun's test runner repo-wide (see closed PR #200). Everything here passes locally. Merging relies on admin override past that known-bad check.

#194)

DOMPurify's mathMl profile disallows <semantics>/<annotation>, and its
default KEEP_CONTENT strips the tags but keeps their text — so the raw
TeX source inside KaTeX's MathML annotation survived as a bare text
node and rendered visibly next to the typeset formula. Add ADD_TAGS/
ADD_ATTR to the sanitize config to allowlist the annotation wrapper
tags, and export sanitize so it's directly testable.

Adds a jsdom-based DOM test harness for frontend/ui (bunfig preload +
jsdom.ts), which currently has no DOM test environment. Uses jsdom
rather than the @happy-dom/global-registrator pattern used in
frontend/workspace: happy-dom's NodeIterator doesn't implement the DOM
spec's live-mutation adjustment, so once DOMPurify removes the first
disallowed node mid-walk, iteration silently stops and everything
after goes unsanitized — verified directly, and confirmed jsdom does
not have this problem.
…lity (#192)

unsupportedParts() gated image/pdf parts solely on the fine-grained
capabilities.input.image/pdf flag, which is absent or wrong for models
outside the local models.dev catalog. The synthetic OpenRouter model
in particular hardcoded attachment:false + input.image:false for any
whitelisted-but-uncataloged model, so vision-capable models silently
had their images swapped for an ERROR text part that the model then
paraphrased as "this model doesn't support image input".

Fall back to the coarse attachment capability for image/pdf only when
the finer modality flag is missing (audio/video untouched), and make
the synthetic model's placeholder capabilities permissive for
image/pdf so a genuinely-unsupported attachment surfaces a real
provider error instead of a fabricated one.
…tly (#190)

On some ARM64 hosts (e.g. 64KB page-size kernels) the prebuilt native
binary is killed by a signal (SIGSEGV/SIGILL) rather than exiting with
a status code. spawnSync then returns status: null, signal: "SIG...",
and the launcher's tail (`code = status ?? 0`) silently exited 0 with
no output — the reporter's "exits silently."

Add a signal branch in run() that prints an actionable diagnostic
(signal name, host platform/arch, remediation steps) and exits 1. The
existing result.error branch and numeric-status exit path are
unchanged.
Make the dnaerys/onekgpd-skill (1000 Genomes individual-genotype queries,
MIT) discoverable via the existing `skill add` flow rather than bundling a
third-party skill that depends on an external endpoint the maintainers
don't control.
@vercel

vercel Bot commented Jul 22, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
openscience Ready Ready Preview, Comment Jul 22, 2026 8:57am

Request Review

…tion tests (#202)

Audit for #202 found no DOMPurify-based tests in frontend/workspace (the
only DOMPurify test, frontend/ui markdown.test.ts, is already on jsdom).
Add a guard comment on the happy-dom registrator so future sanitizer tests
aren't written against it and silently pass with a broken sanitizer.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment