Skip to content

Commit 6bbb0ac

Browse files
authored
Merge pull request #6888 from Countly/ar2rsawseen/24.05
[security] Fix injection possibility on res.expose
2 parents ad98dde + 3add5ba commit 6bbb0ac

File tree

2 files changed

+29
-38
lines changed

2 files changed

+29
-38
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
## Version 24.05.xx
2+
Fixes:
3+
- [security] Fixed injection possibility on res.expose
4+
25
Enterprise Fixes:
36
- [data-manager] Fixed bug when merging events with ampersand symbol in the name
47
- [groups] Add logs for user updates

frontend/express/libs/express-expose.js

Lines changed: 26 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ function renderNamespace(str) {
157157
function renderObject(obj, namespace) {
158158
return Object.keys(obj).map(function(key) {
159159
var val = obj[key];
160-
return namespace + '["' + key + '"] = ' + string(val) + ';';
160+
return namespace + '["' + escape_js_string(key) + '"] = ' + string(val) + ';';
161161
}).join('\n');
162162
}
163163

@@ -180,61 +180,49 @@ function string(obj) {
180180
}
181181
else if ('[object Object]' === Object.prototype.toString.call(obj)) {
182182
return '{' + Object.keys(obj).map(function(key) {
183-
return '"' + key + '":' + string(obj[key]);
183+
return '"' + escape_js_string(key) + '":' + string(obj[key]);
184184
}).join(', ') + '}';
185185
}
186186
else {
187-
obj = escape_html(JSON.stringify(obj));
187+
obj = JSON.stringify(obj);
188188
if (obj) {
189+
// Only escape things that could break out of script context
189190
obj = obj.replace(/<\/script>/ig, '</scr"+"ipt>');
191+
obj = obj.replace(/<!--/g, '<\\!--');
192+
obj = obj.replace(/\u2028/g, '\\u2028'); // Line separator
193+
obj = obj.replace(/\u2029/g, '\\u2029'); // Paragraph separator
190194
}
191195
return obj;
192196
}
193197
}
194198

195-
var matchHtmlRegExp = /[<>]/;
196-
197199
/**
198-
* Escape special characters in the given string of html.
200+
* Escape special characters that could break JavaScript string context
199201
*
200-
* @param {string} str - The string to escape for inserting into HTML
202+
* @param {string} str - The string to escape
201203
* @return {string} escaped string
202204
* @public
203205
*/
204-
function escape_html(str) {
205-
str = '' + str;
206-
var match = matchHtmlRegExp.exec(str);
207-
208-
if (!match) {
206+
function escape_js_string(str) {
207+
if (typeof str !== 'string') {
209208
return str;
210209
}
211210

212-
var escape;
213-
var html = '';
214-
var index = 0;
215-
var lastIndex = 0;
216-
217-
for (index = match.index; index < str.length; index++) {
218-
switch (str.charCodeAt(index)) {
219-
case 60: // <
220-
escape = '&lt;';
221-
break;
222-
case 62: // >
223-
escape = '&gt;';
224-
break;
225-
default:
226-
continue;
227-
}
228-
229-
if (lastIndex !== index) {
230-
html += str.substring(lastIndex, index);
231-
}
232-
233-
lastIndex = index + 1;
234-
html += escape;
235-
}
236-
237-
return lastIndex !== index ? html + str.substring(lastIndex, index) : html;
211+
return str
212+
.replace(/\\/g, '\\\\') // Backslash
213+
.replace(/"/g, '\\"') // Double quote
214+
.replace(/'/g, "\\'") // Single quote
215+
.replace(/`/g, '\\`') // Backtick (template literal)
216+
.replace(/\$/g, '\\$') // Dollar sign (template literal)
217+
.replace(/\n/g, '\\n') // Newline
218+
.replace(/\r/g, '\\r') // Carriage return
219+
.replace(/\t/g, '\\t') // Tab
220+
.replace(/\f/g, '\\f') // Form feed
221+
.replace(/\v/g, '\\v') // Vertical tab
222+
.replace(/\0/g, '\\0') // Null character
223+
.replace(/[\u0000-\u001F\u007F-\u009F]/g, function(ch) {
224+
return '\\u' + ('0000' + ch.charCodeAt(0).toString(16)).slice(-4);
225+
});
238226
}
239227

240228
exports = module.exports = function(app) {

0 commit comments

Comments
 (0)