Skip to content

Commit ffd3f76

Browse files
committed
xss fixes
1 parent 24628a8 commit ffd3f76

File tree

5 files changed

+83
-33
lines changed

5 files changed

+83
-33
lines changed
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11

2-
<span class="fas fa-paperclip small"></span> <a href="{{url}}">{{{value}}}</a>
2+
<span class="fas fa-paperclip small"></span> <a href="{{url}}">{{value}}</a>

Diff for: client/src/view-helper.js

+71
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,77 @@ define('view-helper', ['lib!client/lib/purify.min.js'], function () {
321321
sanitizeHtml: function (text, options) {
322322
return DOMPurify.sanitize(text, options);
323323
},
324+
325+
moderateSanitizeHtml: function (value) {
326+
value = value || '';
327+
value = value.replace(/<[\/]{0,1}(base)[^><]*>/gi, '');
328+
value = value.replace(/<[\/]{0,1}(object)[^><]*>/gi, '');
329+
value = value.replace(/<[\/]{0,1}(embed)[^><]*>/gi, '');
330+
value = value.replace(/<[\/]{0,1}(applet)[^><]*>/gi, '');
331+
value = value.replace(/<[\/]{0,1}(iframe)[^><]*>/gi, '');
332+
value = value.replace(/<[\/]{0,1}(script)[^><]*>/gi, '');
333+
value = value.replace(/<[^><]*([^a-z]{1}on[a-z]+)=[^><]*>/gi, function (match) {
334+
return match.replace(/[^a-z]{1}on[a-z]+=/gi, ' data-handler-stripped=');
335+
});
336+
337+
value = this.stripEventHandlersInHtml(value);
338+
339+
value = value.replace(/href=" *javascript\:(.*?)"/gi, function(m, $1) {
340+
return 'removed=""';
341+
});
342+
value = value.replace(/href=' *javascript\:(.*?)'/gi, function(m, $1) {
343+
return 'removed=""';
344+
});
345+
value = value.replace(/src=" *javascript\:(.*?)"/gi, function(m, $1) {
346+
return 'removed=""';
347+
});
348+
value = value.replace(/src=' *javascript\:(.*?)'/gi, function(m, $1) {
349+
return 'removed=""';
350+
});
351+
352+
return value;
353+
},
354+
355+
stripEventHandlersInHtml: function (html) {
356+
function stripHTML(){
357+
html = html.slice(0, strip) + html.slice(j);
358+
j = strip;
359+
strip = false;
360+
}
361+
function isValidTagChar(str) {
362+
return str.match(/[a-z?\\\/!]/i);
363+
}
364+
var strip = false;
365+
var lastQuote = false;
366+
for (var i = 0; i<html.length; i++){
367+
if (html[i] === "<" && html[i+1] && isValidTagChar(html[i+1])) {
368+
i++;
369+
for (var j = i; j<html.length; j++){
370+
if (!lastQuote && html[j] === ">"){
371+
if (strip) {
372+
stripHTML();
373+
}
374+
i = j;
375+
break;
376+
}
377+
if (lastQuote === html[j]){
378+
lastQuote = false;
379+
continue;
380+
}
381+
if (!lastQuote && html[j-1] === "=" && (html[j] === "'" || html[j] === '"')){
382+
lastQuote = html[j];
383+
}
384+
if (!lastQuote && html[j-2] === " " && html[j-1] === "o" && html[j] === "n"){
385+
strip = j-2;
386+
}
387+
if (strip && html[j] === " " && !lastQuote){
388+
stripHTML();
389+
}
390+
}
391+
}
392+
}
393+
return html;
394+
},
324395
});
325396

326397
return ViewHelper;

Diff for: client/src/views/attachment/fields/name.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
* these Appropriate Legal Notices must retain the display of tтhe "EspoCRM" word.
2727
************************************************************************/
2828

