Unclaimed files left in the canonical checkout. Inventory: canonical-cleanup-inventory.md (commit-as-evidence groups). detect-secrets scan clean on 2026-11-02: hits are sha256 pins, git object ids, test sentinels already in tracked tests and unit-file lines.
23 KiB
Row 148 / #1642: library choice (step 1)
Researcher, 2026-10-16T17:48Z. Nothing under public/ changed; no claim made; nothing in the repository was
installed or run. Downloads live in /mnt/storage/scratch/researcher/q148-lib/ (each in its own new directory,
extracted with tar, no scripts run).
Answer
Toast UI Editor 3.2.2 is the only candidate that comes close, and it does not pass your grep rule as written. Your decision, Sage:
- The built file contains two
Function("return this")call sites, one writtennew Function(…)(details below). Both sit behindglobalThis/window/selfchecks and did not run in my CSP probe. A literal grep still finds them. If you rule that a dead fallback is acceptable, I vendor it. If you rule the literal rule is absolute, no candidate fits and the fallback (textarea plus a preview built from repository code) applies. - Its stylesheet embeds 8
data:images. The Console CSP has noimg-src, so it falls back todefault-src 'self'and blocks them (toolbar rendered empty, 1 violation). A CSP change is not allowed, so I would ship a derived stylesheet with the images extracted into files served from `'self'. That is a modification of the upstream CSS, so say yes or no.
If both are yes, the vendor set is the minified variant below.
The candidate
| Field | Value |
|---|---|
| Name | Toast UI Editor (@toast-ui/editor) |
| Version | 3.2.2 (built Fri Feb 17 2023; last release) |
| License | MIT, Copyright (c) 2020 NHN Cloud Corp. Header in the bundle says @license MIT. |
| Bundled third-party | DOMPurify 2.3.3 (Apache-2.0 / MPL-2.0 dual), ProseMirror (MIT), tui-code-snippet (MIT) |
| Mode | Real WYSIWYG, with a Markdown tab toggle. Output is Markdown (getMarkdown()). |
| Upstream LICENSE | https://raw.githubusercontent.com/nhn/tui.editor/master/LICENSE, 1,072 B, sha256 1c7c070da956f0c3122883c1b4eb706b1b819971082ccde001fe2d241d90e3c0 |
Provenance gap
- The npm tarball (
@toast-ui/[email protected], sha512dist.integrityverified) has no LICENSE file and no standalone browser bundle. Itsdist/toastui-editor.jsexpects ProseMirror externals and is not usable in a plain<script>. - The standalone bundle
toastui-editor-all(.min).jscomes fromuicdn.toast.com/editor/3.2.2/, downloaded once. It is not an npm-verified artifact. I compared it to the npm dist: 98.62% of the npmdistlines appear verbatim in the all.js (the remainder is the UMD wrapper). The CSS from the CDN is byte-identical to the npm CSS (sha256c0ceb967…). - The LICENSE file comes from GitHub
master, not from the release tag.
Files I would vendor (recommended: minified variant, derived CSS)
Directory public/vendor/toastui-editor-3.2.2/ (shown relative to it):
| sha256 | bytes | file |
|---|---|---|
| f50e1b7c0fc4e5d9a1ccd0d8be78cb3a950ccb3bf676fbf1627810c76aeaedd8 | 534289 | toastui-editor-all.min.js (unmodified upstream) |
| 61aabe63e8ddb86e18d7560bd883c3fd60f8adc14851fcad3eecd472c1854dd2 | 27790 | toastui-editor.min.css (derived) |
| 1a5dd9ba2158c9f579545ece60c293b496249231a1ed3aea6143c6d646217275 | 23915 | img/1a5dd9ba.png (extracted) |
| e15d7afe083df2f3c835a681a905cce197f388dd5bda89788caef03d42ec0217 | 53818 | img/e15d7afe.png (extracted) |
| 9fc65db774689965f7dfc9b83390d748ed8640414078ebc39ba534c2433ac569 | 506 | img/9fc65db7.svg (extracted) |
| 0b41c43d3595f11277fd39f5e31723095ec70aefa28561145723eae234f75dda | 166 | img/0b41c43d.svg (extracted) |
| ccdcf3ad97fec299e13a5b57abc474c6c64b1228d3335c9963356d3b435c9a59 | 342 | img/ccdcf3ad.svg (extracted) |
| b2eb3ec2ef047acf17091ba8eab9c8cf34f9752c370c349c518c92a9ac725cd6 | 257 | img/b2eb3ec2.svg (extracted) |
| 1c7c070da956f0c3122883c1b4eb706b1b819971082ccde001fe2d241d90e3c0 | 1072 | LICENSE |
Total: 642,155 bytes (641,083 of code and images plus the 1,072 B LICENSE). vendor.sha256 would list these paths.
That is 9 files plus vendor.sha256 itself. serve.mjs serves from an explicit route table, so it needs a route for each file the
page loads (the js, the css, the 6 images: 8 routes; LICENSE and vendor.sha256 only if you want them served). Row 148 owns serve.mjs.
The derived CSS is node harness/split-css.mjs over the upstream toastui-editor.min.css (upstream sha256
c70e24c68fefc205e8e504edc07fd6a5efd3044a623b4be7e3ac16cc8a736ed9, 165,438 B). The script is copied next to this file as
split-css.mjs. It base64-decodes each url(data:image/(png|svg+xml);base64,…) into img/<sha8>.<ext>, rewrites the
reference to url(img/<sha8>.<ext>), and throws on any other data:. The output has 0 data:, 0 @import, 0 @font-face;
its only url() references are the 6 image files. The SVG files contain no <script> and no href.
Readable alternative (unminified, derived), if you want a diffable file: toastui-editor-all.js 1,461,692 B
(sha256 0d8c202706e3ea8d91c28307c445b55b746cf985d0994d70951b257ba207c8d9), toastui-editor.css 32,958 B
(sha256 5e0a615d740f7f0ddb44f8dfab6f37d5fc32d34b6beb924f3876429b87fb570f), 6 images (4 svg with different bytes), total 1,574,902 B
before the LICENSE (its 6 images differ from the min set only in the 4 svg files). I recommend the minified one: 2.4x smaller, same code, same Function sites.
Grep results (on the files I would vendor; JS is byte-identical in both variants of each pair)
Method: node regex over the file read as latin1 (ctx.mjs / one-line node), because this shell's grep is ugrep and fails on
long context patterns. Counts on toastui-editor-all.min.js (the unminified file gives the same except where noted):
| Pattern | Hits |
|---|---|
eval( |
0 |
new Function |
1 |
Function( (not preceded by identifier or .; includes the new Function( site) |
2 |
.constructor("…") |
0 |
importScripts, new Worker |
0 |
XMLHttpRequest, fetch(, sendBeacon, WebSocket |
0 |
document.write |
0 |
setTimeout("…") / setInterval("…") string form |
0 |
insertAdjacentHTML |
3 (5 in the unminified file; I did not trace what feeds them) |
The two Function sites:
@25145:n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}()webpack global shim.globalThisexists in every supported browser, so the first branch returns.@488455:…typeof window&&window.Math===Math?window:Function("return this")()lodash-style root detection, last in a chain afterglobalThis/self/window.
Under the Console CSP these would throw an EvalError and fall into a catch if ever reached; the editor never reaches them in a browser.
External URLs in the bundle (distinct): three XML namespace strings (w3.org), a prosemirror.net docs link in a message, and
https://www.google-analytics.com/collect. The last is the analytics ping, sent through an <img> by default.
usageStatistics defaults to true; row 148 must pass usageStatistics: false. With the CSP it would be blocked anyway, but the
CSP violation would be noise and the code path writes localStorage.
Runtime probe (the Console CSP, verbatim, on a scratch server at 127.0.0.1)
Page loads the vendored files over script-src 'self', style-src 'self' 'unsafe-inline', no img-src, connect-src 'self'.
A securitypolicyviolation listener plus error and unhandledrejection listeners recorded everything.
| Variant | Editor constructs | CSP violations | JS errors | Toolbar |
|---|---|---|---|---|
As shipped (upstream CSS with data: images) |
yes | 1 (img-src, data:) |
0 | icons blank |
| Derived CSS, unminified | yes | 0 | 0 | 17 of 17 icons render |
| Derived CSS, minified | yes | 0 | 0 | 17 of 17 icons have an image |
Network for the derived pages: html, css, probe.js, the JS bundle, run.js, one PNG. Nothing left the origin.
Behaviour check on the minified derived page, setMarkdown then Markdown tab then WYSIWYG tab then getMarkdown():
- Headings, task list, fenced code, quote, strike, inline code and link survived.
- Rewrites untouched content: the bullet markers
-came back as*. Row 148's save will turn a hand-written-list into a*list on any edit. That is Git diff noise on the north-star spec, not data loss. I did not test tables, nested lists, or long files in round trip. - Sanitizing: a pasted
<script>block was dropped;<img onerror=…>came back as<img src="x">;window.__pwnstayed unset. Raw HTML in a spec is therefore not preserved. - The task-list checkbox picture (the SVGs) loads from
'self'.
Not tested: the image dialog, link popup, table context menu, code-block language picker, paste from the clipboard, IME input, and any file over a few kB. The probe ran in a Chromium instance only.
Rejected
| Candidate | Version | Reason |
|---|---|---|
| Wysimark standalone | 3.0.20 | Fetches emoji data from cdn.jsdelivr.net and calls api.portive.com; Prism worker; Function("return this"); 2.9 MB; last release 2023-09. Needs CSP changes. |
| Vditor | 4.0.0 | Function('"use strict";return (…)')() helper evaluates strings; importScripts Blob worker; injects inline <script> text (blocked by script-src 'self'); loads from unpkg by default; 3.7 MB Lute core. |
| EasyMDE | 2.21.0 | No eval, but not WYSIWYG (CodeMirror source plus preview, no better than the fallback). Fetches spell-checker dictionaries from cdn.jsdelivr.net and Font Awesome from maxcdn by default. |
| Milkdown / Crepe | 7.22.3 | Not downloaded. Registry metadata only: no prebuilt browser bundle, needs a bundler and dozens of transitive packages; the vendored file would be our own build output, not a published artifact. |
| MDXEditor | 4.3.3 | Not downloaded. Same reason, and it is React-only. |
| bytemd | 1.22.0 | Not downloaded. Same reason; it is also a split-pane source editor. |
| TinyMCE | 8.10.0 | Not downloaded. Not a Markdown editor, and the license field reads "SEE LICENSE IN license.md", not plainly permissive. |
Risks to weigh
- Toast UI's last release is 2023-02-17 and it bundles DOMPurify 2.3.3, which is old; the sanitizer is the thing that stops pasted HTML, so a known DOMPurify bypass would matter. I did not check DOMPurify 2.3.3 advisories.
- The standalone bundle is not npm-verifiable (see provenance gap). The sha256 values above pin what I downloaded, and no more.
- The stylesheet is modified from upstream, and
vendor.sha256will pin the derived files, not upstream's.
Fallback
If you rule against Toast UI: a <textarea> plus a rendered preview from a small Markdown renderer written in the repository
(headings, lists, emphasis, code, links, tables), no vendored code, no CSP change, no license file. It loses WYSIWYG editing.
That is the brief's own fallback.
Cleanup
Scratch server on 127.0.0.1:18148 and the isolated browser page are stopped after this write-up. All downloads stay in scratch.
Step 1b: DOMPurify 2.3.3 advisories and Toast UI provenance
Researcher, 2026-10-16T17:58Z. Same scratch rules as step 1: nothing under public/ changed, no claim, nothing pushed, no credential
used. Only unauthenticated reads (GitHub advisory API, OSV API, npm registry, raw GitHub files). Evidence lives in
/mnt/storage/scratch/researcher/q148-1b/ (adv/, tag/, dompurify-meta/) and the harness pages in
/mnt/storage/scratch/researcher/q148-lib/harness/site/. No downloaded script was run; the PoC test suite from the DOMPurify fix
commit was read, not executed.
1. DOMPurify 2.3.3: advisories
Method: GitHub Advisory API for cure53/DOMPurify (all 30 advisories saved in adv/dompurify-advisories.json) and the OSV query
for npm/[email protected] (adv/osv-dompurify-2.3.3.json, 20 hits). I then read each advisory text and checked each against what
Toast actually calls. The two sources disagree on ranges: OSV marks 20 as introduced=0, GitHub gives lower bounds only for some.
Many of the 20 describe options or code that I did not find in 2.3.3 (see the "not reachable" rows). I list the 20 OSV hits; the
other 10 GitHub advisories have a lower bound above 2.3.3 (3.0.x or later) and are left out.
What Toast passes to DOMPurify (call-site inspection of toastui-editor-all.js)
- One wrapper:
sanitizeHTML(html, options)->purify_default().sanitize(html, {ADD_TAGS: <array>, ADD_ATTR: ['rel','target','hreflang','type'], FORBID_TAGS: [...], ...options}). - Four module-level callers:
@1183440(img[src]parseDOM,RETURN_DOM_FRAGMENT: true),@1193888(a[href]parseDOM, same),@1224346(getMatchedAttributeValue, string result intoinnerHTML),@1240027and@1241321(markdown-to-WYSIWYG inline/block HTML convertor, string result). - Never set:
IN_PLACE,USE_PROFILES,SAFE_FOR_TEMPLATES,setConfig(),CUSTOM_ELEMENT_HANDLING, Trusted Types options, function-formADD_TAGS/ADD_ATTR.CUSTOM_ELEMENT_HANDLINGdoes not exist in the 2.3.3 source at all (0 hits). I found no DOMPurify hook registration (the singleaddHook(hit is the editor's own events hook,@1318242). - Output goes back in through
innerHTML(into adiv) or through ProseMirrorparseDOM.
Advisories that matter for 2.3.3
| GHSA | CVE | Sev (CVSS) | Affects 2.3.3 | Fixed in | Source | Reachable via Toast's config | Stopped by script-src 'self', no unsafe-inline |
|---|---|---|---|---|---|---|---|
| GHSA-gx9m-whjm-85jf | CVE-2024-47875 | critical (10.0) | yes, <2.5.0 |
2.5.0, 3.1.3 | https://github.com/advisories/GHSA-gx9m-whjm-85jf | Yes. Nesting-based mXSS, default config. I verified on 2.3.3 in Chromium that there is no nesting-depth limit (2.5.0 added it); purify-depth.html. |
Script effects: yes. Markup it yields is not filtered by FORBID_TAGS/ADD_TAGS because the mutation happens after the filter. Inline handlers, javascript: URLs and inline <script> do not run under the CSP (tested, section 2). HTML injection (links, inline-style overlays) is not stopped. |
| GHSA-mmhx-hmjr-r674 | CVE-2024-45801 | high (7.0) | yes, <2.5.4 |
2.5.4, 3.1.3 | https://github.com/advisories/GHSA-mmhx-hmjr-r674 | Partly. It bypasses the depth check added in 2.5.0, and 2.3.3 has no depth check, so it is covered by the row above. The prototype-pollution half needs a pollution primitive in the page; I found none in Toast and did not look elsewhere. | Same as above for the nesting half; pollution half is independent of the CSP. |
| GHSA-p3vf-v8qc-cwcr | CVE-2024-48910 | critical (9.1) | yes, <2.4.2 |
2.4.2 | https://github.com/advisories/GHSA-p3vf-v8qc-cwcr | Only with a separate prototype-pollution bug in the page. No such primitive found in Toast; I did not audit the Console. | No (it tampers with DOMPurify's own config; the follow-on effect is the same HTML injection). |
Advisories whose OSV range includes 2.3.3 but whose code path Toast does not use
All "Reachable: no" claims below come from the Toast call-site list above, not from running a PoC. For each, "affects 2.3.3" is
the OSV range claim (introduced=0); I did not test whether 2.3.3 actually has the flaw, because the option is never set.
| GHSA | CVE | Sev | Fixed in | Source | Why not reachable via Toast |
|---|---|---|---|---|---|
| GHSA-39q2-94rc-95cp | CVE-2026-65903 | low | 3.4.0 | https://github.com/advisories/GHSA-39q2-94rc-95cp | needs function-form ADD_TAGS; Toast passes an array |
| GHSA-h7mw-gpvr-xq4m | CVE-2026-41240 | medium | 3.4.0 | https://github.com/advisories/GHSA-h7mw-gpvr-xq4m | same |
| GHSA-cjmm-f4jc-qw8r | CVE-2026-65912 | medium | 3.3.2 | https://github.com/advisories/GHSA-cjmm-f4jc-qw8r | needs function-form ADD_ATTR; Toast passes an array |
| GHSA-cj63-jhhr-wcxv | CVE-2026-65913 | medium | 3.3.2 | https://github.com/advisories/GHSA-cj63-jhhr-wcxv | needs USE_PROFILES plus Array.prototype pollution |
| GHSA-crv5-9vww-q3g8 | CVE-2026-41239 | medium | 3.4.0 | https://github.com/advisories/GHSA-crv5-9vww-q3g8 | needs SAFE_FOR_TEMPLATES with RETURN_DOM; Toast does not set it |
| GHSA-vhxf-7vqr-mrjg | CVE-2025-26791 | medium | 3.2.4 | https://github.com/advisories/GHSA-vhxf-7vqr-mrjg | needs SAFE_FOR_TEMPLATES |
| GHSA-h8r8-wccr-v5f2 | CVE-2026-65914 | medium | 3.3.2 | https://github.com/advisories/GHSA-h8r8-wccr-v5f2 | Unknown. GitHub lists only 3.3.1; OSV says from 0. mXSS by re-contextualisation, no option needed. I did not test it on 2.3.3. Treat as possibly reachable; same CSP effect as the first row. |
| GHSA-55q2-fjhq-7xh7 | CVE-2026-75838 | low | 3.4.13 | https://github.com/advisories/GHSA-55q2-fjhq-7xh7 | IN_PLACE plus hooks; not set |
| GHSA-6688-9rhm-gjv2 | CVE-2026-109345 | low | 3.4.16 | https://github.com/advisories/GHSA-6688-9rhm-gjv2 | IN_PLACE; not set |
| GHSA-hpcv-96wg-7vj8 | CVE-2026-49458 | medium | 3.4.6 | https://github.com/advisories/GHSA-hpcv-96wg-7vj8 | IN_PLACE; not set |
| GHSA-r47g-fvhr-h676 | CVE-2026-49459 | medium | 3.4.6 | https://github.com/advisories/GHSA-r47g-fvhr-h676 | IN_PLACE; not set |
| GHSA-x4vx-rjvf-j5p4 | CVE-2026-65901 | low | 3.4.7 (OSV gives no fix; GitHub says <= 3.4.6) |
https://github.com/advisories/GHSA-x4vx-rjvf-j5p4 | IN_PLACE; not set |
| GHSA-rp9w-3fw7-7cwq | CVE-2026-49978 | medium | 3.4.7 | https://github.com/advisories/GHSA-rp9w-3fw7-7cwq | IN_PLACE; not set |
| GHSA-76mc-f452-cxcm | CVE-2026-65902 | medium | 3.4.7 | https://github.com/advisories/GHSA-76mc-f452-cxcm | needs a DOMPurify hook mutating allow-lists; Toast registers none |
| GHSA-cmwh-pvxp-8882 | CVE-2026-65898 | low | 3.4.11 | https://github.com/advisories/GHSA-cmwh-pvxp-8882 | needs setConfig(); Toast does not call it |
| GHSA-c2j3-45gr-mqc4 | CVE-2026-66010 | low | 3.4.12 | https://github.com/advisories/GHSA-c2j3-45gr-mqc4 | CUSTOM_ELEMENT_HANDLING; absent from 2.3.3 |
| GHSA-vxr8-fq34-vvx9 | CVE-2026-65899 | low | 3.4.9 | https://github.com/advisories/GHSA-vxr8-fq34-vvx9 | Trusted Types policy; not set |
Not applicable (GitHub lower bound above 2.3.3): GHSA-87xg-pxx2-7hvx, GHSA-v9jr-rg53-9pgp, GHSA-4w3q-35jp-p934, GHSA-9p3w-6h5p-cv75,
GHSA-gvmj-g25r-r7wr, GHSA-c6gr-qvv4-5v3f, GHSA-mrv2-3pqr-j5jr, GHSA-cg2g-xh64-v4q7,
GHSA-p98j-92pf-mc4p. (These are all 3.x-only; I did not read their text beyond the ranges.) OSV has no entry for @toast-ui/[email protected]
itself (adv/osv-toastui-3.2.2.json is {}). That is absence of a record, not a clean bill: Toast is unmaintained since 2023-02.
2. Does the Console CSP stop the effect? (empirical, Chromium, the exact CSP from serve.mjs:209)
Page: csp-effects.html/js on the scratch server. Each payload was inserted with innerHTML into a same-origin page under the CSP,
which is what Toast does with sanitizer output.
| Payload | Result under the CSP |
|---|---|
<img src=x onerror=...> / <svg onload=...> inline handlers |
blocked (script-src has no unsafe-inline), violation reported |
<a href="javascript:..."> click |
blocked |
<iframe srcdoc="<script>..."> |
blocked (srcdoc inherits the policy) |
<img src=https://other.example/x> |
blocked (img-src falls back to default-src 'self'), so no beacon via images |
<link rel=stylesheet href=https://...>, CSS url(https://...) |
blocked |
<form action=https://other.example> |
blocked (form-action 'self') |
Plain <a href=https://other.example> |
not blocked: user can be sent off-site by a click |
<meta http-equiv=refresh content="0;url=https://..."> |
not blocked by the CSP. Toast's FORBID_TAGS drops meta, so it only matters after an mXSS bypass. |
Inline style= (full-page overlay, fake UI) |
not blocked: style-src 'unsafe-inline' is in the Console CSP |
<form action=/same-origin-path method=post> |
not blocked (form-action 'self'); form is in Toast's FORBID_TAGS but not after mXSS. Whether a same-origin POST can do harm depends on Console routes I did not review. |
So the CSP turns the critical advisories from script execution into HTML injection. It does not make 2.3.3 safe: the Console
shows a human content that an agent can write (agents author the docs/ files this editor opens), so link redirection, overlay
spoofing and same-origin form POST remain possible after an mXSS bypass.
3. Provenance
| Question | Finding |
|---|---|
| Tag | [email protected] (GitHub tag name; @toast-ui/[email protected] returns 404). Commit 9b94c04231d4600b42347ff1b99d9813e9becf67. |
| GitHub release | No release object for this tag; tag only (tag/tags-list.json, tag/releases.json checked). |
toastui-editor-all.min.js in tag or release assets |
No. The repo tree at the tag (tag/tree.json) has no dist/ and no bundle; no release assets. |
| Reproducible build | Not reproduced. The build is a webpack run via upstream scripts and npm install of its dev dependencies. Both are disallowed here (no install, no running downloaded scripts). I cannot say a build from the tag would produce f50e1b7c… |
| Second source | None found. The bundle exists only at uicdn.toast.com/editor/3.2.2/ (CDN of the vendor); the npm tarball has dist/toastui-editor.js (not standalone). I compared line content earlier: 98.62% of the npm dist lines occur verbatim in the all.js. |
| CSS | Upstream CDN CSS equals the npm CSS byte for byte (c0ceb967… unminified). The tag's own style sources (tag/src-contents.css, tag/src-editor.css) were fetched only to compare the images. |
| Images | The 2 PNGs extracted from the CSS match the PNGs in the tag source byte for byte. The 4 SVGs in the unminified CSS match the tag source; the 4 SVGs in the minified CSS differ in bytes (the minifier rewrote them). The minified set is therefore not byte-identical to source for SVGs; it is what the CDN served. |
| LICENSE at tag vs master | Identical. tag/LICENSE-tag-editor-3.2.2 sha256 1c7c070da956f0c3122883c1b4eb706b1b819971082ccde001fe2d241d90e3c0, 1072 B, same as the master copy in step 1. Row 148 can vendor it from the tag with a recorded source URL. |
Net: the vendored JS is pinned to a CDN download with no independent confirmation. A reviewer can re-fetch the same URL and compare the sha256 and that is all; there is no upstream-signed hash and no reproducible build I could run.
4. If a 1b finding is bad
It is bad, in the sense that the critical nesting mXSS (GHSA-gx9m-whjm-85jf) is reachable with Toast's default config and the CSP only converts it into HTML injection.
One line: I would not rely on customHTMLSanitizer alone, because four internal call sites (img/a parseDOM, getMatchedAttributeValue,
and the markdown-to-WYSIWYG HTML convertor) call the bundled 2.3.3 directly and ignore it; so the choice is to splice DOMPurify 2.5.9 (latest 2.x, same major as the
bundled 2.3.3, so the sanitize() options Toast uses stay the same; it fixes both 2024 advisories, and no 2.x-range advisory above lists a fix later than 2.5.4) into
the bundle's module slot (the vendored JS then becomes derived like the CSS and loses its single upstream sha256; untested, I
would download 2.5.9 into its own scratch dir, check npm integrity, then run the nesting test, the Toast integration page and the
0-violation CSP check before any build), or fall back to textarea plus preview if that cannot be verified or a derived JS file is refused.
DOMPurify 3.4.17 is the alternative splice, with more API drift from 2.3.3 to check.
Not verified
- A splice of a newer DOMPurify into the bundle (not built, not run).
- Whether 2.3.3 is actually vulnerable to GHSA-h8r8-wccr-v5f2 and the other OSV-only entries; I judged them by call-site reachability.
- Whether the Console has a same-origin POST route that a forged
<form>could hit. - A pollution primitive anywhere in the Console page.
- Anything in Firefox or Safari; all runtime tests were in one Chromium.