Skip to content

chore(security): remediate 25 Dependabot alerts - #4596

Merged
Jocs merged 4 commits into
developfrom
chore/security-dependabot-remediation
Jun 21, 2026
Merged

Jocs merged 4 commits into
developfrom
chore/security-dependabot-remediation

Conversation

@Jocs

@Jocs Jocs commented Jun 20, 2026

Copy link
Copy Markdown
Member

Summary

Triage and remediation of all 25 open Dependabot alerts on this repo.

Key finding: every alert maps to a genuinely-installed vulnerable version (no false positives), but none is reachable by an end user of the shipped desktop app:

  • The only user-facing package is dompurify (the editor's HTML sanitizer). All 7 of its CVEs require IN_PLACE / RETURN_DOM / addHook / SAFE_FOR_TEMPLATES / Trusted-Types usage; MarkText only calls DOMPurify.sanitize() with string input and RETURN_TRUSTED_TYPE: false, so they were not reachable. Bumped anyway as defense-in-depth.
  • The remaining 18 alerts live in build / test / website tooling (electron-builder, @electron/rebuild, electron-vite, vite, esbuild, vitest/jsdom, happy-dom, eslint, the website's Cloudflare/Next stack) — never bundled into the shipped app, only present on developer/CI machines.

Everything is still cleaned up: keeping the bundled sanitizer/parsers current is good hygiene, and a clean Dependabot dashboard is signal rather than noise.

Changes (4 atomic commits)

# Package From → To Alerts cleared
1 dompurify (desktop / muya / muyajs) ^3.4.3/^3.4.5 → ^3.4.9 (3.4.11) #443 #451 #452 #453 #454 #455 #456
2 happy-dom (muya devDep) ^15.11.7 → ^20.8.9 (20.10.6) #426 #427 #428 #434 #437 #438
3 vite (desktop / muya / muya-e2e) 7.3.3 / 8.0.14 → 7.3.5 / 8.0.16 #441 #442 #447 #448
4 transitive deps via pnpm.overrides see below #411 #444 #445 #446¹ #449 #450 #457 #439

Commit 4 overrides (range-scoped so unaffected older majors stay put):

form-data   4.0.5       -> 4.0.6     (#450 CRLF injection)
tmp         0.2.5       -> 0.2.6     (#411 path traversal)
tar         7.5.15      -> 7.5.16    (#449 PAX file smuggling)
ws          8.20.1      -> 8.21.0    (#444 memory-exhaustion DoS)
undici      7.24/7.25   -> 7.28.0    (#457 SOCKS5 TLS bypass; keeps 6.25.0)
esbuild     0.27/0.28.0 -> 0.28.1    (#439 dev-server file read; keeps 0.25.x)
@babel/core 7.29.0      -> 7.29.6    (#445 sourceMappingURL file read)
js-yaml     4.1.1       -> 4.2.0     (#446 merge-key DoS; keeps 3.14.2 — see below)

DOMPurify test-environment note (commit 1)

DOMPurify 3.4.8+ hardened cross-realm namespace validation. The happy-dom test environment doesn't satisfy it — under happy-dom, DOMPurify 3.4.11 strips every element, even default-allowed tags like <p>/<h1>. Real Chromium/Electron is unaffected (verified against jsdom, which matches production DOM behavior and sanitizes correctly: <h1>t</h1> → <h1>t</h1>, XSS → stripped). The five DOMPurify-dependent muya specs were therefore moved to the jsdom environment, and jsdom is declared as a muya devDependency.

Residual

¹ #446 (js-yaml merge-key DoS) — the 4.1.1 instance (eslint) is overridden to 4.2.0, but [email protected] remains via the website's gray-matter (no 3.x patch exists; forcing 4.x breaks gray-matter's API). The website parses only trusted first-party content, so the DoS is not exploitable there. Recommend dismissing #446 as "not affected / dev tooling" for that path.

Verification

  • pnpm lint — 0 errors
  • pnpm typecheck — clean
  • muya — lint 0 errors, types clean, 143 unit + 4 conformance suites pass
  • desktop — 17 unit suites pass
  • pnpm build:unpack — builds cleanly (esbuild 0.28.1 + vite 7.3.5 / 8.0.16)

🤖 Generated with Claude Code

Jocs and others added 4 commits June 20, 2026 15:49
Updates the HTML sanitizer from ^3.4.3/^3.4.5 to ^3.4.9 (resolves to
3.4.11), deduping the two installed versions (3.4.3 + 3.4.7) into one.
Clears Dependabot alerts #443/#451/#452/#453/#454/#455/#456.

MarkText calls DOMPurify.sanitize() only with string input and
RETURN_TRUSTED_TYPE: false (no IN_PLACE/RETURN_DOM/addHook/
SAFE_FOR_TEMPLATES), so none of these CVEs were reachable; the bump is
defense-in-depth for the editor's HTML sanitization path.

DOMPurify 3.4.8+ hardened cross-realm namespace validation, which the
happy-dom test environment does not satisfy: under happy-dom it strips
every element, even default-allowed tags like <p>/<h1>. Real
Chromium/Electron is unaffected (verified: jsdom, which matches production
DOM behavior, sanitizes correctly). Move the five DOMPurify-dependent muya
specs to the jsdom environment and declare jsdom as a muya devDependency.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Updates muya's vitest DOM test environment from ^15.11.7 to ^20.8.9
(resolves to 20.10.6). Clears Dependabot alerts
#426/#427/#428/#434/#437/#438 (2 critical "VM context escape / RCE",
4 high).

happy-dom is a devDependency used only as the unit-test DOM; it is never
bundled into the shipped app, and the muya suites run trusted fixtures, so
these CVEs were not reachable. The bump keeps the test toolchain current and
clears the critical badges. The full muya unit suite (143 files) passes on
20.x — the DOMPurify-dependent specs already moved to jsdom in the previous
commit.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Bumps vite to the patched releases across the workspace (desktop ^7.3.5,
muya + muya-e2e ^8.0.16), updating the declared floors so installs can't
regress below the fix. Clears Dependabot alerts #441/#442/#447/#448
(server.fs.deny bypass + launch-editor NTLMv2 disclosure — both Windows
dev-server only; vite is build tooling, never shipped to users).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Adds range-scoped pnpm.overrides to force patched releases of the transitive
dependencies Dependabot flagged, while leaving unaffected older majors in
place (undici 6.x, esbuild 0.25.x, js-yaml 3.x):

  form-data   4.0.5      -> 4.0.6   (#450 CRLF injection)
  tmp         0.2.5      -> 0.2.6   (#411 path traversal)
  tar         7.5.15     -> 7.5.16  (#449 PAX file smuggling)
  ws          8.20.1     -> 8.21.0  (#444 memory-exhaustion DoS)
  undici      7.24/7.25  -> 7.28.0  (#457 SOCKS5 TLS bypass; keeps 6.25.0)
  esbuild     0.27/0.28.0-> 0.28.1  (#439 dev-server file read; keeps 0.25.x)
  @babel/core 7.29.0     -> 7.29.6  (#445 sourceMappingURL file read)
  js-yaml     4.1.1      -> 4.2.0   (#446 merge-key DoS; keeps 3.14.2)

All are build/test/website tooling reachable only on developer/CI machines,
never bundled into the shipped app. js-yaml 3.14.2 remains via the website's
gray-matter (no 3.x patch exists); it parses only trusted first-party
content, so #446 is not exploitable there and will be dismissed on GitHub.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@github-actions

Copy link
Copy Markdown

Website preview ready: https://pr-4596-marktext-website.ransixi.workers.dev

Built from 77eb6f63da91548ee6278d34700c1ba8b7372d44 · Worker: marktext-website · Version: c8273eef-f9f6-4517-9960-93e726ecf2d6 · Alias: pr-4596

@github-actions

Copy link
Copy Markdown

Build artifacts for PR #4596:

Run: https://github.com/marktext/marktext/actions/runs/27864965691

Artifact Size Link
marktext-windows-arm64 256.9 MB Download
marktext-windows-x64 258.2 MB Download
marktext-linux 558.3 MB Download
marktext-macos-x64 257.8 MB Download
marktext-macos-arm64 247.5 MB Download

Jocs added a commit that referenced this pull request Jun 20, 2026
… on CI)

The desktop e2e went red after the refresh bumped @playwright/test 1.60->1.61:
every worker's teardown hit "Worker teardown timeout of 30000ms" (app.close()
hanging) on the Linux/xvfb CI runner, cascading to ~16 failures + did-not-run.
The same suite passes 113/114 locally on macOS with 1.61, so it's a Linux
Electron-teardown regression specific to playwright 1.61's _electron handling
(#4596 passed CI on 1.60). Pin both desktop + muya-e2e back to ^1.60.0; 1.61
can be retried once its Electron teardown is stable on Linux.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Jocs added a commit that referenced this pull request Jun 20, 2026
…own on CI)

The desktop e2e went red after the refresh bumped @playwright/test 1.60->1.61:
every worker's teardown hit "Worker teardown timeout of 30000ms" (app.close()
hanging) on the Linux/xvfb CI runner, cascading to ~16 failures + did-not-run.
The same suite passes 113/114 locally on macOS with 1.61, so it's a Linux
Electron-teardown regression in playwright 1.61's _electron handling (#4596
passed CI on 1.60).

Pin desktop + muya-e2e to ~1.60.0 (tilde, not caret — ^1.60.0 still resolves
1.61). 1.61 can be retried once its Electron teardown is stable on Linux.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Jocs added a commit that referenced this pull request Jun 20, 2026
…xvfb CI)

Root-caused the desktop e2e teardown cascade. app.close() hangs cumulatively
across the workers:1 sequential run on Linux/xvfb: each Electron app leaves
child processes that don't terminate promptly, accumulating until close — then
later launches — fail (~16 "Worker teardown timeout" + did-not-run). It does
NOT reproduce on macOS (113/114 local), and is not font-list / vue-router /
@playwright/test / electron-updater (each eliminated with evidence: e.g.
font-list 2.0.2 leaks the same handle; reverting playwright/vue-router didn't
help). The only changed process-runtime dependency is electron 42.1->42.4
(clean-CI #4596 ran 42.1.0). Pin to ~42.1.0; revisit 42.x once its
headless-Linux child-process teardown is stable.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@Jocs
Jocs merged commit 0080de2 into develop Jun 21, 2026
18 checks passed
Jocs added a commit that referenced this pull request Jun 21, 2026
…xvfb CI)

Root-caused the desktop e2e teardown cascade. app.close() hangs cumulatively
across the workers:1 sequential run on Linux/xvfb: each Electron app leaves
child processes that don't terminate promptly, accumulating until close — then
later launches — fail (~16 "Worker teardown timeout" + did-not-run). It does
NOT reproduce on macOS (113/114 local), and is not font-list / vue-router /
@playwright/test / electron-updater (each eliminated with evidence: e.g.
font-list 2.0.2 leaks the same handle; reverting playwright/vue-router didn't
help). The only changed process-runtime dependency is electron 42.1->42.4
(clean-CI #4596 ran 42.1.0). Pin to ~42.1.0; revisit 42.x once its
headless-Linux child-process teardown is stable.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Jocs added a commit that referenced this pull request Jun 21, 2026
…4597)

* chore(deps): refresh all packages to latest within current majors

Routine `pnpm update -r` across the workspace — bumps every dependency to its
latest release within the existing major (no breaking-major changes) and
updates the package.json caret floors. Highlights:

- desktop: electron 42.1.0->42.4.1, electron-builder 26.8.1->26.15.3,
  axios 1.16.1->1.18.0, element-plus 2.14.0->2.14.2, vue 3.5.34->3.5.38,
  vue-i18n 11.4.2->11.4.6, electron-updater 6.8.3->6.8.9, vitest 4.1.6->4.1.9
- muya/muyajs: snabbdom 3.6.3->3.6.4, fuse.js 7.3.0->7.4.2
- tooling: prettier 3.8.4, tsx 4.22.4, @playwright/test 1.61.0,
  typescript-eslint 8.61.1, eslint-plugin-* bumps
- website: react 19.2.7, wrangler 4.103.0, @axe-core/playwright 4.11.3

@types/node kept on the v22 line to match the Node 22 runtime. Shared deps
now align across packages except the cross-major splits (eslint/typescript/
vite) handled in following commits.

Verified: lint 0 errors, typecheck clean, muya 143 unit + 4 conformance,
desktop 17 unit, build:unpack OK.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

* chore(deps): upgrade desktop to typescript 6 + vue-tsc 3

Aligns desktop's TypeScript with muya (already on 6). vue-tsc 2->3
(vue-language-tools 3); typescript-eslint 8.61 supports TS <6.1 and vue-tsc 3
peers TS >=5, so the desktop type-check stack moves cleanly.

Verified: `vue-tsc --noEmit` passes on desktop. TypeScript is type-check-only
tooling here (esbuild drives build + vitest), so runtime/build are unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

* chore(deps): upgrade katex to ^0.17.0 across muya/muyajs/desktop

Bumps the math renderer to 0.17 and normalizes muya's pin from an exact
`0.16.47` to `^0.17.0`, so the declared version is consistent across the three
packages. mermaid + micromark-extension-math keep their own transitive
katex 0.16.x (forcing those to 0.17 risks their math rendering).

Verified: muya 143 unit + 4 conformance, desktop 17 unit, build:unpack OK.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

* chore(deps): upgrade plist to ^5.0.0 (desktop)

plist is used in a single place: parsing the macOS NSFilenamesPboardType
clipboard payload via `plist.parse()` (main/ipc/shell.ts). The parse API is
stable across the major bump — desktop type-check passes against plist 5's
types and build:unpack is clean. Transitive plist 3.x pulled by other tooling
is left as-is.

Verified: desktop typecheck clean, build:unpack OK.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

* chore(deps): upgrade vue-router to ^5.1.0 (desktop)

Desktop's router uses only modern APIs (createRouter + createWebHashHistory,
RouteRecordRaw, named/children/redirect routes, <router-view>) — none of the
deprecated APIs v5 removes. v5's only required peer is vue ^3.5.34 (satisfied);
pinia / @pinia/colada / vite peers are optional.

Verified: desktop typecheck clean, 17 unit suites pass, build:unpack OK.
Real-app routing is additionally exercised by the e2e / run-on-pr-head CI jobs.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

* chore(deps): upgrade marked to ^18.0.5 (muya parser)

marked 17+ changed tokenization in ways muya's parser layer must absorb:

- Task lists: marked now emits a `checkbox` token and keeps the literal
  "[ ] " / "[x] " marker in the item content (in a paragraph's `text` for
  loose lists). muya renders the marker from the task-list-item `checked`
  meta, so `compatibleTaskList` now strips it from both tight and loose items
  — otherwise every md -> state -> md round-trip duplicated the checkbox.
- Indented code: the `code` token text now carries a trailing newline (fenced
  does not); markdownToState strips it so indented blocks round-trip.
- Setext headings: conformance improved — CommonMark #84/#89 and GFM #54/#59
  now pass and are removed from expected-failures.json.

Verified: muya 143 unit + 4 conformance suites (compliance up), desktop 17
unit, build:unpack OK.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

* chore(deps): prune now-redundant pnpm overrides after upgrades

Re-checked every security override against the upgraded tree. The dependency
refresh now resolves patched versions natively, so 7 of the 8 overrides are
redundant and removed:

  @babel/core (7.29.6), form-data (4.0.6), js-yaml (4.2.0), tar (7.5.16),
  tmp (0.2.6), undici (7.28.0), ws (8.21.0)

Kept:
  - esbuild >=0.27.0 <0.28.1 -> 0.28.1: vite 7.3.5 (desktop, pinned by
    electron-vite which peers vite <=7) still pulls the vulnerable esbuild
    0.27.7; this bridges it until electron-vite supports vite 8.
  - postcss 8.5.15: pre-existing (since #4314), pins to current latest 8.x.

Verified: lockfile keeps every previously-flagged package at a patched
version; build:unpack OK.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

* chore(deps): align muya mermaid specifier to caret

muya pinned mermaid to an exact "11.15.0" while desktop/muyajs use "^11.15.0".
Normalize to "^11.15.0" so the declared version is consistent across packages
(resolves to the same 11.15.0).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

* fix(deps): defer vue-router 5 — keep ^4.6.4

vue-router 5 passed typecheck/unit/build but broke the desktop app launch in
e2e: every suite's beforeAll timed out because the renderer never reached
ready (renderer errors captured by the e2e helper). Unit tests don't mount the
full app + router, so the runtime regression only surfaced in e2e.

Revert to ^4.6.4 (latest 4.x, known-good). vue-router 5 needs a dedicated
migration PR with e2e verification, like codemirror 6 / website next 16.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

* fix(deps): make plist 5 work in the CJS main process

plist 5 ships ESM-only with named exports and no CJS `exports` entry, so the
externalized main-process `require('plist')` threw
ERR_PACKAGE_PATH_NOT_EXPORTED at startup — the app never launched, which failed
every desktop e2e suite (beforeAll launch timeout). The earlier vue-router
revert was a red herring; muya's own e2e passed, ruling out the engine.

Fix: exclude plist from externalization so electron-vite bundles it as CommonJS
(same pattern as electron-store), and switch shell.ts to a namespace import
(`import * as plist`) since plist 5 dropped its default export.

Verified: build:unpack bundles plist inline (no runtime require('plist')),
typecheck clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

* fix(deps): pin electron to ~42.1.0 (42.4 breaks Electron teardown on xvfb CI)

Root-caused the desktop e2e teardown cascade. app.close() hangs cumulatively
across the workers:1 sequential run on Linux/xvfb: each Electron app leaves
child processes that don't terminate promptly, accumulating until close — then
later launches — fail (~16 "Worker teardown timeout" + did-not-run). It does
NOT reproduce on macOS (113/114 local), and is not font-list / vue-router /
@playwright/test / electron-updater (each eliminated with evidence: e.g.
font-list 2.0.2 leaks the same handle; reverting playwright/vue-router didn't
help). The only changed process-runtime dependency is electron 42.1->42.4
(clean-CI #4596 ran 42.1.0). Pin to ~42.1.0; revisit 42.x once its
headless-Linux child-process teardown is stable.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

* test(muya): run formatToggle html_tag-mount tests under jsdom

Split the two "mounts the html_tag into the live editor DOM" cases (<u>/<mark>)
out of formatToggle.spec.ts into formatToggleHtmlTagMount.spec.ts (jsdom). They
route a live element through DOMPurify, which strips all elements under
happy-dom on dompurify 3.4.8+ (its namespace hardening isn't satisfied there).
The rest of formatToggle.spec.ts stays on happy-dom because its selection /
picker assertions rely on happy-dom's selection behavior. Consistent with the
other DOMPurify-dependent specs moved to jsdom in the dompurify bump.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
@Jocs
Jocs deleted the chore/security-dependabot-remediation branch June 21, 2026 00:41
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