Skip to content

Commit ee7bba0

Browse files
Merge pull request #456 from magento/magento-mpi-MC-32766
[Support] MC-32766: Page builder issue with secure variable
2 parents 9e0e6ae + 9be708b commit ee7bba0

File tree

3 files changed

+127
-3
lines changed
  • app/code/Magento/PageBuilder/view/adminhtml/web
  • dev/tests/js/jasmine/tests/app/code/Magento/PageBuilder/view/adminhtml/web/js/content-type/text

3 files changed

+127
-3
lines changed

app/code/Magento/PageBuilder/view/adminhtml/web/js/content-type/text/preview.js

Lines changed: 15 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/code/Magento/PageBuilder/view/adminhtml/web/ts/js/content-type/text/preview.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,14 @@ import events from "Magento_PageBuilder/js/events";
88
import _ from "underscore";
99
import HideShowOption from "../../content-type-menu/hide-show-option";
1010
import {OptionsInterface} from "../../content-type-menu/option.types";
11+
import {DataObject} from "../../data-store";
1112
import delayUntil from "../../utils/delay-until";
1213
import {
13-
createBookmark, createDoubleClickEvent,
14-
findNodeIndex, getActiveEditor, getNodeByIndex,
14+
createBookmark,
15+
createDoubleClickEvent,
16+
findNodeIndex,
17+
getActiveEditor,
18+
getNodeByIndex,
1519
isWysiwygSupported,
1620
lockImageSize,
1721
moveToBookmark,
@@ -300,6 +304,18 @@ export default class Preview extends BasePreview {
300304
protected bindEvents() {
301305
super.bindEvents();
302306

307+
this.contentType.dataStore.subscribe((state: DataObject) => {
308+
// Find html elements which attributes contain magento variables directives
309+
const sanitizedContent = state.content.replace(/<([a-z0-9\-\_]+)([^>]+?[a-z0-9\-\_]+="[^"]*?\{\{.+?\}\}.*?".*?)>/gi, (match1: string, tag: string, attributes: string) => {
310+
// Replace double quote with single quote within magento variable directive
311+
const sanitizedAttributes = attributes.replace(/\{\{[^\{\}]+\}\}/gi, (match2: string) => match2.replace(/"/g, "'"));
312+
return "<" + tag + sanitizedAttributes + ">";
313+
});
314+
if (sanitizedContent !== state.content) {
315+
state.content = sanitizedContent;
316+
}
317+
});
318+
303319
// After drop of new content type open TinyMCE and focus
304320
events.on("text:dropAfter", (args: ContentTypeMountEventParamsInterface) => {
305321
if (args.id === this.contentType.id) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Copyright © Magento, Inc. All rights reserved.
3+
* See COPYING.txt for license details.
4+
*/
5+
6+
/* eslint-disable max-nested-callbacks */
7+
/* eslint-disable max-depth */
8+
/* jscs:disable jsDoc*/
9+
define([
10+
'Magento_PageBuilder/js/content-type/text/preview',
11+
'Magento_PageBuilder/js/content-type'
12+
], function (Preview, ContentType) {
13+
'use strict';
14+
15+
describe('Magento_PageBuilder/js/content-type/text/preview', function () {
16+
var preview;
17+
18+
function ObservableUpdater() {}
19+
ObservableUpdater.prototype.update = function () {};
20+
21+
beforeEach(function () {
22+
var contentType,
23+
config,
24+
observableUpdater;
25+
26+
observableUpdater = new ObservableUpdater();
27+
config = {
28+
name: 'text',
29+
label: 'Text'
30+
};
31+
contentType = new ContentType(null, config);
32+
preview = new Preview(contentType, config, observableUpdater);
33+
});
34+
35+
describe('Text component content change', function () {
36+
it('Should replace double quote with single quote if the directive is not in html attr', function () {
37+
var actual = '<p>' +
38+
'<a title="Contact Us" href="{{config path="web/secure/base_url"}}contact">Contact Us</a>' +
39+
'</p>',
40+
expected = '<p>' +
41+
'<a title="Contact Us" href="{{config path=\'web/secure/base_url\'}}contact">Contact Us</a>' +
42+
'</p>';
43+
44+
preview.contentType.dataStore.setState({
45+
content: actual
46+
});
47+
expect(preview.contentType.dataStore.get('content')).toBe(expected);
48+
});
49+
it('Should not replace double quote with single quote if the directive is not in html attr', function () {
50+
var actual = '<p>Our website:</p><p>{{config path="web/secure/base_url"}}</p>',
51+
expected = actual;
52+
53+
preview.contentType.dataStore.setState({
54+
content: actual
55+
});
56+
expect(preview.contentType.dataStore.get('content')).toBe(expected);
57+
});
58+
});
59+
});
60+
61+
if (typeof Object.assign !== 'function') {
62+
// Must be writable: true, enumerable: false, configurable: true
63+
Object.defineProperty(Object, 'assign', {
64+
value: function assign(target) { // .length of function is 2
65+
var to,
66+
index,
67+
nextKey,
68+
nextSource;
69+
70+
if (target === null || target === undefined) {
71+
throw new TypeError('Cannot convert undefined or null to object');
72+
}
73+
to = Object(target);
74+
75+
for (index = 1; index < arguments.length; index++) {
76+
nextSource = arguments[index];
77+
78+
if (nextSource !== null && nextSource !== undefined) {
79+
for (nextKey in nextSource) {
80+
// Avoid bugs when hasOwnProperty is shadowed
81+
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
82+
to[nextKey] = nextSource[nextKey];
83+
}
84+
}
85+
}
86+
}
87+
88+
return to;
89+
},
90+
writable: true,
91+
configurable: true
92+
});
93+
}
94+
});

0 commit comments

Comments
 (0)