Skip to content

Update dependency jodit to v4.12.26 [SECURITY]#1013

Open
renovate[bot] wants to merge 1 commit into
developfrom
renovate/npm-jodit-vulnerability
Open

Update dependency jodit to v4.12.26 [SECURITY]#1013
renovate[bot] wants to merge 1 commit into
developfrom
renovate/npm-jodit-vulnerability

Conversation

@renovate

@renovate renovate Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

ℹ️ Note

This PR body was truncated due to platform limits.

This PR contains the following updates:

Package Change Age Confidence
jodit (source) 4.7.94.12.26 age confidence

jodit: Prototype pollution in Jodit via Jodit.modules.Helpers.set()

CVE-2026-55886 / GHSA-vpmm-x3fm-qr5c

More information

Details

Summary

Jodit.modules.Helpers.set(chain, value, obj) walks the dot-separated chain, creating and following each path segment, without filtering prototype-mutating keys. A chain that begins with (or contains) __proto__, constructor, or prototype lets the final assignment reach and mutate Object.prototype (prototype pollution).

Affected
  • Package: jodit (npm)
  • Versions: < 4.12.26
  • Public API: Jodit.modules.Helpers.set(chain, value, obj)
Proof of Concept
const { Jodit } = require('jodit');
delete Object.prototype.polluted;
Jodit.modules.Helpers.set('__proto__.polluted', 'yes', {});
console.log(({}).polluted); // "yes" (before the fix)
delete Object.prototype.polluted;
Impact

Applications that pass a user-controlled or partially user-controlled key path into Jodit.modules.Helpers.set() could be vulnerable to prototype pollution (CWE-1321): unexpected property injection, logic bypass, denial of service, or secondary security issues.

Patch

Fixed in 4.12.26 by rejecting any chain whose segments include __proto__, constructor, or prototype, reusing the same guard introduced for Jodit.configure() in 4.12.18.

Credit

Responsibly reported by Junming Wu.

Severity

  • CVSS Score: 6.9 / 10 (Medium)
  • Vector String: CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:L/SC:N/SI:N/SA:N

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


Release Notes

xdan/jodit (jodit)

v4.12.26

Compare Source

🐛 Bug Fix
  • Security / Helpers (prototype pollution): Jodit.modules.Helpers.set(chain, value, obj) walked the dot-separated chain and created/followed each segment without filtering prototype-mutating keys, so a chain such as __proto__.polluted (e.g. set('__proto__.polluted', 'yes', {})) could reach and mutate Object.prototype (CWE-1321). set now bails out when any segment is __proto__, constructor, or prototype, reusing the same guard added for Jodit.configure() in 4.12.18. Responsibly reported by Junming Wu.

v4.12.25

Compare Source

🐛 Bug Fix
  • Source mode / complex scripts (Thai, Arabic, Hindi, Hebrew, Persian): the ACE source editor was loaded from CDN at version 1.4.2 (2018), which predates working bidirectional-text and combining-character support — complex-script text rendered misaligned with the underlying code ("invisible characters" after lines), the caret/selection landed on the wrong characters and copying lost as many characters as there were combining marks. The default sourceEditorCDNUrlsJS now points to ACE 1.43.3, which ships an automatic per-line bidi handler and years of complex-script rendering fixes; all existing source-mode integration tests pass against the new build, plus new regression tests for the Thai round-trip and the bidi layer.

v4.12.24

Compare Source

