diff options
Diffstat (limited to '')
-rw-r--r-- | web/index.html | 259 |
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> red </b></span> is critical, <span style="color:#fe7d37"><b> orange </b></span> is warning, <span style="color: #4c1"><b> bright green </b></span> is ok, <span style="color: #9f9f9f"><b> light grey </b></span> is undefined (i.e. no data or no status), <span style="color: #000"><b> black </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/> <br/>role: <b>' + alarm.recipient + '</b><br/> <br/><b><i class="fas fa-chart-line"></i></b><small> <a href="#" onClick="scrollToChartAfterHidingModal(\'' + alarm.chart + '\'); $(\'#alarmsModal\').modal(\'hide\'); return false;">jump to chart</a></small>'):(' ')); + var action_buttons = '<br/> <br/>role: <b>' + alarm.recipient + '</b><br/> <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/> <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/> <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/> <br/><embed src="' + badge_url + '" type="image/svg+xml" height="20"/><br/> <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 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 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> |