VYPR
Moderate severityNVD Advisory· Published Dec 8, 2022· Updated Apr 23, 2025

Cross-site scripting vulnerability in TinyMCE alerts

CVE-2022-23494

Description

tinymce is an open source rich text editor. A cross-site scripting (XSS) vulnerability was discovered in the alert and confirm dialogs when these dialogs were provided with malicious HTML content. This can occur in plugins that use the alert or confirm dialogs, such as in the image plugin, which presents these dialogs when certain errors occur. The vulnerability allowed arbitrary JavaScript execution when an alert presented in the TinyMCE UI for the current user. This vulnerability has been patched in TinyMCE 5.10.7 and TinyMCE 6.3.1 by ensuring HTML sanitization was still performed after unwrapping invalid elements. Users are advised to upgrade to either 5.10.7 or 6.3.1. Users unable to upgrade may ensure the the images_upload_handler returns a valid value as per the images_upload_handler documentation.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
tinymcenpm
>= 6.0.0, < 6.3.16.3.1
tinymce/tinymcePackagist
>= 6.0.0, < 6.3.16.3.1
TinyMCENuGet
>= 6.0.0, < 6.3.16.3.1
tinymcenpm
< 5.10.75.10.7
tinymce/tinymcePackagist
< 5.10.75.10.7
TinyMCENuGet
< 5.10.75.10.7

Affected products

1

Patches

2
8bb2d2646d4e

Merge pull request from GHSA-gg8r-xjwq-4w92

