summaryrefslogtreecommitdiffstats
path: root/web/index.html
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--web/index.html259
1 files changed, 213 insertions, 46 deletions
diff --git a/web/index.html b/web/index.html
index e3c03909..ea146fc6 100644
--- a/web/index.html
+++ b/web/index.html
@@ -232,7 +232,7 @@
font-weight: 500;
color: #767676;
}
- .dashboard-sidebar .nav > li > a > .fa {
+ .dashboard-sidebar .nav > li > a > .svg-inline--fa {
width: 20px;
text-align: center;
}
@@ -517,6 +517,45 @@
}
}
+ .action-button {
+ position: relative;
+ display: inline-block;
+ color: gray;
+ cursor: pointer;
+ margin: 0 auto;
+ width: 30px;
+ height: 30px;
+ font-size: 25px;
+ }
+
+ .ripple {
+ position: relative;
+ /*overflow: hidden;*/
+ transform: translate3d(0, 0, 0)
+ }
+
+ .ripple:after {
+ content: "";
+ display: block;
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+ pointer-events: none;
+ background-image: radial-gradient(circle, #000 10%, transparent 10.01%);
+ background-repeat: no-repeat;
+ background-position: 50%;
+ transform: scale(18, 18); /* the size of the ripple */
+ opacity: 0;
+ transition: transform .5s, opacity 1s
+ }
+
+ .ripple:active:after {
+ transform: scale(0, 0);
+ opacity: .2;
+ transition: 0s
+ }
</style>
<!-- check which theme to use -->
@@ -635,6 +674,7 @@
if(urlOptions.server !== null && urlOptions.server !== '') {
netdataServerStatic = document.location.origin.toString() + document.location.pathname.toString();
netdataServer = urlOptions.server;
+ netdataCheckXSS = true;
}
else
urlOptions.server = null;
@@ -2016,6 +2056,15 @@
})
}
+ var clipboardLoaded = false;
+ function loadClipboard(callback) {
+ if(clipboardLoaded === false) {
+ clipboardLoaded = true;
+ loadJs('lib/clipboard-polyfill-be05dad.js', callback);
+ }
+ else callback();
+ }
+
var bootstrapTableLoaded = false;
function loadBootstrapTable(callback) {
if(bootstrapTableLoaded === false) {
@@ -2045,9 +2094,7 @@
function loadLzString(callback) {
if(lzStringLoaded === false) {
lzStringLoaded = true;
- loadJs('lib/lz-string-1.4.4.min.js', function() {
- callback();
- });
+ loadJs('lib/lz-string-1.4.4.min.js', callback);
}
else callback();
}
@@ -2056,18 +2103,30 @@
function loadPako(callback) {
if(pakoLoaded === false) {
pakoLoaded = true;
- loadJs('lib/pako-1.0.6.min.js', function() {
- callback();
- });
+ loadJs('lib/pako-1.0.6.min.js', callback);
}
else callback();
}
+ // ----------------------------------------------------------------------------
+
+ function clipboardCopy(text) {
+ clipboard.writeText(text);
+ }
+ function clipboardCopyBadgeEmbed(url) {
+ clipboard.writeText('<embed src="' + url + '" type="image/svg+xml" height="20"/>');
+ }
+
+
+ // ----------------------------------------------------------------------------
+
function alarmsUpdateModal() {
var active = '<h3>Raised Alarms</h3><table class="table">';
var all = '<h3>All Running Alarms</h3><div class="panel-group" id="alarms_all_accordion" role="tablist" aria-multiselectable="true">';
var footer = '<hr/><a href="https://github.com/firehol/netdata/wiki/Generating-Badges" target="_blank">netdata badges</a> refresh automatically. Their color indicates the state of the alarm: <span style="color: #e05d44"><b>&nbsp;red&nbsp;</b></span> is critical, <span style="color:#fe7d37"><b>&nbsp;orange&nbsp;</b></span> is warning, <span style="color: #4c1"><b>&nbsp;bright green&nbsp;</b></span> is ok, <span style="color: #9f9f9f"><b>&nbsp;light grey&nbsp;</b></span> is undefined (i.e. no data or no status), <span style="color: #000"><b>&nbsp;black&nbsp;</b></span> is not initialized. You can copy and paste their URLs to embed them in any web page.<br/>netdata can send notifications for these alarms. Check <a href="https://github.com/firehol/netdata/blob/master/conf.d/health_alarm_notify.conf">this configuration file</a> for more information.';
+ loadClipboard(function() {});
+
NETDATA.alarms.get('all', function(data) {
options.alarm_families = [];
@@ -2129,17 +2188,25 @@
function alarm_to_html(alarm, full) {
var chart = options.data.charts[alarm.chart];
if(typeof(chart) === 'undefined') {
- // this means the charts loaded are incomplete
- // probably netdata was restarted and more charts
- // are now available.
- return '';
+ chart = options.data.charts_by_name[alarm.chart];
+ if (typeof(chart) === 'undefined') {
+ // this means the charts loaded are incomplete
+ // probably netdata was restarted and more alarms
+ // are now available.
+ console.log('Cannot find chart ' + alarm.chart + ', you probably need to refresh the page.');
+ return '';
+ }
}
var has_alarm = (typeof alarm.warn !== 'undefined' || typeof alarm.crit !== 'undefined');
+ var badge_url = NETDATA.alarms.server + '/api/v1/badge.svg?chart=' + alarm.chart + '&alarm=' + alarm.name + '&refresh=auto';
- var role_href = ((has_alarm === true)?('<br/>&nbsp;<br/>role: <b>' + alarm.recipient + '</b><br/>&nbsp;<br/><b><i class="fas fa-chart-line"></i></b><small>&nbsp;&nbsp;<a href="#" onClick="scrollToChartAfterHidingModal(\'' + alarm.chart + '\'); $(\'#alarmsModal\').modal(\'hide\'); return false;">jump to chart</a></small>'):('&nbsp;'));
+ var action_buttons = '<br/>&nbsp;<br/>role: <b>' + alarm.recipient + '</b><br/>&nbsp;<br/>'
+ + '<div class="action-button ripple" title="click to scroll the dashboard to the chart of this alarm" data-toggle="tooltip" data-placement="bottom" onClick="scrollToChartAfterHidingModal(\'' + alarm.chart + '\'); $(\'#alarmsModal\').modal(\'hide\'); return false;"><i class="fab fa-periscope"></i></div>'
+ + '<div class="action-button ripple" title="click to copy to the clipboard the URL of this badge" data-toggle="tooltip" data-placement="bottom" onClick="clipboardCopy(\'' + badge_url + '\'); return false;"><i class="far fa-copy"></i></div>'
+ + '<div class="action-button ripple" title="click to copy to the clipboard an auto-refreshing <code>embed</code> html element for this badge" data-toggle="tooltip" data-placement="bottom" onClick="clipboardCopyBadgeEmbed(\'' + badge_url + '\'); return false;"><i class="fas fa-copy"></i></div>';
- var html = '<tr><td class="text-center" style="vertical-align:middle" width="40%"><b>' + alarm.chart + '</b><br/>&nbsp;<br/><embed src="' + NETDATA.alarms.server + '/api/v1/badge.svg?chart=' + alarm.chart + '&alarm=' + alarm.name + '&refresh=auto" type="image/svg+xml" height="20"/><br/>&nbsp;<br/><span style="font-size: 18px">' + alarm.info + '</span>' + role_href + '</td>'
+ var html = '<tr><td class="text-center" style="vertical-align:middle" width="40%"><b>' + alarm.chart + '</b><br/>&nbsp;<br/><embed src="' + badge_url + '" type="image/svg+xml" height="20"/><br/>&nbsp;<br/><span style="font-size: 18px">' + alarm.info + '</span>' + action_buttons + '</td>'
+ '<td><table class="table">'
+ ((typeof alarm.warn !== 'undefined')?('<tr><td width="10%" style="text-align:right">warning&nbsp;when</td><td><span style="font-family: monospace; color:#fe7d37; font-weight: bold;">' + alarm.warn + '</span></td></tr>'):'')
+ ((typeof alarm.crit !== 'undefined')?('<tr><td width="10%" style="text-align:right">critical&nbsp;when</td><td><span style="font-family: monospace; color: #e05d44; font-weight: bold;">' + alarm.crit + '</span></td></tr>'):'');
@@ -2194,6 +2261,7 @@
html += '</table>';
$('#alarm_all_' + id.toString()).html(html);
+ enableTooltipsAndPopovers();
}
// find the proper family of each alarm
@@ -2301,6 +2369,7 @@
document.getElementById('alarms_active').innerHTML = active;
document.getElementById('alarms_all').innerHTML = all;
+ enableTooltipsAndPopovers();
if(families_sorted.length > 0) alarm_family_show(0);
@@ -2747,32 +2816,78 @@
}
}
+ // an object to keep initilization configuration
+ // needed due to the async nature of the XSS modal
+ var initializeConfig = {
+ url: null,
+ custom_info: true,
+ };
+
+ function loadCustomDashboardInfo(url, callback) {
+ loadJs(url, function () {
+ $.extend(true, netdataDashboard, customDashboard);
+ callback();
+ });
+ }
+
+ function initializeChartsAndCustomInfo() {
+ NETDATA.alarms.callback = alarmsCallback;
+
+ // download all the charts the server knows
+ NETDATA.chartRegistry.downloadAll(initializeConfig.url, function(data) {
+ if(data !== null) {
+ if (initializeConfig.custom_info === true && typeof data.custom_info !== 'undefined' && data.custom_info !== "" && netdataSnapshotData === null) {
+ //console.log('loading custom dashboard decorations from server ' + initializeConfig.url);
+ loadCustomDashboardInfo(NETDATA.serverDefault + data.custom_info, function () {
+ initializeDynamicDashboardWithData(data);
+ });
+ }
+ else {
+ //console.log('not loading custom dashboard decorations from server ' + initializeConfig.url);
+ initializeDynamicDashboardWithData(data);
+ }
+ }
+ });
+ }
+
+ function xssModalDisableXss() {
+ //console.log('disabling xss checks');
+ NETDATA.xss.enabled = false;
+ NETDATA.xss.enabled_for_data = false;
+ initializeConfig.custom_info = true;
+ initializeChartsAndCustomInfo();
+ return false;
+ }
+
+ function xssModalKeepXss() {
+ //console.log('keeping xss checks');
+ NETDATA.xss.enabled = true;
+ NETDATA.xss.enabled_for_data = true;
+ initializeConfig.custom_info = false;
+ initializeChartsAndCustomInfo();
+ return false;
+ }
+
function initializeDynamicDashboard(netdata_url) {
if(typeof netdata_url === 'undefined' || netdata_url === null)
netdata_url = NETDATA.serverDefault;
+ initializeConfig.url = netdata_url;
+
// initialize clickable alarms
NETDATA.alarms.chart_div_offset = -50;
NETDATA.alarms.chart_div_id_prefix = 'chart_';
NETDATA.alarms.chart_div_animation_duration = 0;
NETDATA.pause(function() {
- NETDATA.alarms.callback = alarmsCallback;
-
- // download all the charts the server knows
- NETDATA.chartRegistry.downloadAll(netdata_url, function(data) {
- if(data !== null) {
- if(typeof data.custom_info !== 'undefined' && data.custom_info !== "" && netdataSnapshotData === null) {
- loadJs(NETDATA.serverDefault + data.custom_info, function () {
- $.extend(true, netdataDashboard, customDashboard);
- initializeDynamicDashboardWithData(data);
- });
- }
- else {
- initializeDynamicDashboardWithData(data);
- }
- }
- });
+ if(typeof netdataCheckXSS !== 'undefined' && netdataCheckXSS === true) {
+ //$("#loadOverlay").css("display","none");
+ document.getElementById('netdataXssModalServer').innerText = initializeConfig.url;
+ $('#xssModal').modal('show');
+ }
+ else {
+ initializeChartsAndCustomInfo();
+ }
});
}
@@ -2921,7 +3036,7 @@
function printPreflight() {
- var url = document.location.origin + document.location.search + '#' + urlOptions.genHash() + ';mode=print';
+ var url = document.location.origin.toString() + document.location.pathname.toString() + document.location.search.toString() + urlOptions.genHash() + ';mode=print';
var width = 990;
var height = screen.height * 90 / 100;
//console.log(url);
@@ -3141,6 +3256,7 @@
$('#loadSnapshotImport').addClass('disabled');
if(tmpSnapshotData === null) {
+ loadSnapshotPreflightEmpty();
loadSnapshotModalLog('danger', 'no data have been loaded');
return;
}
@@ -3207,6 +3323,10 @@
urlOptions.highlight = false;
}
+ netdataCheckXSS = false; // disable the modal - this does not affect XSS checks, since dashboard.js is already loaded
+ NETDATA.xss.enabled = true; // we should not do any remote requests, but if we do, check them
+ NETDATA.xss.enabled_for_data = true; // check also snapshot data - that have been excluded from the initial check, due to compression
+ loadSnapshotPreflightEmpty();
initializeDynamicDashboard();
});
});
@@ -3214,12 +3334,14 @@
function loadSnapshotPreflightFile(file) {
+ var filename = NETDATA.xss.string(file.name);
var fr = new FileReader();
fr.onload = function(e) {
- document.getElementById('loadSnapshotFilename').innerHTML = file.name;
+ document.getElementById('loadSnapshotFilename').innerHTML = filename;
var result = null;
try {
- result = JSON.parse(e.target.result);
+ result = NETDATA.xss.checkAlways('snapshot', JSON.parse(e.target.result), /^(snapshot\.info|snapshot\.data)$/);
+
//console.log(result);
var date_after = new Date(result.after_ms);
var date_before = new Date(result.before_ms);
@@ -3236,7 +3358,7 @@
if (typeof result.data_size === 'undefined')
result.data_size = 0;
- document.getElementById('loadSnapshotFilename').innerHTML = '<code>' + file.name + '</code>';
+ document.getElementById('loadSnapshotFilename').innerHTML = '<code>' + filename + '</code>';
document.getElementById('loadSnapshotHostname').innerHTML = '<b>' + result.hostname + '</b>, netdata version: <b>' + result.netdata_version.toString() + '</b>';
document.getElementById('loadSnapshotURL').innerHTML = result.url;
document.getElementById('loadSnapshotCharts').innerHTML = result.charts.charts_count.toString() + ' charts, ' + result.charts.dimensions_count.toString() + ' dimensions, ' + result.data_points.toString() + ' points per dimension, ' + Math.round(result.duration_ms / result.data_points).toString() + ' ms per point';
@@ -3268,6 +3390,7 @@
document.getElementById('loadSnapshotInfo').innerHTML = '';
document.getElementById('loadSnapshotTimeRange').innerHTML = '';
document.getElementById('loadSnapshotComments').innerHTML = '';
+ loadSnapshotModalLog('success', 'Browse for a snapshot file (or drag it and drop it here), then click <b>Import</b> to render it.');
$('#loadSnapshotImport').addClass('disabled');
};
@@ -3803,6 +3926,21 @@
};
}
+ // ----------------------------------------------------------------------------
+
+ function enableTooltipsAndPopovers() {
+ $('[data-toggle="tooltip"]').tooltip({
+ animated: 'fade',
+ trigger: 'hover',
+ html: true,
+ delay: {show: 500, hide: 0},
+ container: 'body'
+ });
+ $('[data-toggle="popover"]').popover();
+ }
+
+ // ----------------------------------------------------------------------------
+
var runOnceOnDashboardLastRun = 0;
function runOnceOnDashboardWithjQuery() {
if(runOnceOnDashboardLastRun !== 0) {
@@ -4197,16 +4335,7 @@
runOnceOnDashboardWithjQuery();
$(".shorten").shorten();
-
- $('[data-toggle="tooltip"]').tooltip({
- animated: 'fade',
- trigger: 'hover',
- html: true,
- delay: {show: 500, hide: 0},
- container: 'body'
- });
- $('[data-toggle="popover"]').popover();
-
+ enableTooltipsAndPopovers();
if(isdemo()) {
// do not to give errors on netdata demo servers for 60 seconds
@@ -4274,7 +4403,7 @@
});
NETDATA.requiredJs.push({
- url: NETDATA.serverStatic + 'dashboard_info.js?v20171208-1',
+ url: NETDATA.serverStatic + 'dashboard_info.js?v20180224-3',
async: false,
isAlreadyLoaded: function() { return false; }
});
@@ -4458,7 +4587,7 @@
<div class="col-md-10" role="main">
<div class="p">
<big><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></big><br/>
- <i class="fas fa-copyright"></i> Copyright 2016-2017, <a href="mailto:costa@tsaousis.gr">Costa Tsaousis</a>.<br/>
+ <i class="fas fa-copyright"></i> Copyright 2016-2018, <a href="mailto:costa@tsaousis.gr">Costa Tsaousis</a>.<br/>
Released under <a href="http://www.gnu.org/licenses/gpl-3.0.en.html" target="_blank">GPL v3 or later</a>.<br/>
</div>
<div class="p">
@@ -4474,6 +4603,9 @@
<i class="fas fa-circle"></i> <a href="http://bernii.github.io/gauge.js/" target="_blank">Gauge.js</a> web chart library,
<i class="fas fa-copyright"></i> Copyright, Bernard Kobos, <a href="http://bernii.github.io/gauge.js/" target="_blank">MIT License</a>
+ <i class="fas fa-circle"></i> <a href="https://github.com/benkeen/d3pie" target="_blank">d3pie</a> web chart library,
+ <i class="fas fa-copyright"></i> Copyright 2014-2015 Benjamin Keen, <a href="https://github.com/benkeen/d3pie/blob/master/LICENSE" target="_blank">MIT License</a>
+
<i class="fas fa-circle"></i> <a href="https://jquery.org/" target="_blank">jQuery</a>,
<i class="fas fa-copyright"></i> Copyright 2015, jQuery Foundation, <a href="https://jquery.org/license/" target="_blank">MIT License</a>
@@ -4503,6 +4635,40 @@
</div>
</div>
+ <div class="modal fade" id="xssModal" tabindex="-1" role="dialog" aria-labelledby="xssModalLabel" data-keyboard="false" data-backdrop="static" style="z-index: 3000">
+ <div class="modal-dialog modal-lg" role="document">
+ <div class="modal-content">
+ <div class="modal-header">
+ <h4 class="modal-title" id="xssModalLabel">XSS Protection</h4>
+ </div>
+ <div class="modal-body">
+ <p>
+ This dashboard is about to render data from server:
+ </p>
+ <p style="font-size: 1.25em;">
+ <code id="netdataXssModalServer"></code>
+ </p>
+ <p>
+ To protect your privacy, the dashboard will <b>check all data transferred</b> for cross site scripting (XSS).
+ <br/>This is CPU intensive, so your browser might be a bit slower.
+ </p>
+ <p>
+ If you <b>trust</b> the remote server, you can disable XSS protection.<br/>
+ In this case, any remote dashboard decoration code (javascript) will also run.
+ </p>
+ <p>
+ If you <b>don't trust</b> the remote server, you should keep the protection on.<br/>
+ The dashboard will run slower and remote dashboard decoration code will not run, but better be safe than sorry...
+ </p>
+ </div>
+ <div class="modal-footer">
+ <a href="#" onclick="return xssModalKeepXss();" type="button" class="btn btn-success" data-dismiss="modal">Keep protecting me</a>
+ <a href="#" onclick="return xssModalDisableXss();" type="button" class="btn btn-danger" data-dismiss="modal">I don't need this, the server is mine</a>
+ </div>
+ </div>
+ </div>
+ </div>
+
<div class="modal fade" id="printPreflightModal" tabindex="-1" role="dialog" aria-labelledby="printPreflightModalLabel">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
@@ -4593,6 +4759,7 @@
</p>
</div>
<div class="modal-footer">
+ <span style="display: inline-block; padding-right: 20px;">Snapshot files contain both data and javascript code. Make sure <b>you trust the files</b> you import!</span>
<a id="loadSnapshotImport" href="#" onclick="loadSnapshot(); return false;" type="button" class="btn btn-success disabled">Import</a>
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
@@ -5552,6 +5719,6 @@
</div>
</div>
<div id="hiddenDownloadLinks" style="display: none;" hidden></div>
- <script type="text/javascript" src="dashboard.js?v20171208-5"></script>
+ <script type="text/javascript" src="dashboard.js?v20180326-2"></script>
</body>
</html>