send pipes URL encoded (#4358)
* send pipes URL encoded; fixes #3819 * use encodeURIComponent() for user supplied parameters * fix LGTM detected XSS * escape also parenthesis on URLs * escape also parenthesis on URLs no2
Costa Tsaousis committed
Oct 5, 2018 at 12:26 UTC
98f13d85150227cfc83648bfab1dd64b9a0217f6
3 files changed
+65
-34
web/dashboard.js
+22
-11
@@ -75,6 +75,14 @@
75
var NETDATA = window.NETDATA || {};
76
77
(function(window, document, $, undefined) {
78
+
79
+ NETDATA.encodeURIComponent = function(s) {
80
+ if(typeof(s) === 'string')
81
+ return encodeURIComponent(s);
82
+
83
+ return s;
84
+ };
85
+
86
// ------------------------------------------------------------------------
87
// compatibility fixes
88
@@ -2927,7 +2935,7 @@ var NETDATA = window.NETDATA || {};
2935
that.force_update_every *= 1000;
2936
2937
// the dimensions requested by the user
2930
- that.dimensions = NETDATA.dataAttribute(that.element, 'dimensions', null);
2938
+ that.dimensions = NETDATA.encodeURIComponent(NETDATA.dataAttribute(that.element, 'dimensions', null));
2939
2940
that.title = NETDATA.dataAttribute(that.element, 'title', null); // the title of the chart
2941
that.units = NETDATA.dataAttribute(that.element, 'units', null); // the units of the chart dimensions
@@ -2935,8 +2943,11 @@ var NETDATA = window.NETDATA || {};
2943
that.units_current = that.units;
2944
that.units_common = NETDATA.dataAttribute(that.element, 'common-units', null);
2945
2938
- that.append_options = NETDATA.dataAttribute(that.element, 'append-options', null); // additional options to pass to netdata
2939
- that.override_options = NETDATA.dataAttribute(that.element, 'override-options', null); // override options to pass to netdata
2946
+ // additional options to pass to netdata
2947
+ that.append_options = NETDATA.encodeURIComponent(NETDATA.dataAttribute(that.element, 'append-options', null));
2948
+
2949
+ // override options to pass to netdata
2950
+ that.override_options = NETDATA.encodeURIComponent(NETDATA.dataAttribute(that.element, 'override-options', null));
2951
2952
that.debug = NETDATA.dataAttributeBoolean(that.element, 'debug', false);
2953
@@ -4686,12 +4697,12 @@ var NETDATA = window.NETDATA || {};
4697
ret = this.library.options(this);
4698
4699
if(this.append_options !== null)
4689
- ret += '|' + this.append_options.toString();
4700
+ ret += '%7C' + this.append_options.toString();
4701
4691
- ret += '|jsonwrap';
4702
+ ret += '%7C' + 'jsonwrap';
4703
4704
if(NETDATA.options.current.eliminate_zero_dimensions === true)
4694
- ret += '|nonzero';
4705
+ ret += '%7C' + 'nonzero';
4706
4707
return ret;
4708
};
@@ -8468,7 +8479,7 @@ var NETDATA = window.NETDATA || {};
8479
enabled: true,
8480
xssRegexIgnore: new RegExp('^/api/v1/data\.result.data$'),
8481
format: function(state) { void(state); return 'json'; },
8471
- options: function(state) { return 'ms|flip' + (this.isLogScale(state)?'|abs':'').toString(); },
8482
+ options: function(state) { return 'ms' + '%7C' + 'flip' + (this.isLogScale(state)?('%7C' + 'abs'):'').toString(); },
8483
legend: function(state) {
8484
return (this.isSparkline(state) === false && NETDATA.dataAttributeBoolean(state.element, 'legend', true) === true) ? 'right-side' : null;
8485
},
@@ -8513,7 +8524,7 @@ var NETDATA = window.NETDATA || {};
8524
enabled: true,
8525
xssRegexIgnore: new RegExp('^/api/v1/data\.result$'),
8526
format: function(state) { void(state); return 'array'; },
8516
- options: function(state) { void(state); return 'flip|abs'; },
8527
+ options: function(state) { void(state); return 'flip' + '%7C' + 'abs'; },
8528
legend: function(state) { void(state); return null; },
8529
autoresize: function(state) { void(state); return false; },
8530
max_updates_to_recreate: function(state) { void(state); return 5000; },
@@ -8533,7 +8544,7 @@ var NETDATA = window.NETDATA || {};
8544
enabled: true,
8545
xssRegexIgnore: new RegExp('^/api/v1/data\.result$'),
8546
format: function(state) { void(state); return 'ssvcomma'; },
8536
- options: function(state) { void(state); return 'null2zero|flip|abs'; },
8547
+ options: function(state) { void(state); return 'null2zero' + '%7C' + 'flip' + '%7C' + 'abs'; },
8548
legend: function(state) { void(state); return null; },
8549
autoresize: function(state) { void(state); return false; },
8550
max_updates_to_recreate: function(state) { void(state); return 5000; },
@@ -8553,7 +8564,7 @@ var NETDATA = window.NETDATA || {};
8564
enabled: true,
8565
xssRegexIgnore: new RegExp('^/api/v1/data\.result.data$'),
8566
format: function(state) { void(state); return 'json'; },
8556
- options: function(state) { void(state); return 'objectrows|ms'; },
8567
+ options: function(state) { void(state); return 'objectrows' + '%7C' + 'ms'; },
8568
legend: function(state) { void(state); return null; },
8569
autoresize: function(state) { void(state); return false; },
8570
max_updates_to_recreate: function(state) { void(state); return 50; },
@@ -8633,7 +8644,7 @@ var NETDATA = window.NETDATA || {};
8644
enabled: true,
8645
xssRegexIgnore: new RegExp('^/api/v1/data\.result.data$'),
8646
format: function(state) { void(state); return 'json'; },
8636
- options: function(state) { void(state); return 'objectrows|ms'; },
8647
+ options: function(state) { void(state); return 'objectrows' + '%7C' + 'ms'; },
8648
legend: function(state) { void(state); return null; },
8649
autoresize: function(state) { void(state); return false; },
8650
max_updates_to_recreate: function(state) { void(state); return 5000; },
web/goto-host-from-alarm.html
+24
-10
@@ -34,12 +34,26 @@ function escapeUserInputHTML(s) {
34
.replace(/\)/g,')')
35
.replace(/\//g,'/');
36
}
37
-function escapeUserInputJS(s) {
38
- return s.toString()
39
- .replace(/"/g, '"')
40
- .replace(/'/g, ''')
41
- .replace(/\(/g,'(')
42
- .replace(/\)/g,')');
37
+
38
+// if string.startsWith is not defined, define it
39
+if(typeof String.prototype.startsWith !== 'function') {
40
+ String.prototype.startsWith = function(s) {
41
+ if(s.length > this.length) return false;
42
+ return this.slice(s.length) === s;
43
+ };
44
+}
45
+
46
+function verifyURL(s) {
47
+ if(typeof(s) === 'string' && (s.startsWith('http://') || s.startsWith('https://')))
48
+ return s
49
+ .replace(/'/g, '%22')
50
+ .replace(/"/g, '%27')
51
+ .replace(/\)/g, '%28')
52
+ .replace(/\(/g, '%29');
53
+
54
+ console.log('invalid URL detected:');
55
+ console.log(s);
56
+ return 'javascript:alert("invalid url");';
57
}
58
59
var urlOptions = {
@@ -107,7 +121,7 @@ function gotoServerValidateUrl(id, guid, url) {
121
var finalURL = netdataURL(url);
122
123
setTimeout(function() {
110
- document.getElementById('gotoServerList').innerHTML += '<tr><td style="padding-left: 20px;"><a href="' + escapeUserInputJS(finalURL) + '" target="_blank">' + escapeUserInputHTML(url) + '</a></td><td style="padding-left: 30px;"><code id="' + guid + '-' + id + '-status">checking...</code></td></tr>';
124
+ document.getElementById('gotoServerList').innerHTML += '<tr><td style="padding-left: 20px;"><a href="' + verifyURL(finalURL) + '" target="_blank">' + escapeUserInputHTML(url) + '</a></td><td style="padding-left: 30px;"><code id="' + guid + '-' + id + '-status">checking...</code></td></tr>';
125
126
NETDATA.registry.hello(url, function(data) {
127
if(typeof data !== 'undefined' && data !== null && typeof data.machine_guid === 'string' && data.machine_guid === guid) {
@@ -120,11 +134,11 @@ function gotoServerValidateUrl(id, guid, url) {
134
if(gotoServerMiddleClick) {
135
window.open(finalURL);
136
gotoServerMiddleClick = false;
123
- document.getElementById('gotoServerResponse').innerHTML = '<b>Opening new window to ' + NETDATA.registry.machines[guid].name + '<br/><a href="' + escapeUserInputJS(finalURL) + '">' + escapeUserInputHTML(url) + '</a></b><br/>(check your pop-up blocker if it fails)';
137
+ document.getElementById('gotoServerResponse').innerHTML = '<b>Opening new window to ' + NETDATA.registry.machines[guid].name + '<br/><a href="' + verifyURL(finalURL) + '">' + escapeUserInputHTML(url) + '</a></b><br/>(check your pop-up blocker if it fails)';
138
}
139
else {
140
document.getElementById('gotoServerResponse').innerHTML += 'found it! It is at:<br/><small>' + escapeUserInputHTML(url) + '</small>';
127
- document.location = escapeUserInputJS(finalURL);
141
+ document.location = verifyURL(finalURL);
142
}
143
}
144
}
@@ -140,7 +154,7 @@ function gotoServerValidateUrl(id, guid, url) {
154
155
if(thisIsHttps === true && urlsInHttp > 0) {
156
document.getElementById('gotoServerResponse').innerHTML += '<br/>redirecting myself to HTTP to allow checking';
143
- document.location = escapeUserInputJS(document.location.toString().replace('https://', 'http://'));
157
+ document.location = verifyURL(document.location.toString().replace('https://', 'http://'));
158
}
159
}
160
}
web/index.html
+19
-13
@@ -592,12 +592,18 @@
592
.replace(/\)/g,')')
593
.replace(/\//g,'/');
594
}
595
- function escapeUserInputJS(s) {
596
- return s.toString()
597
- .replace(/"/g, '"')
598
- .replace(/'/g, ''')
599
- .replace(/\(/g,'(')
600
- .replace(/\)/g,')');
595
+
596
+ function verifyURL(s) {
597
+ if(typeof(s) === 'string' && (s.startsWith('http://') || s.startsWith('https://')))
598
+ return s
599
+ .replace(/'/g, '%22')
600
+ .replace(/"/g, '%27')
601
+ .replace(/\)/g, '%28')
602
+ .replace(/\(/g, '%29');
603
+
604
+ console.log('invalid URL detected:');
605
+ console.log(s);
606
+ return 'javascript:alert("invalid url");';
607
}
608
609
// --------------------------------------------------------------------
@@ -1142,7 +1148,7 @@
1148
}
1149
1150
function netdataReload(url) {
1145
- document.location = netdataURL(url, true);
1151
+ document.location = verifyURL(netdataURL(url, true));
1152
1153
// since we play with hash
1154
// this is needed to reload the page
@@ -1150,7 +1156,7 @@
1156
}
1157
1158
function gotoHostedModalHandler(url) {
1153
- document.location = url + urlOptions.genHash();
1159
+ document.location = verifyURL(url + urlOptions.genHash());
1160
return false;
1161
}
1162
@@ -1172,7 +1178,7 @@
1178
var finalURL = netdataURL(url);
1179
1180
setTimeout(function() {
1175
- document.getElementById('gotoServerList').innerHTML += '<tr><td style="padding-left: 20px;"><a href="' + escapeUserInputJS(finalURL) + '" target="_blank">' + escapeUserInputHTML(url) + '</a></td><td style="padding-left: 30px;"><code id="' + guid + '-' + id + '-status">checking...</code></td></tr>';
1181
+ document.getElementById('gotoServerList').innerHTML += '<tr><td style="padding-left: 20px;"><a href="' + verifyURL(finalURL) + '" target="_blank">' + escapeUserInputHTML(url) + '</a></td><td style="padding-left: 30px;"><code id="' + guid + '-' + id + '-status">checking...</code></td></tr>';
1182
1183
NETDATA.registry.hello(url, function(data) {
1184
if(typeof data !== 'undefined' && data !== null && typeof data.machine_guid === 'string' && data.machine_guid === guid) {
@@ -1183,13 +1189,13 @@
1189
gotoServerStop = true;
1190
1191
if(gotoServerMiddleClick) {
1186
- window.open(finalURL, '_blank');
1192
+ window.open(verifyURL(finalURL), '_blank');
1193
gotoServerMiddleClick = false;
1188
- document.getElementById('gotoServerResponse').innerHTML = '<b>Opening new window to ' + NETDATA.registry.machines[guid].name + '<br/><a href="' + escapeUserInputJS(finalURL) + '">' + escapeUserInputHTML(url) + '</a></b><br/>(check your pop-up blocker if it fails)';
1194
+ document.getElementById('gotoServerResponse').innerHTML = '<b>Opening new window to ' + NETDATA.registry.machines[guid].name + '<br/><a href="' + verifyURL(finalURL) + '">' + escapeUserInputHTML(url) + '</a></b><br/>(check your pop-up blocker if it fails)';
1195
}
1196
else {
1197
document.getElementById('gotoServerResponse').innerHTML += 'found it! It is at:<br/><small>' + escapeUserInputHTML(url) + '</small>';
1192
- document.location = finalURL;
1198
+ document.location = verifyURL(finalURL);
1199
}
1200
}
1201
}
@@ -5780,6 +5786,6 @@
5786
</div>
5787
</div>
5788
<div id="hiddenDownloadLinks" style="display: none;" hidden></div>
5783
- <script type="text/javascript" src="dashboard.js?v20180922-1"></script>
5789
+ <script type="text/javascript" src="dashboard.js?v20181005-5"></script>
5790
</body>
5791
</html>