29-
Espo.define('views/attachment/fields/name', 'views/fields/varchar', function (Dep) {
29+
define('views/attachment/fields/name', 'views/fields/varchar', function (Dep) {
3030

3131
return Dep.extend({
3232

Diff for: client/src/views/fields/wysiwyg.js

+1-24
Original file line numberDiff line numberDiff line change
@@ -165,30 +165,7 @@ Espo.define('views/fields/wysiwyg', ['views/fields/text', 'lib!Summernote'], fun
165165

166166

167167
sanitizeHtmlLight: function (value) {
168-
value = value || '';
169-
value = value.replace(/<[\/]{0,1}(base)[^><]*>/gi, '');
170-
value = value.replace(/<[\/]{0,1}(object)[^><]*>/gi, '');
171-
value = value.replace(/<[\/]{0,1}(embed)[^><]*>/gi, '');
172-
value = value.replace(/<[\/]{0,1}(applet)[^><]*>/gi, '');
173-
value = value.replace(/<[\/]{0,1}(iframe)[^><]*>/gi, '');
174-
value = value.replace(/<[\/]{0,1}(script)[^><]*>/gi, '');
175-
value = value.replace(/<[^><]*([^a-z]{1}on[a-z]+)=[^><]*>/gi, function (match) {
176-
return match.replace(/[^a-z]{1}on[a-z]+=/gi, ' data-handler-stripped=');
177-
});
178-
179-
value = value.replace(/href=" *javascript\:(.*?)"/gi, function(m, $1) {
180-
return 'removed=""';
181-
});
182-
value = value.replace(/href=' *javascript\:(.*?)'/gi, function(m, $1) {
183-
return 'removed=""';
184-
});
185-
value = value.replace(/src=" *javascript\:(.*?)"/gi, function(m, $1) {
186-
return 'removed=""';
187-
});
188-
value = value.replace(/src=' *javascript\:(.*?)'/gi, function(m, $1) {
189-
return 'removed=""';
190-
});
191-
return value;
168+
return this.getHelper().moderateSanitizeHtml(value);
192169
},
193170

194171
getValueForEdit: function () {

Diff for: client/src/views/preferences/fields/dashboard-tab-list.js

+9-7
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
2727
************************************************************************/
2828

29-
Espo.define('views/preferences/fields/dashboard-tab-list', 'views/fields/array', function (Dep) {
29+
define('views/preferences/fields/dashboard-tab-list', 'views/fields/array', function (Dep) {
3030

3131
return Dep.extend({
3232

@@ -39,17 +39,18 @@ Espo.define('views/preferences/fields/dashboard-tab-list', 'views/fields/array',
3939
this.translatedOptions[value] = value;
4040
}, this);
4141
},
42-
4342
getItemHtml: function (value) {
44-
var translatedValue = this.translatedOptions[value] || value;
43+
value = value.toString();
44+
var valueSanitized = this.escapeValue(value);
45+
var translatedValue = this.escapeValue(this.translatedOptions[value] || value);
4546

4647
var html = '' +
47-
'<div class="list-group-item link-with-role form-inline" data-value="' + value + '">' +
48+
'<div class="list-group-item link-with-role form-inline" data-value="' + valueSanitized + '">' +
4849
'<div class="pull-left" style="width: 92%; display: inline-block;">' +
49-
'<input data-name="translatedValue" data-value="' + value + '" class="role form-control input-sm" value="'+translatedValue+'">' +
50+
'<input data-name="translatedValue" data-value="' + valueSanitized + '" class="role form-control input-sm" value="'+translatedValue+'">' +
5051
'</div>' +
5152
'<div style="width: 8%; display: inline-block; vertical-align: top;">' +
52-
'<a href="javascript:" class="pull-right" data-value="' + value + '" data-action="removeValue"><span class="fas fa-times"></a>' +
53+
'<a href="javascript:" class="pull-right" data-value="' + valueSanitized + '" data-action="removeValue"><span class="fas fa-times"></a>' +
5354
'</div><br style="clear: both;" />' +
5455
'</div>';
5556

@@ -60,7 +61,8 @@ Espo.define('views/preferences/fields/dashboard-tab-list', 'views/fields/array',
6061
var data = Dep.prototype.fetch.call(this);
6162
data.translatedOptions = {};
6263
(data[this.name] || []).forEach(function (value) {
63-
data.translatedOptions[value] = this.$el.find('input[data-name="translatedValue"][data-value="'+value+'"]').val() || value;
64+
var valueInternal = value.replace(/"/g, '\\"');
65+
data.translatedOptions[value] = this.$el.find('input[data-name="translatedValue"][data-value="'+valueInternal+'"]').val() || value;
6466
}, this);
6567

6668
return data;

0 commit comments

Comments
 (0)