Skip to content

0.9.29#442

Merged
shd101wyy merged 13 commits into
developfrom
fix/wavedrom-security
Jun 5, 2026
Merged

0.9.29#442
shd101wyy merged 13 commits into
developfrom
fix/wavedrom-security

Conversation

@shd101wyy

Copy link
Copy Markdown
Owner

No description provided.

shd101wyy and others added 13 commits June 4, 2026 18:44
…; improve MathJax 4 performance

WaveDrom (shd101wyy/vscode-markdown-preview-enhanced#2315):
- Replace window.eval with JSON5.parse in renderWavedrom() to prevent
  arbitrary code execution from user-controlled fenced code blocks
- JSON5.parse safely handles unquoted keys, comments, trailing commas,
  and single-quoted strings without executing code

Bitfield:
- Replace interpretJS (vm.runInNewContext) with JSON5.parse in
  renderBitfield() — bitfield register definitions are purely data

MathJax 4 (shd101wyy/vscode-markdown-preview-enhanced#2312):
- Disable enableAssistiveMml, enableMenu, enableExplorer in default
  MathJax 4 config to reduce per-formula overhead
- Remove redundant typesetClear() and texReset() calls before
  typesetPromise() since DOM is freshly rebuilt on every render
WaveDrom (#2315): the previous fix only patched the live-preview path. The
bundled WaveDrom.ProcessAll()/eva() helpers used in presentation mode and HTML
export still eval'd <script type="WaveDrom"> content, and that script can be
injected via raw HTML in markdown. Move the defense to the trust boundary: the
HTML sanitizer (server + client) now validates and normalizes every WaveDrom
data script to inert strict JSON via a shared normalizeWavedromSource() helper
(escaping `<` to prevent </script> breakout, dropping non-data scripts), so no
downstream eval can execute attacker-controlled JavaScript.

MathJax (#2312): the prior config (enableAssistiveMml/enableMenu/enableExplorer)
gave ~0 speedup — MathJax ignores those flags in the config block, and
assistiveMml is already off by default. The real cost is a11y semantic
enrichment (speech-rule-engine), ~890ms vs ~42ms for 127 formulas in Chrome
(~21x). Disable it via options.enableEnrichment:false, re-applied onto the live
MathDocument through a startup.ready hook (buildMathJaxConfigScript), since the
config-block flag is otherwise reset during startup. Users can opt back in.
Also restore typesetClear()/texReset() in the webview: dropping them leaked
MathJax's internal math list across re-renders and broke equation numbering
("Label multiply defined").

Tests:
- unit: normalizeWavedromSource, sanitizer WaveDrom normalization,
  buildMathJaxConfigScript, getDefaultMathjaxConfig
- browser (Playwright, new `pnpm test:browser` + CI job): MathJax equation
  numbering across re-renders, enrichment-disabled-by-default/opt-in, and the
  WaveDrom security fix exercised through the real eval-based ProcessAll path

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… filenames

Reported by @byte16384: openFile() opened external/unsupported link targets
with child_process.exec(`start ${href}`), which runs through cmd.exe. The href
comes from the markdown file and is URL-decoded, so a crafted link like
[x](mailto:a%26%20calc.exe) decoded to `mailto:a& calc.exe` and cmd.exe ran
`calc.exe` as a second command — one-click RCE on Windows.

- openFile(): spawn via execFile('explorer.exe', [target]) (no shell) so the
  target is always one uninterpreted argument; `open --` on macOS to block
  option injection.
- Similar sinks: the diagram `filename` attribute flows into converters that
  shell out (ImageMagick via imagemagick-cli's child_process.exec; mermaid-cli
  spawned with shell:true). Added sanitizeImageFilename() (allowlist + no path
  traversal) applied where filenames are resolved in process-graphs.
- pdf2svg (@import of a .pdf, auto-rendered on preview) no longer spawns with
  shell:true / manual quoting.

Tests: test/command-injection.test.ts covers the byte16384 PoC (no shell, href
passed as a single arg) across win32/darwin/linux, plus sanitizeImageFilename.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Promote the [Unreleased] CHANGELOG section to [0.9.29] and bump package.json.
Includes the WaveDrom eval RCE fix (#2315), MathJax 4 enrichment performance
fix (#2312), and the Windows command-injection fix for clicked preview links
and diagram filenames (reported by @byte16384).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…x shell)

From code review of fix/wavedrom-security:

- normalizeWavedromSource: reject non-finite numbers (Infinity/NaN/-Infinity)
  instead of letting JSON.stringify silently coerce them to `null`, and reject
  top-level array roots (WaveDrom roots must be objects). Drops the diagram
  rather than rendering corrupted data.
- latex.ts: drop `shell: true` + manual quoting, matching the pdf2svg fix.
  `latexEngine` comes from the `latex_engine` code-chunk attribute
  (attacker-controlled markdown); spawning without a shell prevents command
  chaining. Code-chunk execution remains gated behind enableScriptExecution.
- tests: cover Infinity/NaN and array-root rejection; fix a mislabeled
  wavedrom-parser test ("backtick" -> "arrow-function IIFE").

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Avoid publishing exploit details (decoded payload / mechanism) before users
have updated; keep the entry to the mitigation and the credit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace [@user](https://github.com/user) links with plain @user tags.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The owner/repo#num shorthand doesn't autolink in a rendered Markdown file;
use [vscode-mpe#NNNN](…/issues/NNNN) to match the existing CHANGELOG style.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
sanitizeImageFilename previously rejected a leading `/`, treating it as an
absolute path. But in MPE `/xxx` is the project-root-relative convention (same
as imageFolderPath, e.g. default `/assets`), not a filesystem-absolute path.

- sanitizeImageFilename: allow a leading `/`; keep the shell-safe allowlist and
  the `..`-traversal rejection (so the path stays inside the root).
- process-graphs: new resolveOutputImagePath() resolves a leading-`/` name
  against projectDirectoryPath (via `'.' + name`), else against the image
  output dir — mirroring markdown-convert's imageFolderPath handling. This also
  fixes the latent bug where `path.resolve(imageDir, '/x.png')` wrote to the
  filesystem root instead of the project root.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…I allowlist)

The previous ASCII-only allowlist (^[A-Za-z0-9_./-]+$) silently rejected
non-English filenames like 图表.png or schéma.png, falling back to an
auto-generated name. Unicode letters have no shell meaning, so the security
goal is to block shell metacharacters, not non-ASCII letters.

Switch to a denylist: reject shell metacharacters, quotes, whitespace, the
backslash, and Unicode control/format chars (\p{C}, e.g. NUL / RTL override),
plus the existing `..` traversal check. Everything else — including CJK and
accented Latin — is allowed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the UNSAFE_IMAGE_FILENAME_CHARS const above the JSDoc so the doc comment
sits directly on the function (no behavior change).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@shd101wyy shd101wyy merged commit 5588ca2 into develop Jun 5, 2026
2 checks passed
@shd101wyy shd101wyy deleted the fix/wavedrom-security branch June 5, 2026 03:27
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