🚀 New Feature
  • Link dialog: new opt-in option link.ariaLabelInput (default false) adds an Aria label text field to the Insert/Edit link form. It reads the existing aria-label when editing and writes it to the <a> on submit (clearing it when empty) — useful for accessibility when several links share the same visible text (e.g. multiple "here" links a screen reader can't tell apart). Addresses #​1204.
  • Image editor: new afterImageEditorSave event, fired when the user clicks Save / Save as in the image editor (crop/resize). The handler receives the action box { action: 'resize' | 'crop', box } (and the new name for Save as), so you can react to the applied crop/resize — e.g. update the image's aspect ratio or set its real size. The existing afterImageEditor only fired when the editor opened. Addresses #​820.
  • Uploader: new uploader.beforeUpload(files) hook, called with the file list right before upload (or base64 read). Return false to abort — useful for client-side validation of size/type/count; this is the uploader (so this.j is the editor). Addresses #​1329.
  • Table / Select cells: Ctrl/Cmd + click now toggles individual cells into a non-contiguous selection (clicking an already-selected cell removes it), in addition to the existing click-and-drag rectangular selection. The cell properties popup opens for the accumulated selection, and toolbar actions (background color, etc.) apply to every selected cell. Previously each Ctrl+click reset the selection to the single clicked cell. Fixes #​1163.
  • Backspace/Delete: new option delete.disableCases (a Set<string>) lets you turn off individual Backspace/Delete cleanup behaviors that the plugin applies after the native deletion — e.g. delete: { disableCases: new Set(['join-neighbors']) } stops Backspace at the start of a paragraph from merging it into the previous one. Available keys: remove-unbreakable, remove-not-editable, remove-char, table-cell, remove-empty-parent, remove-empty-neighbor, join-two-lists, join-neighbors, unwrap-first-list-item. Addresses #​1060.
  • Clean HTML: new opt-in option cleanHTML.collapseEmptyValueToEmptyString (default false). When the editor holds only a single empty block — e.g. <p><br></p> left in the DOM after the user deletes all content (contenteditable keeps that caret container) — editor.value and the synced source element now return an empty string '' instead of <p><br></p>, which is what forms usually expect on submit. Real content (including a <p><br></p> followed by other blocks) is never collapsed. Addresses #​1149.
🐛 Bug Fix
  • Font select button: with the font control rendered as a select (controls: { font: { component: 'select' } }), unstyled text showed the editor's raw default font stack (e.g. -apple-system) on the button instead of Default. The font control's value now returns an empty value when the computed font-family equals the editor's own default, so the button shows the Default list entry. Fixes #​1370.

v4.12.23

Compare Source

🐛 Bug Fix
  • Search (Ctrl+F): pressing Enter in the search input submitted the parent <form> instead of jumping to the next match — the preventDefault() for Enter lived inside a debounced handler, so it ran only after the browser had already dispatched the form submission. Enter is now canceled synchronously, while the search logic itself stays debounced. Fixes #​918.
  • i18n / language bundles: translations for keys that were missing from ar.js never reached the build — the master i18n key list was generated only from ar.js, and every other language is serialized as an array indexed by that list, so existing translations for the ordered-list menu items (Lower Alpha, Lower Greek, Lower Roman, Upper Alpha, Upper Roman — translated in de/es/fi/it/ja/pt_br) and a dozen other keys were silently dropped from every bundle. The key list is now built as the union of all language files. Fixes #​997.
  • Custom ownerWindow (editor created inside an iframe): UI components (toolbar collection, popups, dialogs) always kept the global window as their owner window instead of the configured one. Popup/dropdown outside-click close handlers were therefore bound to the wrong window — clicks inside the iframe never reached them, so toolbar dropdowns did not close on an outside click. ViewComponent now inherits the owner window from its parent view. Fixes #​965.
  • Toolbar content controls (getContent): controls rendered through getContent (e.g. the FileBrowser Upload button) never ran the status calculation, so their isDisabled/isActive/update callbacks were silently ignored — the Upload button stayed enabled even when the backend permissions (canI('FileUpload')) said otherwise. ToolbarContent.update() now evaluates the control's status like regular toolbar buttons and also propagates the disabled state to nested form controls (the file <input> of the Upload button). Thanks @​WatchfulEyeOfZod for the diagnosis and the patch draft. Fixes #​1094.
  • Inline toolbar / Iframe mode: with iframe: true the selection toolbar (toolbarInlineForSelection) opened far away from the selected text — the popup was positioned by range.getBoundingClientRect(), whose coordinates are iframe-local, while the popup itself lives in the host document. The iframe offset is now added to the selection bound. Fixes #​1058.
  • Paste toolbar button: the Paste from clipboard toolbar button inserted only the plain text flavor of the clipboard, while Ctrl+V pasted the full HTML — the button explicitly requested text/plain from navigator.clipboard.read() even when text/html was available. The button now prefers the HTML flavor (falling back to plain text), so both paste paths behave the same. Fixes #​1061.
  • Format block (H1–H6 / paragraph): applying a heading to a block pasted from an external source seemed to do nothing — the tag actually changed, but leftover inline styles like font-weight: normal; font-size: 24px visually overrode the new format, so an <h2> looked exactly like the old text. When a block format is applied, the conflicting font-size/font-weight inline styles are now removed from the block (other styles like color stay). Fixes #​1063.
  • Backspace/Delete next to a table: pressing Backspace at the start of a paragraph that follows a table (or Delete at the end of a paragraph before one) merged the paragraph's text into the <table> element itself — it landed after </tbody>, which is invalid HTML; on the next parse the browser foster-parents such content out of the table, corrupting the document and causing follow-up DOM errors (the Failed to execute 'appendChild' console spam from the report). The join logic now merges the text into the edge table cell (last cell for Backspace, first for Delete). Covers #​1064.
  • Paste from Word (askBeforePasteFromWord): office content was frequently mis-detected as plain HTML, so the generic Paste as HTML dialog appeared instead of the Word one. The detector only matched a Microsoft Word N meta generator or mso- styles in double quotes combined with <font> tags — while the raw Word clipboard fragment uses single-quoted style='mso-…', unquoted class=MsoNormal, an unquoted ProgId meta and office XML namespaces (and modern Word emits no <font> at all); LibreOffice/OpenOffice content was never detected. The detector now also recognizes all of these markers. Fixes #​1078.
  • Drag and drop (enableDragAndDropFileToEditor: false): dropping a file (e.g. a JPG from another window) into the editor still embedded it — with the option off no handler was bound to the drop event, nothing called preventDefault, and Firefox inserted the dropped image natively. With the option disabled the editor now cancels file drops, so they do nothing — as the option promises. Fixes #​1077.
  • AI Assistant: the Insert After button placed the generated text after the first node of the selection — with several selected paragraphs the result landed inside/after the first paragraph instead of after the whole selection. The insertion point is now the collapsed end of the selection, so the result follows everything that was selected; single-word/sentence behavior is unchanged. Fixes #​1263.
  • Color / Select cells: applying a text or background color from the main toolbar to table cells selected with the select-cells plugin (click or drag-select a cell — typical for tables pasted from Word) silently did nothing: the cell selection drops or collapses the native range, so the color was committed as a pending caret format in an empty <span> outside the table. When cells are selected and the native selection is collapsed, the forecolor/background commands now recolor the content of every selected cell (including nested spans carrying their own Word colors). Fixes #​1250.
  • Shadow DOM / Table: clicking a table cell inside a Shadow DOM editor (the shadowRoot option, e.g. a Stencil/web component integration) never opened the cell properties popup (background, vertical align, split/merge, add/remove row & column). document.elementFromPoint does not pierce shadow boundaries — it returns the shadow host — so the select-cells plugin could not find the clicked cell on mouseup. The hit-testing now starts from the configured shadow root in select-cells (cell selection on click and drag-select), add-new-line (the Break line over tables/images) and Popup.getKeepBound. Fixes #​1312.

v4.12.22

Compare Source

🐛 Bug Fix
  • Clipboard (copy/cut inside the editor): copying or cutting a selection that sat entirely inside the text of a formatted element (e.g. selecting part of a bold-italic word with the mouse) put the bare text into the clipboard — the <strong>/<em>/… context was lost, so pasting it elsewhere dropped the formatting. Native browser copy keeps that context, but Jodit intercepts copy/cut and serialized only the range contents. The copied fragment is now wrapped in shallow clones of the selection's inline ancestors, like browsers do. Fixes #​1202.
  • Source mode / multiple editors: switching an editor into source mode focused its code mirror unconditionally — when the switch happened programmatically (e.g. a Vue/React wrapper re-initializing the editor) it pulled the focus out of the editor the user was typing in. The mirror is now focused only when the focus is not already inside another editor or control. Fixes #​1356 (follow-up to #​1313).
  • Limit (limitChars + countTextSpaces): the character limiter never counted spaces — even with countTextSpaces: true the limit applied to non-space characters only (limitChars: 10 allowed I am only 8 because only 8 visible characters were counted), while the status-bar counter showed the space-inclusive number. With countTextSpaces: true the limiter now counts characters the same way as the counter — including spaces. Fixes #​1144.
  • Value assignment re-requested images (jodit-react): every editor.value = … assignment parsed the HTML into a helper element of the live document to sanitize it, so the browser issued a network request for every <img> in the value — with wrappers that sync the value on each change (e.g. jodit-react onChange), the image URLs were re-requested on every click/keystroke. The sanitize sandbox is now an inert document (implementation.createHTMLDocument), which never loads sub-resources; Dom.isElement/Dom.isHTMLElement no longer require a browsing context (defaultView), so the sanitizer keeps working on inert nodes. The internal safeHTML hook is now fired as an event for extensibility. Fixes #​1237.
  • Inline toolbar (toolbarInlineForSelection): clicking a button in the selection toolbar (e.g. Bold) applied the command but closed the toolbar, even though the selection was still active — applying several formats required re-selecting the same text again and again. After a command fired from inside the popup, the selection toolbar is now reopened while the selection is not collapsed. Fixes #​1238.
  • Clean HTML (allowTags): when cleanHTML.allowTags was configured as a map with per-tag attribute rules (e.g. {p: true, a: {href: true}}) and the content contained a tag outside the list that had attributes (e.g. <meta charset="utf-8"> from pasted e-mail HTML), the cleanup worker threw TypeError: Cannot read properties of undefined on every pass — the editor "crashed" on the first click and disallowed tags were never removed. The attribute filter now skips tags that are not in the allow list (the tags filter removes them). Fixes #​1224.
  • Paste (plain text): pasting plain text containing a stray < (e.g. TD}gy{<KMj3ScW1[efbi) lost everything after the bracket — the clipboard text was inserted through the HTML path, so <KMj3… was parsed as an unclosed tag and swallowed the rest of the string. When the clipboard contains only text/plain (and it is not actual HTML), special characters are now escaped before insertion, so <, > and & survive as literal text; the nl2brInPlainText line-break conversion still applies after escaping. Fixes #​1227.
  • i18n (Turkish): the Word/Excel paste prompt ("The pasted content is coming from a Microsoft Word/Excel document…") was shown in German when the editor language was set to Turkish — langs/tr.js contained a copy of the German translation for that key. Replaced with the proper Turkish text. Fixes #​1245.
  • Dialog: a dialog (e.g. Paste as HTML) could be resized by its corner handle to a size smaller than its own header, content and footer, so the footer buttons ended up rendered outside the dialog box. Interactive resizing is now clamped to a minimum size: dialog.minWidth/dialog.minHeight options when set, otherwise the sum of the header, the content's CSS min-height and the footer (and the footer buttons' row width). Fixes #​1262.
  • Resize handler / Iframe: resizing the editor itself by the statusbar handle was jerky in iframe mode — while dragging, the editor height jumped between two values. Same root cause as the image-resizer fix below: mousemove events proxied from the editor's iframe carry iframe-viewport coordinates and were mixed as-is with host-window ones; they are now shifted into the host coordinate space (checked via e.view). Fixes #​1287.
  • Resizer / Iframe: resizing an image (or table) by its handles was jerky in iframe mode (e.g. with editHTMLDocumentMode) — the size jumped while dragging, making it impossible to set precisely. During a drag, mousemove events arrive both from the host window (pointer over the resize frame) and proxied from the editor's iframe (pointer over the content), but the iframe-viewport coordinate correction was applied to all of them, so host-window events were shifted by the workplace offset. The correction is now applied only to events that originate from the iframe window (e.view). Fixes #​1264.
🏠 Internal
  • Popup tests: added a regression test asserting that with allowTabNavigation: true an inner popup (a dropdown opened from a button inside another popup — e.g. the image Horizontal align list) is positioned next to its trigger button. With that option the inner popup is appended into the button inside the outer popup, whose CSS transform shifts position: fixed coordinates; covers #​1265 (already fixed in 4.12.8 by the getFixedPositionOffset containing-block compensation from #​1350).
  • Shadow DOM tests: added regression tests asserting that with the shadowRoot option the image resizer frame, the image properties dialog (double click) and the inline popup all open correctly inside the shadow root. Covers the scenario from #​1109, which does not reproduce with a vanilla Shadow DOM.
  • Tooltip tests: added a regression test asserting that buttons inside the table-cell inline popup show their tooltip on hover. Covers #​1141, fixed back in 4.2.40.
  • Image-processor tests: added regression tests asserting that a base64 (data:) image is replaced with a blob: URL only in the view and never leaks into editor.value, the source element, the controlled-value loop (value re-assigned on every change, as jodit-react does) or the value left after destruct() (React remount). Covers the scenario from #​1244, which does not reproduce in the core editor.
  • Selection tests: added a regression test asserting that editor.selection.insertHTML() called after the editor lost focus (e.g. from a click handler on a non-focusable page element) inserts at the previously saved caret position instead of the start of the editor. Covers the scenario from #​1239, which does not reproduce on the current version in Chrome or Firefox.
  • Color tests: added a regression test asserting that applying a font color over a selection that includes a whole table recolors the text in every cell — including cells whose color sits on the <td> itself (as Word produces) rather than on inner spans. Covers the scenario from #​1221, which does not reproduce on the current version.
  • Select-cells tests: added a regression test asserting that the table cells popup stays open after a fast drag-select — including a pending trailing throttled mousemove, the Firefox redraw-hack cleanup, debounced history changes and the browser's click dispatched on the common TR ancestor. Covers the scenario from #​1174, which does not reproduce on the current version in Chrome or Firefox.
  • Paste tests: added regression tests asserting that Word's inter-word spaces placed in their own spans (<span style="letter-spacing:-.5pt"> </span>) survive pasting in every mode (keep, clean, text). Covers the scenario from #​1230, which does not reproduce on the current version (verified with the full table HTML from the issue).
  • Resizer tests: added a regression test asserting that in iframe: true + editHTMLDocumentMode: true mode the image resize frame is hidden when the iframe content is scrolled (the iframe plugin proxies the inner window's scroll event into the main window, where the resizer plugin listens). Covers the scenario from #​1266, which does not reproduce on the current version.

v4.12.21

Compare Source

🐛 Bug Fix
  • Security (stored XSS): the HTML sanitizer (safeHTML) stripped on* event handlers and javascript: links but left several executable constructs in the serialized editor value, so an application that re-rendered editor.value as trusted HTML could execute attacker script. It now also: drops iframe[srcdoc]; removes data:text/html / data:application/xhtml (and SVG data: URLs in iframe/object/embed/frame) sources; and strips javascript:/vbscript:/livescript:/mocha: from every URL-bearing attribute (src, data, action, formaction, poster, background, xlink:href), not just <a href>. Safe data:image/* sources (e.g. base64 PNG/SVG used in <img>) are preserved. Responsibly reported by Yuji Tounai (@​yousukezan).
  • Paste (Insert as Text): pasting multi-line content with the Insert as Text option lost its line breaks — the escaped text kept raw newlines that collapse to spaces when rendered. The Insert as Text path now converts newlines to <br> (gated on nl2brInPlainText, like the plain-text paste path). Fixes #​1093.
  • Hotkeys: the default keyboard shortcut for Insert Unordered List (Ctrl/Cmd+Shift+8) never fired — both variants were written as a single comma-joined string 'ctrl+shift+8, cmd+shift+8' instead of two separate array entries, so the combined value never matched a real keypress. Fixes #​1079.
  • Color / Brush button: the brush (text/background color) button never reflected the color under the caret. Its update handler computed the current color into the icon fill but then unconditionally reset icon.fill to an empty string on every toolbar update, discarding it — which also made the icon render invisibly against some themes (editorCssClass). The computed fill is now kept when a color is present (and only cleared when there is none). Fixes #​195 and #​182.
  • Toolbar / Selection: when a text selection was started inside the editor and the mouse button was released outside of the editable area (a drag-select that ends over the page), the editor never received the mouseup event, so the active state of toolbar buttons (Bold, Italic, etc.) was not recalculated and stayed stale. A document-level mouseup listener now re-fires changeSelection when the selection still belongs to the editor, so the toolbar updates correctly. Fixes #​1251.
  • Media embed (YouTube & Vimeo): real-world share URLs were not converted to an embedded player. For YouTube, short links of the form https://youtu.be/<id>?si=… (the Share button format) and ?t= timestamp links were inserted as plain text — the video id sits in the path but was only read when there was no query string — and /shorts/<id> / /live/<id> produced a broken embed/shorts/<id> src. For Vimeo, ?share=copy tracking params and channels/<name>/<id> / groups/<name>/videos/<id> URLs produced a broken video/channels/… src. convertMediaUrlToVideoEmbed now extracts the video id (and the unlisted Vimeo hash) from the URL path, ignoring tracking params and channel/group/embed/shorts/live prefixes, and also recognises the m.youtube.com (mobile) and music.youtube.com hosts. Fixes #​1209.
  • Indent / Lists: the Decrease Indent (outdent) toolbar button stayed disabled when the cursor was inside a nested list item, even though Shift+Tab could un-nest it. The button's enabled state only considered an inline indent margin and ignored list nesting. It is now also enabled when the cursor sits in a list whose parent is another list item (matching the tab plugin's outdent behaviour). Fixes #​1247.
  • Uploader (base64): with uploader.insertImageAsBase64URI = true, dropping or pasting images in formats outside the default imagesExtensions list — notably .svg, .bmp, .webp — failed with Need Url for Ajax Request instead of being inlined as a data URI (the file fell through to the server-upload path). The default imagesExtensions now also includes webp, bmp, svg, tiff and avif. Fixes #​1228.
  • Paste (Insert only Text): pasting multi-paragraph HTML with the Insert only Text option collapsed everything into a single paragraph — block boundaries became spaces, so the text could no longer be split into list items or separate blocks. stripTags gained an opt-in blockBr mode and the Insert only Text path now uses it (gated on nl2brInPlainText), so paragraphs are separated by <br>. The default stripTags behaviour (space-joined single-line plain text) is unchanged. Fixes #​1232.
  • Paste from Word: list items pasted from Microsoft Word kept their auto-generated marker text — the bullet glyph or the literal item number (1., 2., …) leaked into the content as plain text. Word emits these markers inside <span style="mso-list:Ignore"> elements that are explicitly flagged as display-only; both paste paths now drop those spans entirely instead of just stripping their attributes — cleanFromWord (the Clean / As text options) and applyStyles (the Keep / insert-as-HTML option). Fixes #​948.
🏠 Internal
  • Accessibility tests: added a regression test asserting that a toolbar button's aria-label is placed on the interactive <button> element (not only on the wrapper <span>), covering the toolbar-specific case of #​1261 (already fixed in 4.9.7).
  • Color tests: added a regression test for applying a single font color over a selection that already contains several different colors — every part of the text must be recolored, none left without a color, covering #​169 (already fixed in the v4 style engine).

v4.12.20

Compare Source

v4.12.18

Compare Source

🚀 New Feature
  • Drag and drop element: added a startDragElement event so another plugin can begin dragging a specific element programmatically — e.g. from a dedicated drag handle/anchor shown next to a block. Fire editor.e.fire('startDragElement', element, mouseEvent); the element does not need to be listed in draggableTags (the listener is registered even when that list is empty), so handles can move elements such as <pre> code blocks that are not auto-draggable.
🐛 Bug Fix
  • Formatting / Selection: toggling Bold/Italic/Underline (etc.) on a collapsed cursor and then clicking in the editor lost one or more of the pending formats. The click placed the caret just before the empty marker elements, so clean-html removed them. The caret is now moved back into the innermost pending marker on click, and clean-html keeps empty inline elements that hold the live caret, so the next typed character keeps every format. Fixes #​1291.
  • History / Enter: pressing Enter while a selection was active (e.g. Ctrl+A then Enter) required two Ctrl+Z presses to undo — the first only reverted to an intermediate empty state. The delete-of-selection and the new block are now a single history transaction, so one undo restores the original content. Fixes #​1292.
  • Security / Config (prototype pollution): Jodit.configure() — and the internal ConfigMerge/ConfigProto helpers — merged user-supplied options without filtering prototype-mutating keys, so a payload nested under an existing plain-object option such as controls (e.g. {"controls":{"__proto__":{"polluted":"yes"}}}) could reach and mutate Object.prototype (CWE-1321). Merging now rejects __proto__, constructor, and prototype at every nesting level. Responsibly reported by Junming Wu.
💅 Polish
  • Drag and drop element: dropping a non-editable block (e.g. a <pre> code sample) no longer leaves an invisible filler text node () next to it. Previously this stray node showed up as an extra empty line until clean-html removed it later; it is now stripped in onDrop right after insertion.

v4.12.17

Compare Source

🐛 Bug Fix
  • Lists / Selection: in a multi-level bulleted/numbered list, clicking to the right of an item that has a nested list put the caret at the start of the line instead of the end (a Blink/WebKit contenteditable quirk; Firefox was already correct). A :click handler now moves the caret to the end of the item's own text in that case. Fixes #​1296.

v4.12.16

Compare Source

🐛 Bug Fix
  • DTD / Formatting: applying an inline style (bold, underline, etc.) with the cursor on an empty line removed a <br> line break. The removeExtraBr cleanup (run after the style marker is inserted) deleted the following <br> even when real content followed it. It now only removes a trailing <br> (with nothing meaningful after it), so line breaks in the middle of a block are preserved. Fixes #​1302.

v4.12.15

Compare Source

🐛 Bug Fix
  • Table / Cell selection: pressing Backspace or Delete with several table cells selected threw HierarchyRequestError: ... insertNode ... #document (the normal delete ran against a collapsed, document-level range). Backspace/Delete on a multi-cell selection now clears the content of the selected cells instead. Fixes #​1273.

v4.12.14

Compare Source

🐛 Bug Fix
  • Backspace / Lists: pressing Backspace at the start of a nested list item deleted the last character of the parent item instead of removing the nesting. checkRemoveChar walked out of the nested <li>/list and removed a character from the parent item's text. It no longer crosses a list-item boundary, so the list cases handle the operation cleanly. Fixes #​1277.

v4.12.13

Compare Source

🐛 Bug Fix
  • Backspace: pressing Backspace at the start of a styled inline run (e.g. <em><strong><u>…</u></strong></em>) that was preceded by a <br> deleted the entire run instead of just the line break. checkJoinNeighbors walked up to the inline element and tried to merge its content into the preceding <br> (a void element), which removed the whole element. It now removes the <br> as a single unit. Fixes #​1282.

v4.12.12

Compare Source

🐛 Bug Fix
  • Fullsize: after entering fullsize mode, resizing the browser window, and then exiting fullsize, the editor kept the fullscreen width/height instead of returning to its original size. The resize handler (bound to window.resize when globalFullSize is on) re-saved the "original" size on every window resize, so it captured the fullscreen size. The original size is now stored only once when entering fullsize and restored on exit. Fixes #​1278.

v4.12.11

Compare Source

🐛 Bug Fix
  • Color / Apply style: applying the black font color (#000000) did nothing when the editor's default text color had been changed. The redundancy check in toggleAttributes measured the element's "native" value inside an isolated iframe, where the default text color is always the browser black — so black always matched and was skipped. The probe now inherits the editor's effective text color (the editor default is #222, not pure black), so an explicitly chosen color is applied correctly. Fixes #​1311. (Note: applying black to a link now keeps the anchor and sets the color, which matches the original intent of #​936.)

v4.12.10

Compare Source

v4.12.9

Compare Source

🐛 Bug Fix
  • Table / Cell selection: dragging and dropping the contents of several table cells into another cell threw TypeError: Cannot read properties of undefined (reading 'Infinity'). When the drag left a stale selection anchor and the drop target was no longer part of the table's formal matrix, getSelectedBound returned its Infinity sentinel and __onStopSelection dereferenced an out-of-range matrix slot. It now bails out gracefully when the selected cells can't be resolved in the table. Fixes #​1357.

v4.12.8

Compare Source

🐛 Bug Fix
  • Tooltip / Popup: tooltips and dropdown popups were mis-positioned (offset away from their button) when the editor was placed inside an ancestor with a CSS transform — e.g. a modal/flyout. Both elements use position: fixed, and a transformed ancestor becomes their containing block, so the viewport coordinates were shifted by that ancestor's top-left corner. Added a getFixedPositionOffset helper that returns the containing-block offset ({0, 0} when there is no transformed ancestor, so normal usage is unaffected) and subtracted it when positioning the tooltip and popup. Fixes #​1350.

v4.12.7

Compare Source

🐛 Bug Fix
  • Table: deleting a row could throw NotFoundError: Failed to execute 'insertBefore' on 'Node' for tables with mixed rowspans (e.g. after inserting a column and repeatedly splitting/merging cells). When a row with a rowspan cell was removed, the cell was moved into the next row and inserted before the cell that logically follows it — but that reference cell can physically belong to an earlier <tr> (when it spans down from a row above), which is not a child of the target row. __removeRow now picks the first following cell that actually belongs to the next row (falling back to appendChild). Fixes #​1358.

v4.12.6

Compare Source

🏠 Internal
  • CI / Screenshot tests: the Playwright screenshot job started failing with browserType.launch: Executable doesn't exist after Playwright 1.60.0 was published. The screenshots Docker image was pinned to the v1.58.0-jammy base but installed playwright / @playwright/test unpinned, so the runner jumped to 1.60.0 while the image only shipped the 1.58 browser binaries. Bumped the base image to v1.60.0-jammy, pinned the install to 1.60.0, and aligned @playwright/test in package.json to ^1.60.0 so the runner and the bundled browsers always match.

v4.12.5

Compare Source

🏠 Internal
  • Dependencies: npm ci failed with an ERESOLVE error because typescript@^6.0.2 clashed with the peer range of @typescript-eslint/*@&#8203;8.57 (>=4.8.4 <6.0.0). Bumped @typescript-eslint/eslint-plugin and @typescript-eslint/parser to ^8.60.1, whose peer range (>=4.8.4 <6.1.0) allows TypeScript 6, and regenerated the lockfile so a clean npm ci install succeeds. Fixes #​1368.

v4.12.4

Compare Source

🐛 Bug Fix
  • Speech Recognize: the recognition confirmation "beep" created a new AudioContext on every result and never closed it. During continuous dictation these piled up — leaking until the browser's per-page limit was hit and the overlapping audio turned into a "roar" that could crash the tab. The sound helper now reuses a single AudioContext per recognizer and releases it on destruct. Recognizer cleanup is also tied to the editor lifecycle (previously the toolbar button's beforeDestruct), so it survives toolbar rebuilds and is freed when the editor is destroyed.

v4.12.3

Compare Source

v4.12.2

Compare Source

🐛 Bug Fix
  • Security / Helpers (prototype pollution): Jodit.modules.Helpers.set(chain, value, obj) walked the dot-separated chain and created/followed each segment without filtering prototype-mutating keys, so a chain such as __proto__.polluted (e.g. set('__proto__.polluted', 'yes', {})) could reach and mutate Object.prototype (CWE-1321). set now bails out when any segment is __proto__, constructor, or prototype, reusing the same guard added for Jodit.configure() in 4.12.18. Responsibly reported by Junming Wu.

v4.11.15

Compare Source

🏠 Internal
  • Add data-ref attributes to image buttons in UIImageMainTab

v4.11.14

Compare Source

🚀 New Feature
  • Add stream() method to Ajax class for SSE (Server-Sent Events) streaming over XMLHttpRequest. Returns an AsyncGenerator<string> that yields parsed data: fields incrementally via onprogress.

    const ajax = new Jodit.modules.Ajax({
      method: 'POST',
      url: '/api/ai/stream',
      contentType: 'application/json',
      data: { prompt: 'Hello' }
    });
    
    try {
      for await (const data of ajax.stream()) {
        const event = JSON.parse(data);
        editor.s.insertHTML(event.text);
      }
    } finally {
      ajax.destruct();
    }
📝 Documentation
  • Rewrite src/core/request/README.md with full API reference, usage examples, SSE streaming guide, AbortController integration, and real-world patterns (FileBrowser, Uploader, plugins).

v4.11.13

Compare Source

v4.11.11

Compare Source

v4.11.10

Compare Source

v4.11.9

Compare Source

v4.11.8

Compare Source

v4.11.7

Compare Source

🚀 New Feature
  • Implement custom highlight styling for search results (#​1343)
  • Add backSpaceAfterDelete event triggered after backspace operations
  • Add Dom.findFirstMatchedNode() method for retrieving first matched node in DOM tree
💅 Polish
  • Improve cursor positioning in wrapNodes plugin after backspace
  • Improve readability of selection marker queries and add focus condition check
🏠 Internal
  • Enhance test coverage for undo/redo, backspace, search, and other plugins

v4.11.6

Compare Source

🏠 Internal
  • Release workflow now populates GitHub Release notes from CHANGELOG.md instead of relying on auto-generated notes

v4.11.5

Compare Source

🚀 New Feature
  • ProgressBar.showFileUploadAnimation(from?, to?) — animated file icon that flies from a given point and fades out. Coordinates are relative to the editor container. Both from and to are optional with sensible defaults. The animation is automatically cleaned up on destruct().
    const editor = Jodit.make('#editor')
    jodit.progressbar.showFileUploadAnimation();

v4.11.4

Compare Source

💥 Breaking Change
  • .jodit-icon transform-origin changed from 0 0 !important to var(--jd-icon-transform-origin) (default center), and the !important flag was removed. If your layout depends on the old top-left origin, restore it via CSS:
    :root {
        --jd-icon-transform-origin: 0 0 !important;
    }
🚀 New Feature
  • IUIIconState now supports scale property — when set, applies transform: scale(...) to the SVG icon element, overriding the CSS variable

  • IControlType.icon now accepts string | IUIIconState — allows setting icon name, fill, iconURL, and scale directly from toolbar button config

    Per-button scale example:

    Jodit.make('#editor', {
        buttons: Jodit.atom([
            'bold',
            {
                name: 'big-italic',
                icon: { name: 'italic', fill: '', iconURL: '', scale: 1.5 },
                tooltip: 'Italic (large icon)'
            },
            'underline'
        ])
    });
  • CSS custom properties --jd-icon-transform-origin and --jd-icon-transform-scale for global icon scaling. Override them to resize all editor icons at once. Per-button scale in IUIIconState takes priority over the CSS variable.

    Global scale override via CSS:

    :root {
        --jd-icon-transform-scale: 1.3;
        --jd-icon-transform-origin: center;
    }

v4.11.3

Compare Source

Full Changelog: xdan/jodit@4.11.2...4.11.3

v4.11.2

Compare Source

💥 Breaking Change
  • cleanHTML.denyTags default changed from 'script' to 'script,iframe,object,embed' — iframes, objects, and embeds are now blocked by default
  • cleanHTML.removeOnError is deprecated in favor of cleanHTML.removeEventAttributes — all on* event handler attributes (onerror, onclick, onload, onmouseover, etc.) are now removed by default, not just onerror
  • cleanHTML.safeLinksTarget is now true by default — links with target="_blank" automatically get rel="noopener noreferrer"
  • cleanHTML.sandboxIframesInContent is now true by default — all <iframe> elements in editor content get sandbox="" attribute
  • cleanHTML.convertUnsafeEmbeds is now ['object', 'embed'] by default — listed elements are converted to sandboxed <iframe>
🏠 Internal
  • Add Statoscope for webpack bundle size analysis and build comparison
  • refactor: replace setAttribute and removeAttribute with attr helper in multiple files
🚀 New Feature
  • New option cleanHTML.removeEventAttributes — removes all on* event handler attributes for comprehensive XSS protection (onerror, onclick, onload, onmouseover, onfocus, etc.)
  • New option cleanHTML.safeLinksTarget — automatically adds rel="noopener noreferrer" to target="_blank" links to prevent window.opener attacks
  • New option cleanHTML.allowedStyles — whitelist of allowed CSS properties in style attributes, prevents CSS injection attacks (e.g. data exfiltration via background-image: url(...))
  • New option cleanHTML.sanitizer — hook for external sanitizer integration (e.g. DOMPurify). Called before Jodit's built-in sanitization
  • New option cleanHTML.sandboxIframesInContent — adds sandbox="" to all <iframe> in editor content
  • New option cleanHTML.convertUnsafeEmbeds (false | string[]) — converts listed tags to sandboxed <iframe>, customizable list
📝 Documentation
  • New comprehensive security guide: docs/security.md covering XSS protection, CSP, Trusted Types, HTML Sanitizer API, server-side sanitization, and hardened configuration examples

v4.10.3

Compare Source

Full Changelog: xdan/jodit@4.10.2...4.10.3

v4.10.2

Compare Source

What's Changed

New Contributors

Full Changelog: xdan/jodit@4.10.1...4.10.2

v4.10.1

Compare Source

💥 Breaking Change
  • Fix custom cell popup buttons not working when referenced by string name (e.g. popup: { cells: Jodit.atom(['valign', 'deleteTable', ...]) }) #​1328
  • Rename cell popup button deletedeleteTable to avoid conflict with document.execCommand('delete')

v4.9.19

Compare Source

What's Changed

Full Changelog: xdan/jodit@4.9.18...4.9.19

v4.9.16

Compare Source

🏠 Internal
  • Downgrade Node.js version in .nvmrc to 22

v4.9.15

Compare Source

🐛 Bug Fix
  • Enhance image editor and file browser functionality with new path handling and update tests

v4.9.14

Compare Source

🐛 Bug Fix
  • Enhance image editor functionality with 'Save as' prompt and refactor button definitions

v4.9.13

Compare Source

Full Changelog: xdan/jodit@4.9.12...4.9.13

v4.9.12

Compare Source

🚀 New Feature
  • Enhance accessibility by adding role attributes to UI elements and updating related tests

v4.9.11

Compare Source

🚀 New Feature
  • The getRole():string method has been added to UIElement. This allows you to set container roles for accessibility.
  • The UIGroup role is set to list.

[v4.9.10](https://redirect.github.com/xd

Note

PR body was truncated to here.


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate renovate Bot added the dependencies Pull requests that update a dependency file label Jun 20, 2026
@renovate renovate Bot requested a review from jamesrwelch June 20, 2026 09:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants