Skip to content

Commit 48d62a7

Browse files
authored
Merge pull request #5269 from magento-borg/borg-2.3.5
[CIA] Bug fixes
2 parents d696d32 + 463cae2 commit 48d62a7

File tree

13 files changed

+426
-23
lines changed

13 files changed

+426
-23
lines changed

app/code/Magento/Braintree/Test/Mftf/ActionGroup/AdminRoleActionGroup.xml

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,18 +45,32 @@
4545
<!--Delete role-->
4646
<actionGroup name="AdminDeleteRoleActionGroup">
4747
<annotations>
48-
<description>Deletes a User Role that contains the text 'Role'. PLEASE NOTE: The Action Group values are Hardcoded.</description>
48+
<description>Deletes a User Role.</description>
4949
</annotations>
5050
<arguments>
5151
<argument name="role" defaultValue=""/>
5252
</arguments>
5353

54-
<click stepKey="clickOnRole" selector="{{AdminDeleteRoleSection.theRole}}"/>
54+
<click stepKey="clickResetFilterButtonBefore" selector="{{AdminRoleGridSection.resetButton}}"/>
55+
<waitForPageLoad stepKey="waitForRolesGridFilterResetBefore" time="10"/>
56+
<fillField stepKey="TypeRoleFilter" selector="{{AdminRoleGridSection.roleNameFilterTextField}}" userInput="{{role.name}}"/>
57+
<waitForElementVisible stepKey="waitForFilterSearchButtonBefore" selector="{{AdminRoleGridSection.searchButton}}" time="10"/>
58+
<click stepKey="clickFilterSearchButton" selector="{{AdminRoleGridSection.searchButton}}"/>
59+
<waitForPageLoad stepKey="waitForUserRoleFilter" time="10"/>
60+
<waitForElementVisible stepKey="waitForRoleInRoleGrid" selector="{{AdminDeleteRoleSection.role(role.name)}}" time="10"/>
61+
<click stepKey="clickOnRole" selector="{{AdminDeleteRoleSection.role(role.name)}}"/>
62+
<waitForPageLoad stepKey="waitForRolePageToLoad" time="10"/>
5563
<fillField stepKey="TypeCurrentPassword" selector="{{AdminDeleteRoleSection.current_pass}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/>
64+
<waitForElementVisible stepKey="waitForDeleteRoleButton" selector="{{AdminDeleteRoleSection.delete}}" time="10"/>
5665
<click stepKey="clickToDeleteRole" selector="{{AdminDeleteRoleSection.delete}}"/>
57-
<waitForAjaxLoad stepKey="waitForDeleteConfirmationPopup" time="5"/>
66+
<waitForPageLoad stepKey="waitForDeleteConfirmationPopup" time="5"/>
67+
<waitForElementVisible stepKey="waitForConfirmButton" selector="{{AdminDeleteRoleSection.confirm}}" time="10"/>
5868
<click stepKey="clickToConfirm" selector="{{AdminDeleteRoleSection.confirm}}"/>
5969
<waitForPageLoad stepKey="waitForPageLoad" time="10"/>
6070
<see stepKey="seeSuccessMessage" userInput="You deleted the role."/>
71+
<waitForPageLoad stepKey="waitForRolesGridLoad" />
72+
<waitForElementVisible stepKey="waitForResetFilterButtonAfter" selector="{{AdminRoleGridSection.resetButton}}" time="10"/>
73+
<click stepKey="clickResetFilterButtonAfter" selector="{{AdminRoleGridSection.resetButton}}"/>
74+
<waitForPageLoad stepKey="waitForRolesGridFilterResetAfter" time="10"/>
6175
</actionGroup>
6276
</actionGroups>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* Copyright © Magento, Inc. All rights reserved.
3+
* See COPYING.txt for license details.
4+
*/
5+
6+
var config = {
7+
map: {
8+
'*': {
9+
escaper: 'Magento_Security/js/escaper'
10+
}
11+
}
12+
};
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
/**
2+
* Copyright © Magento, Inc. All rights reserved.
3+
* See COPYING.txt for license details.
4+
*/
5+
6+
/**
7+
* A loose JavaScript version of Magento\Framework\Escaper
8+
*
9+
* Due to differences in how XML/HTML is processed in PHP vs JS there are a couple of minor differences in behavior
10+
* from the PHP counterpart.
11+
*
12+
* The first difference is that the default invocation of escapeHtml without allowedTags will double-escape existing
13+
* entities as the intention of such an invocation is that the input isn't supposed to contain any HTML.
14+
*
15+
* The second difference is that escapeHtml will not escape quotes. Since the input is actually being processed by the
16+
* DOM there is no chance of quotes being mixed with HTML syntax. And, since escapeHtml is not
17+
* intended to be used with raw injection into a HTML attribute, this is acceptable.
18+
*
19+
* @api
20+
*/
21+
define([], function () {
22+
'use strict';
23+
24+
return {
25+
neverAllowedElements: ['script', 'img', 'embed', 'iframe', 'video', 'source', 'object', 'audio'],
26+
generallyAllowedAttributes: ['id', 'class', 'href', 'title', 'style'],
27+
forbiddenAttributesByElement: {
28+
a: ['style']
29+
},
30+
31+
/**
32+
* Escape a string for safe injection into HTML
33+
*
34+
* @param {String} data
35+
* @param {Array|null} allowedTags
36+
* @returns {String}
37+
*/
38+
escapeHtml: function (data, allowedTags) {
39+
var domParser = new DOMParser(),
40+
fragment = domParser.parseFromString('<div></div>', 'text/html');
41+
42+
fragment = fragment.body.childNodes[0];
43+
allowedTags = typeof allowedTags === 'object' && allowedTags.length ? allowedTags : null;
44+
45+
if (allowedTags) {
46+
fragment.innerHTML = data || '';
47+
allowedTags = this._filterProhibitedTags(allowedTags);
48+
49+
this._removeComments(fragment);
50+
this._removeNotAllowedElements(fragment, allowedTags);
51+
this._removeNotAllowedAttributes(fragment);
52+
53+
return fragment.innerHTML;
54+
}
55+
56+
fragment.textContent = data || '';
57+
58+
return fragment.innerHTML;
59+
},
60+
61+
/**
62+
* Remove the always forbidden tags from a list of provided tags
63+
*
64+
* @param {Array} tags
65+
* @returns {Array}
66+
* @private
67+
*/
68+
_filterProhibitedTags: function (tags) {
69+
return tags.filter(function (n) {
70+
return this.neverAllowedElements.indexOf(n) === -1;
71+
}.bind(this));
72+
},
73+
74+
/**
75+
* Remove comment nodes from the given node
76+
*
77+
* @param {Node} node
78+
* @private
79+
*/
80+
_removeComments: function (node) {
81+
var treeWalker = node.ownerDocument.createTreeWalker(
82+
node,
83+
NodeFilter.SHOW_COMMENT,
84+
function () {
85+
return NodeFilter.FILTER_ACCEPT;
86+
},
87+
false
88+
),
89+
nodesToRemove = [];
90+
91+
while (treeWalker.nextNode()) {
92+
nodesToRemove.push(treeWalker.currentNode);
93+
}
94+
95+
nodesToRemove.forEach(function (nodeToRemove) {
96+
nodeToRemove.parentNode.removeChild(nodeToRemove);
97+
});
98+
},
99+
100+
/**
101+
* Strip the given node of all disallowed tags while permitting any nested text nodes
102+
*
103+
* @param {Node} node
104+
* @param {Array|null} allowedTags
105+
* @private
106+
*/
107+
_removeNotAllowedElements: function (node, allowedTags) {
108+
var treeWalker = node.ownerDocument.createTreeWalker(
109+
node,
110+
NodeFilter.SHOW_ELEMENT,
111+
function (currentNode) {
112+
return allowedTags.indexOf(currentNode.nodeName.toLowerCase()) === -1 ?
113+
NodeFilter.FILTER_ACCEPT
114+
// SKIP instead of REJECT because REJECT also rejects child nodes
115+
: NodeFilter.FILTER_SKIP;
116+
},
117+
false
118+
),
119+
nodesToRemove = [];
120+
121+
while (treeWalker.nextNode()) {
122+
if (allowedTags.indexOf(treeWalker.currentNode.nodeName.toLowerCase()) === -1) {
123+
nodesToRemove.push(treeWalker.currentNode);
124+
}
125+
}
126+
127+
nodesToRemove.forEach(function (nodeToRemove) {
128+
nodeToRemove.parentNode.replaceChild(
129+
node.ownerDocument.createTextNode(nodeToRemove.textContent),
130+
nodeToRemove
131+
);
132+
});
133+
},
134+
135+
/**
136+
* Remove any invalid attributes from the given node
137+
*
138+
* @param {Node} node
139+
* @private
140+
*/
141+
_removeNotAllowedAttributes: function (node) {
142+
var treeWalker = node.ownerDocument.createTreeWalker(
143+
node,
144+
NodeFilter.SHOW_ELEMENT,
145+
function () {
146+
return NodeFilter.FILTER_ACCEPT;
147+
},
148+
false
149+
),
150+
i,
151+
attribute,
152+
nodeName,
153+
attributesToRemove = [];
154+
155+
while (treeWalker.nextNode()) {
156+
for (i = 0; i < treeWalker.currentNode.attributes.length; i++) {
157+
attribute = treeWalker.currentNode.attributes[i];
158+
nodeName = treeWalker.currentNode.nodeName.toLowerCase();
159+
160+
if (this.generallyAllowedAttributes.indexOf(attribute.name) === -1 || // eslint-disable-line max-depth,max-len
161+
this.forbiddenAttributesByElement[nodeName] &&
162+
this.forbiddenAttributesByElement[nodeName].indexOf(attribute.name) !== -1
163+
) {
164+
attributesToRemove.push(attribute);
165+
}
166+
}
167+
}
168+
169+
attributesToRemove.forEach(function (attributeToRemove) {
170+
attributeToRemove.ownerElement.removeAttribute(attributeToRemove.name);
171+
});
172+
}
173+
};
174+
});

app/code/Magento/Theme/view/frontend/templates/messages.phtml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,18 @@
1111
class: 'message-' + message.type + ' ' + message.type + ' message',
1212
'data-ui-id': 'message-' + message.type
1313
}">
14-
<div data-bind="html: message.text"></div>
14+
<div data-bind="html: $parent.prepareMessageForHtml(message.text)"></div>
1515
</div>
1616
</div>
1717
<!-- /ko -->
18+
1819
<!-- ko if: messages().messages && messages().messages.length > 0 -->
1920
<div role="alert" data-bind="foreach: { data: messages().messages, as: 'message' }" class="messages">
2021
<div data-bind="attr: {
2122
class: 'message-' + message.type + ' ' + message.type + ' message',
2223
'data-ui-id': 'message-' + message.type
2324
}">
24-
<div data-bind="html: message.text"></div>
25+
<div data-bind="html: $parent.prepareMessageForHtml(message.text)"></div>
2526
</div>
2627
</div>
2728
<!-- /ko -->

app/code/Magento/Theme/view/frontend/web/js/view/messages.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,16 @@ define([
1111
'uiComponent',
1212
'Magento_Customer/js/customer-data',
1313
'underscore',
14+
'escaper',
1415
'jquery/jquery-storageapi'
15-
], function ($, Component, customerData, _) {
16+
], function ($, Component, customerData, _, escaper) {
1617
'use strict';
1718

1819
return Component.extend({
1920
defaults: {
2021
cookieMessages: [],
21-
messages: []
22+
messages: [],
23+
allowedTags: ['div', 'span', 'b', 'strong', 'i', 'em', 'u', 'a']
2224
},
2325

2426
/**
@@ -38,6 +40,16 @@ define([
3840
}
3941

4042
$.cookieStorage.set('mage-messages', '');
43+
},
44+
45+
/**
46+
* Prepare the given message to be rendered as HTML
47+
*
48+
* @param {String} message
49+
* @return {String}
50+
*/
51+
prepareMessageForHtml: function (message) {
52+
return escaper.escapeHtml(message, this.allowedTags);
4153
}
4254
});
4355
});