https://github.com/tinymce/tinymcespockeDec 6, 2022via ghsa
7 files changed · +99 1
  • modules/tinymce/CHANGELOG.md+3 0 modified
    @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
     
     ## Unreleased
     
    +### Fixed
    +- HTML in messages for the `WindowManager.alert` and `WindowManager.confirm` APIs were not properly sanitized. #TINY-3548
    +
     ## 5.10.6 - 2022-10-19
     
     ### Fixed
    
  • modules/tinymce/package.json+1 0 modified
    @@ -23,6 +23,7 @@
         "silver-test-manual": "grunt bedrock-manual:silver"
       },
       "dependencies": {
    +    "dompurify": "2.3.8",
         "tslib": "^2.0.0"
       }
     }
    
  • modules/tinymce/src/themes/silver/main/ts/ui/core/HtmlSanitizer.ts+10 0 added
    @@ -0,0 +1,10 @@
    +/**
    + * Copyright (c) Tiny Technologies, Inc. All rights reserved.
    + * Licensed under the LGPL or a commercial license.
    + * For LGPL see License.txt in the project root for license information.
    + * For commercial licenses see https://www.tiny.cloud/
    + */
    +
    +import createDompurify from 'dompurify';
    +
    +export const sanitizeHtmlString = (html: string): string => createDompurify().sanitize(html);
    
  • modules/tinymce/src/themes/silver/main/ts/ui/dialog/Dialogs.ts+2 1 modified
    @@ -15,6 +15,7 @@ import { Class, SugarBody } from '@ephox/sugar';
     import Env from 'tinymce/core/api/Env';
     
     import { UiFactoryBackstageProviders } from '../../backstage/Backstage';
    +import * as HtmlSanitizer from '../core/HtmlSanitizer';
     import * as NavigableObject from '../general/NavigableObject';
     
     const isTouch = Env.deviceType.isTouch();
    @@ -84,7 +85,7 @@ const pBodyMessage = (message: string, providersBackstage: UiFactoryBackstagePro
           },
           components: [
             {
    -          dom: DomFactory.fromHtml(`<p>${providersBackstage.translate(message)}</p>`)
    +          dom: DomFactory.fromHtml(`<p>${HtmlSanitizer.sanitizeHtmlString(providersBackstage.translate(message))}</p>`)
             }
           ]
         }
    
  • modules/tinymce/src/themes/silver/test/ts/phantom/window/WindowManagerAlertTest.ts+39 0 modified
    @@ -184,4 +184,43 @@ describe('phantom.tinymce.themes.silver.window.WindowManagerAlertTest', () => {
         Mouse.clickOn(SugarBody.body(), '.tox-button:contains("OK")');
         UiFinder.notExists(SugarBody.body(), '[role="dialog"]');
       });
    +
    +  it('TINY-3548: sanitize message', async () => {
    +    createAlert('<a href="javascript:alert(1)">invalid link</a><script>alert(1)</script><a href="http://tiny.cloud">valid link</a>', Fun.noop);
    +    const dialogBody = SelectorFind.descendant(SugarDocument.getDocument(), '.tox-dialog__body').getOrDie('Cannot find dialog body element');
    +    Assertions.assertStructure('A basic alert dialog should have these components',
    +      ApproxStructure.build((s, str, arr) => s.element('div', {
    +        classes: [ arr.has('tox-dialog__body') ],
    +        children: [
    +          s.element('div', {
    +            classes: [ arr.has('tox-dialog__body-content') ],
    +            children: [
    +              s.element('p', {
    +                children: [
    +                  s.element('a', {
    +                    attrs: {
    +                      href: str.none('Should have been trimmed away')
    +                    },
    +                    children: [
    +                      s.text(str.is('invalid link'))
    +                    ]
    +                  }),
    +                  s.element('a', {
    +                    attrs: {
    +                      href: str.is('http://tiny.cloud')
    +                    },
    +                    children: [
    +                      s.text(str.is('valid link'))
    +                    ]
    +                  })
    +                ]
    +              })
    +            ]
    +          })
    +        ]
    +      })),
    +      dialogBody
    +    );
    +    await pTeardown();
    +  });
     });
    
  • modules/tinymce/src/themes/silver/test/ts/phantom/window/WindowManagerConfirmTest.ts+39 0 modified
    @@ -195,4 +195,43 @@ describe('phantom.tinymce.themes.silver.window.WindowManagerConfirmTest', () =>
         Mouse.clickOn(SugarBody.body(), '.tox-button:contains("Yes")');
         UiFinder.notExists(SugarBody.body(), '[role="dialog"]');
       });
    +
    +  it('TINY-3548: sanitize message', async () => {
    +    createConfirm('<a href="javascript:alert(1)">invalid link</a><script>alert(1)</script><a href="http://tiny.cloud">valid link</a>', Fun.noop);
    +    const dialogBody = SelectorFind.descendant(SugarDocument.getDocument(), '.tox-dialog__body').getOrDie('Cannot find dialog body element');
    +    Assertions.assertStructure('A basic alert dialog should have these components',
    +      ApproxStructure.build((s, str, arr) => s.element('div', {
    +        classes: [ arr.has('tox-dialog__body') ],
    +        children: [
    +          s.element('div', {
    +            classes: [ arr.has('tox-dialog__body-content') ],
    +            children: [
    +              s.element('p', {
    +                children: [
    +                  s.element('a', {
    +                    attrs: {
    +                      href: str.none('Should have been trimmed away')
    +                    },
    +                    children: [
    +                      s.text(str.is('invalid link'))
    +                    ]
    +                  }),
    +                  s.element('a', {
    +                    attrs: {
    +                      href: str.is('http://tiny.cloud')
    +                    },
    +                    children: [
    +                      s.text(str.is('valid link'))
    +                    ]
    +                  })
    +                ]
    +              })
    +            ]
    +          })
    +        ]
    +      })),
    +      dialogBody
    +    );
    +    await pTeardown();
    +  });
     });
    
  • yarn.lock+5 0 modified
    @@ -4243,6 +4243,11 @@ domhandler@^2.3.0:
       dependencies:
         domelementtype "1"
     
    +dompurify@2.3.8:
    +  version "2.3.8"
    +  resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.8.tgz#224fe9ae57d7ebd9a1ae1ac18c1c1ca3f532226f"
    +  integrity sha512-eVhaWoVibIzqdGYjwsBWodIQIaXFSB+cKDf4cfxLMsK0xiud6SE+/WCVx/Xw/UwQsa4cS3T2eITcdtmTg2UKcw==
    +
     domutils@^1.5.1, domutils@^1.7.0:
       version "1.7.0"
       resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"
    
6923d85eba6d

Merge pull request from GHSA-gg8r-xjwq-4w92

