Skip to content

Commit 5632dbd

Browse files
fix(components/text-editor): escape merge field attribute values (#797)
1 parent aabaaa3 commit 5632dbd

File tree

6 files changed

+69
-16
lines changed

6 files changed

+69
-16
lines changed

libs/components/text-editor/ng-package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@
44
"lib": {
55
"entryFile": "src/index.ts"
66
},
7-
"allowedNonPeerDependencies": ["dompurify"]
7+
"allowedNonPeerDependencies": ["dompurify", "he"]
88
}

libs/components/text-editor/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
},
3535
"dependencies": {
3636
"dompurify": "2.3.6",
37+
"he": "1.2.0",
3738
"tslib": "^2.3.1"
3839
}
3940
}

libs/components/text-editor/src/lib/modules/text-editor/menubar/text-editor-menubar.component.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
import { SkyLibResourcesService } from '@skyux/i18n';
1010
import { SkyDropdownMessage, SkyDropdownMessageType } from '@skyux/popovers';
1111

12+
import he from 'he';
1213
import { Subject } from 'rxjs';
1314
import { take, takeUntil } from 'rxjs/operators';
1415

@@ -210,9 +211,9 @@ export class SkyTextEditorMenubarComponent implements OnDestroy, OnInit {
210211
this.execCommand(
211212
'insertHTML',
212213
'<img style="display: inline; cursor: grab;" data-fieldid="' +
213-
field.id +
214+
he.escape(field.id) +
214215
'" data-fielddisplay="' +
215-
field.name +
216+
he.escape(field.name) +
216217
'" src="' +
217218
(field.previewImageUrl ||
218219
this.#adapterService.getMergeFieldDataURI(field.name)) +

libs/components/text-editor/src/lib/modules/text-editor/text-editor.component.spec.ts

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { MENU_DEFAULTS } from './defaults/menu-defaults';
2424
import { STYLE_STATE_DEFAULTS } from './defaults/style-state-defaults';
2525
import { TOOLBAR_ACTION_DEFAULTS } from './defaults/toolbar-action-defaults';
2626
import { TextEditorFixtureComponent } from './fixtures/text-editor.component.fixture';
27+
import { SkyTextEditorAdapterService } from './services/text-editor-adapter.service';
2728
import { SkyTextEditorComponent } from './text-editor.component';
2829
import { SkyTextEditorModule } from './text-editor.module';
2930
import { SkyTextEditorStyleState } from './types/style-state';
@@ -274,6 +275,18 @@ describe('Text editor', () => {
274275
fixture.detectChanges();
275276
}
276277

278+
function insertMergeField(index: number): void {
279+
openDropdown('.sky-text-editor-menu-merge-field');
280+
expect(document.querySelector('.sky-dropdown-item')).toBeTruthy();
281+
282+
iframeDocument.body.focus();
283+
const mergeFieldOption = document.querySelectorAll(
284+
'.sky-dropdown-item button'
285+
)[index];
286+
SkyAppTestUtility.fireDomEvent(mergeFieldOption, 'click');
287+
fixture.detectChanges();
288+
}
289+
277290
function getFontPicker(): HTMLElement {
278291
return fixture.nativeElement.querySelector(
279292
'.sky-text-editor-toolbar .sky-text-editor-font-picker'
@@ -653,26 +666,48 @@ describe('Text editor', () => {
653666
tick();
654667
fixture.detectChanges();
655668

656-
openDropdown('.sky-text-editor-menu-merge-field');
657-
expect(document.querySelector('.sky-dropdown-item')).toBeTruthy();
658-
659-
iframeDocument.body.focus();
660-
const mergeFieldOption = document.querySelectorAll(
661-
'.sky-dropdown-item button'
662-
)[2];
663-
SkyAppTestUtility.fireDomEvent(mergeFieldOption, 'click');
664-
fixture.detectChanges();
665-
tick();
666-
fixture.detectChanges();
667-
tick();
668-
fixture.detectChanges();
669+
insertMergeField(2);
669670

670671
expect(testComponent.value).toContain('data-fieldid="2"');
671672
expect(testComponent.value).toContain(
672673
'data-fielddisplay="A field that is really too long for its own good"'
673674
);
674675
}));
675676

677+
it('should escape merge field attribute values', fakeAsync(() => {
678+
testComponent.mergeFields = [
679+
{
680+
id: '"><',
681+
name: '"><',
682+
},
683+
];
684+
685+
fixture.detectChanges();
686+
687+
const adapterSvc = fixture.debugElement
688+
.query(By.css('sky-text-editor'))
689+
.injector.get(SkyTextEditorAdapterService);
690+
691+
spyOn(adapterSvc, 'execCommand').and.callThrough();
692+
693+
insertMergeField(0);
694+
695+
expect(adapterSvc.execCommand).toHaveBeenCalledOnceWith({
696+
command: 'insertHTML',
697+
value: jasmine.stringContaining('data-fieldid="&quot;&gt;&lt;'),
698+
});
699+
700+
expect(adapterSvc.execCommand).toHaveBeenCalledOnceWith({
701+
command: 'insertHTML',
702+
value: jasmine.stringContaining('data-fielddisplay="&quot;&gt;&lt;'),
703+
});
704+
705+
// The browser converts the escaped angle brackets back to their unescaped
706+
// versions since they appear within quotes in an attribute value.
707+
expect(testComponent.value).toContain('data-fieldid="&quot;><');
708+
expect(testComponent.value).toContain('data-fielddisplay="&quot;><"');
709+
}));
710+
676711
it('Toolbar values should update based on selection', fakeAsync(() => {
677712
testComponent.value =
678713
'<font style="font-size: 16px" face="Arial" color="#c14040">' +

package-lock.json

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
"dragula": "3.7.3",
106106
"fontfaceobserver": "2.1.0",
107107
"google-libphonenumber": "3.2.31",
108+
"he": "1.2.0",
108109
"intl-tel-input": "17.0.19",
109110
"jsonc-parser": "3.0.0",
110111
"jwt-decode": "3.1.2",
@@ -157,6 +158,7 @@
157158
"@types/dompurify": "2.3.4",
158159
"@types/fontfaceobserver": "2.1.0",
159160
"@types/fs-extra": "9.0.13",
161+
"@types/he": "1.1.2",
160162
"@types/jasmine": "4.0.3",
161163
"@types/jest": "28.1.8",
162164
"@types/node": "18.7.1",

0 commit comments

Comments
 (0)