app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserActionGroup.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,10 @@
2929
<fillField selector="{{AdminEditUserSection.currentPasswordField}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="enterCurrentPassword"/>
3030
<scrollToTopOfPage stepKey="scrollToTopOfPage"/>
3131
<click selector="{{AdminEditUserSection.userRoleTab}}" stepKey="clickUserRole"/>
32+
<waitForPageLoad stepKey="waitForAdminUserRoleTabLoad"/>
3233
<fillField selector="{{AdminEditUserSection.roleNameFilterTextField}}" userInput="{{role.name}}" stepKey="filterRole"/>
3334
<click selector="{{AdminEditUserSection.searchButton}}" stepKey="clickSearch"/>
34-
<waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear1"/>
35+
<waitForPageLoad stepKey="waitForLoadingMaskToDisappear1"/>
3536
<click selector="{{AdminEditUserSection.searchResultFirstRow}}" stepKey="selectRole"/>
3637
<click selector="{{AdminEditUserSection.saveButton}}" stepKey="clickSaveUser"/>
3738
<waitForPageLoad stepKey="waitForPageLoad2"/>

app/code/Magento/User/Test/Mftf/Section/AdminDeleteRoleSection.xml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@
1010
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd">
1111
<section name="AdminDeleteRoleSection">
1212
<element name="theRole" selector="//td[contains(text(), 'Role')]" type="button"/>
13-
<element name="salesRole" selector="//td[contains(text(), 'Sales')]" type="button"/>
13+
<element name="role" parameterized="true" selector="//td[contains(@class,'col-role_name') and contains(text(), '{{roleName}}')]" type="button"/>
1414
<element name="current_pass" type="button" selector="#current_password"/>
1515
<element name="delete" selector="//button/span[contains(text(), 'Delete Role')]" type="button"/>
1616
<element name="confirm" selector="//*[@class='action-primary action-accept']" type="button"/>
1717
</section>
1818
</sections>
19-

app/code/Magento/User/Test/Mftf/Section/AdminRoleGridSection.xml

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,4 @@
1414
<element name="roleNameInFirstRow" type="text" selector=".col-role_name"/>
1515
<element name="searchResultFirstRow" type="text" selector=".data-grid>tbody>tr"/>
1616
</section>
17-
18-
<section name="AdminDeleteRoleSection">
19-
<element name="theRole" selector="//td[contains(text(), 'Role')]" type="button"/>
20-
<element name="role" parameterized="true" selector="//td[contains(text(), '{{args}}')]" type="button"/>
21-
<element name="current_pass" type="button" selector="#current_password"/>
22-
<element name="delete" selector="//button/span[contains(text(), 'Delete Role')]" type="button"/>
23-
<element name="confirm" selector="//*[@class='action-primary action-accept']" type="button"/>
24-
</section>
2517
</sections>

0 commit comments

Comments
 (0)