https://github.com/tinymce/tinymcespockeDec 6, 2022via ghsa
5 files changed · +82 1
  • modules/tinymce/CHANGELOG.md+3 0 modified
    @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
     
     ## Unreleased
     
    +### Fixed
    +- HTML in messages for the `WindowManager.alert` and `WindowManager.confirm` APIs were not properly sanitized. #TINY-3548
    +
     ## 6.3.0 - 2022-11-23
     
     ### Added
    
  • modules/tinymce/src/themes/silver/main/ts/ui/core/HtmlSanitizer.ts+3 0 added
    @@ -0,0 +1,3 @@
    +import createDompurify from 'dompurify';
    +
    +export const sanitizeHtmlString = (html: string): string => createDompurify().sanitize(html);
    
  • modules/tinymce/src/themes/silver/main/ts/ui/dialog/Dialogs.ts+2 1 modified
    @@ -8,6 +8,7 @@ import { Class, SugarBody } from '@ephox/sugar';
     import Env from 'tinymce/core/api/Env';
     
     import { UiFactoryBackstageProviders } from '../../backstage/Backstage';
    +import * as HtmlSanitizer from '../core/HtmlSanitizer';
     import * as NavigableObject from '../general/NavigableObject';
     
     const isTouch = Env.deviceType.isTouch();
    @@ -77,7 +78,7 @@ const pBodyMessage = (message: string, providersBackstage: UiFactoryBackstagePro
           },
           components: [
             {
    -          dom: DomFactory.fromHtml(`<p>${providersBackstage.translate(message)}</p>`)
    +          dom: DomFactory.fromHtml(`<p>${HtmlSanitizer.sanitizeHtmlString(providersBackstage.translate(message))}</p>`)
             }
           ]
         }
    
  • modules/tinymce/src/themes/silver/test/ts/headless/window/WindowManagerAlertTest.ts+37 0 modified
    @@ -184,4 +184,41 @@ describe('headless.tinymce.themes.silver.window.WindowManagerAlertTest', () => {
         Mouse.clickOn(SugarBody.body(), '.tox-button:contains("OK")');
         UiFinder.notExists(SugarBody.body(), '[role="dialog"]');
       });
    +
    +  it('TINY-3548: sanitize message', async () => {
    +    createAlert('<a href="javascript:alert(1)">invalid link</a><script>alert(1)</script><a href="http://tiny.cloud">valid link</a>', Fun.noop);
    +    const dialogBody = SelectorFind.descendant(SugarDocument.getDocument(), '.tox-dialog__body').getOrDie('Cannot find dialog body element');
    +    Assertions.assertStructure('A basic alert dialog should have these components',
    +      ApproxStructure.build((s, str, arr) => s.element('div', {
    +        classes: [ arr.has('tox-dialog__body') ],
    +        children: [
    +          s.element('div', {
    +            classes: [ arr.has('tox-dialog__body-content') ],
    +            children: [
    +              s.element('p', {
    +                children: [
    +                  s.element('a', {
    +                    exactAttrs: { },
    +                    children: [
    +                      s.text(str.is('invalid link'))
    +                    ]
    +                  }),
    +                  s.element('a', {
    +                    exactAttrs: {
    +                      href: str.is('http://tiny.cloud')
    +                    },
    +                    children: [
    +                      s.text(str.is('valid link'))
    +                    ]
    +                  })
    +                ]
    +              })
    +            ]
    +          })
    +        ]
    +      })),
    +      dialogBody
    +    );
    +    await pTeardown();
    +  });
     });
    
  • modules/tinymce/src/themes/silver/test/ts/headless/window/WindowManagerConfirmTest.ts+37 0 modified
    @@ -195,4 +195,41 @@ describe('headless.tinymce.themes.silver.window.WindowManagerConfirmTest', () =>
         Mouse.clickOn(SugarBody.body(), '.tox-button:contains("Yes")');
         UiFinder.notExists(SugarBody.body(), '[role="dialog"]');
       });
    +
    +  it('TINY-3548: sanitize message', async () => {
    +    createConfirm('<a href="javascript:alert(1)">invalid link</a><script>alert(1)</script><a href="http://tiny.cloud">valid link</a>', Fun.noop);
    +    const dialogBody = SelectorFind.descendant(SugarDocument.getDocument(), '.tox-dialog__body').getOrDie('Cannot find dialog body element');
    +    Assertions.assertStructure('A basic alert dialog should have these components',
    +      ApproxStructure.build((s, str, arr) => s.element('div', {
    +        classes: [ arr.has('tox-dialog__body') ],
    +        children: [
    +          s.element('div', {
    +            classes: [ arr.has('tox-dialog__body-content') ],
    +            children: [
    +              s.element('p', {
    +                children: [
    +                  s.element('a', {
    +                    exactAttrs: { },
    +                    children: [
    +                      s.text(str.is('invalid link'))
    +                    ]
    +                  }),
    +                  s.element('a', {
    +                    exactAttrs: {
    +                      href: str.is('http://tiny.cloud')
    +                    },
    +                    children: [
    +                      s.text(str.is('valid link'))
    +                    ]
    +                  })
    +                ]
    +              })
    +            ]
    +          })
    +        ]
    +      })),
    +      dialogBody
    +    );
    +    await pTeardown();
    +  });
     });
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

8

News mentions

0

No linked articles in our index yet.