3055 lines
117 KiB
JavaScript
3055 lines
117 KiB
JavaScript
/*
|
|
* Copyright (c) 2014-2026 Maltrail developers (https://github.com/stamparm/maltrail/)
|
|
* See the file 'LICENSE' for copying permission
|
|
*/
|
|
|
|
// globals part
|
|
|
|
var _THREATS = {};
|
|
var _SOURCES = {};
|
|
var _TOP_SOURCES = [];
|
|
var _SOURCE_EVENTS = {};
|
|
var _SEVERITY_COUNT = {};
|
|
var _TRAILS = {};
|
|
var _FLOOD_TRAILS = {};
|
|
var _HOURS = {};
|
|
var _DATASET = [];
|
|
var _TRAILS_SORTED = null;
|
|
var _DELETE_DELETE_PRESS = false;
|
|
var _MAX_EVENTS_PER_HOUR = 0;
|
|
var _MAX_SPARKLINE_PER_HOUR = 0;
|
|
var _TOTAL_EVENTS = 0;
|
|
var _USER = null;
|
|
|
|
var IP_COUNTRY = {};
|
|
var CHECK_IP = {};
|
|
var TRAIL_TYPES = {};
|
|
|
|
var CONTEXT_MENU_ROW = null;
|
|
var SPARKLINE_WIDTH = 100;
|
|
var CHART_WIDTH = screen.width - 200;
|
|
var CHART_HEIGHT = screen.height - 340;
|
|
var PIE_FONT_SIZE = 10;
|
|
var MAX_SOURCES_ITEMS = 30;
|
|
var FLOOD_TRAIL_THRESHOLD = 50;
|
|
var LONG_TRAIL_THRESHOLD = 40;
|
|
var MAX_CONDENSED_ITEMS = 100;
|
|
var HIDDEN_THREAT_COUNT = 0;
|
|
var OTHER_COLOR = "#999";
|
|
var THREAT_INFIX = "~>";
|
|
var FLOOD_THREAT_PREFIX = "...";
|
|
var DGA_THREAT_INFIX = " dga "; // set by sensor (based on known DGAs)
|
|
var DNS_EXHAUSTION_THREAT_INFIX = " dns exhaustion "; // set by sensor
|
|
var DATA_PARTS_DELIMITER = ", ";
|
|
var SUSPICIOUS_THREAT_INFIX = "suspicious";
|
|
var HEURISTIC_THREAT_INFIX = "heuristic";
|
|
var FLOOD_UID_SUFFIX = "F0";
|
|
var DGA_UID_SUFFIX = "D0";
|
|
var DNS_EXHAUSTION_UID_SUFFIX = "N0";
|
|
var DEFAULT_STATUS_BORDER = "1px solid #a8a8a8";
|
|
var DEFAULT_FONT_FAMILY = "Verdana, Geneva, sans-serif";
|
|
var LOG_COLUMNS = { TIME: 0, SENSOR: 1, SRC_IP: 2, SRC_PORT: 3, DST_IP: 4, DST_PORT: 5, PROTO: 6, TYPE: 7, TRAIL: 8, INFO: 9, REFERENCE: 10 };
|
|
var LOG_COLUMNS_SIZE = 0;
|
|
var DATATABLES_COLUMNS = { THREAT: 0, SENSOR: 1, EVENTS: 2, SEVERITY: 3, FIRST_SEEN: 4, LAST_SEEN: 5, SPARKLINE: 6, SRC_IP: 7, SRC_PORT: 8, DST_IP: 9, DST_PORT: 10, PROTO: 11, TYPE: 12, TRAIL: 13, INFO: 14, REFERENCE: 15, TAGS: 16 };
|
|
var PORT_NAMES = { 1: "tcpmux", 2: "nbp", 4: "echo", 6: "zip", 7: "echo", 9: "discard", 11: "systat", 13: "daytime", 15: "netstat", 17: "qotd", 18: "msp", 19: "chargen", 20: "ftp-data", 21: "ftp", 22: "ssh", 23: "telnet", 25: "smtp", 37: "time", 39: "rlp", 42: "nameserver", 43: "whois", 49: "tacacs", 50: "re-mail-ck", 53: "dns", 57: "mtp", 65: "tacacs-ds", 67: "bootps", 68: "bootpc", 69: "tftp", 70: "gopher", 77: "rje", 79: "finger", 80: "http", 87: "link", 88: "kerberos", 95: "supdup", 98: "linuxconf", 101: "hostnames", 102: "iso-tsap", 104: "acr-nema", 105: "csnet-ns", 106: "poppassd", 107: "rtelnet", 109: "pop2", 110: "pop3", 111: "sunrpc", 113: "auth", 115: "sftp", 117: "uucp-path", 119: "nntp", 123: "ntp", 129: "pwdgen", 135: "dcom-rpc", 137: "netbios-ns", 138: "netbios-dgm", 139: "netbios-ssn", 143: "imap2", 161: "snmp", 162: "snmp-trap", 163: "cmip-man", 164: "cmip-agent", 174: "mailq", 177: "xdmcp", 178: "nextstep", 179: "bgp", 191: "prospero", 194: "irc", 199: "smux", 201: "at-rtmp", 202: "at-nbp", 204: "at-echo", 206: "at-zis", 209: "qmtp", 210: "z3950", 213: "ipx", 220: "imap3", 345: "pawserv", 346: "zserv", 347: "fatserv", 369: "rpc2portmap", 370: "codaauth2", 371: "clearcase", 372: "ulistserv", 389: "ldap", 406: "imsp", 427: "svrloc", 443: "https", 444: "snpp", 445: "smb", 464: "kpasswd", 465: "urd", 487: "saft", 500: "isakmp", 502: "modbus", 512: "exec", 513: "login", 514: "shell", 515: "printer", 517: "talk", 518: "ntalk", 520: "route", 525: "timed", 526: "tempo", 530: "courier", 531: "conference", 532: "netnews", 533: "netwall", 538: "gdomap", 540: "uucp", 543: "klogin", 544: "kshell", 546: "dhcpv6-client", 547: "dhcpv6-server", 548: "afpovertcp", 549: "idfp", 554: "rtsp", 556: "remotefs", 563: "nntps", 587: "submission", 607: "nqs", 610: "npmp-local", 611: "npmp-gui", 612: "hmmp-ind", 623: "ipmi", 628: "qmqp", 631: "ipp", 636: "ldaps", 655: "tinc", 706: "silc", 749: "kerberos-adm", 750: "kerberos4", 751: "kerberos-master", 752: "passwd-server", 754: "krb-prop", 760: "krbupdate", 765: "webster", 775: "moira-db", 777: "moira-update", 779: "moira-ureg", 783: "spamd", 808: "omirr", 871: "supfilesrv", 873: "rsync", 901: "swat", 989: "ftps-data", 990: "ftps", 992: "telnets", 993: "imaps", 994: "ircs", 995: "pop3s", 1080: "socks", 1093: "proofd", 1094: "rootd", 1099: "rmiregistry", 1109: "kpop", 1127: "supfiledbg", 1178: "skkserv", 1194: "openvpn", 1210: "predict", 1214: "kazaa", 1236: "rmtcfg", 1241: "nessus", 1300: "wipld", 1313: "xtel", 1314: "xtelw", 1352: "lotusnote", 1433: "mssql", 1434: "ms-sql-m", 1524: "ingreslock", 1525: "prospero-np", 1529: "support", 1645: "datametrics", 1646: "sa-msg-port", 1649: "kermit", 1677: "groupwise", 1701: "l2f", 1723: "pptp", 1812: "radius", 1813: "radius-acct", 1863: "msnp", 1900: "upnp", 1957: "unix-status", 1958: "log-server", 1959: "remoteping", 2000: "cisco-sccp", 2003: "cfinger", 2010: "search", 2049: "nfs", 2053: "knetd", 2086: "gnunet", 2101: "rtcm-sc104", 2102: "zephyr-srv", 2103: "zephyr-clt", 2104: "zephyr-hm", 2105: "eklogin", 2111: "kx", 2119: "gsigatekeeper", 2121: "iprop", 2135: "gris", 2150: "ninstall", 2401: "cvspserver", 2430: "venus", 2431: "venus-se", 2432: "codasrv", 2433: "codasrv-se", 2583: "mon", 2600: "zebrasrv", 2601: "zebra", 2602: "ripd", 2603: "ripngd", 2604: "ospfd", 2605: "bgpd", 2606: "ospf6d", 2607: "ospfapi", 2608: "isisd", 2628: "dict", 2792: "f5-globalsite", 2811: "gsiftp", 2947: "gpsd", 2988: "afbackup", 2989: "afmbackup", 3050: "gds-db", 3128: "squid", 3130: "icpv2", 3260: "iscsi-target", 3306: "mysql", 3389: "rdesktop", 3493: "nut", 3632: "distcc", 3689: "daap", 3690: "svn", 4031: "suucp", 4094: "sysrqd", 4190: "sieve", 4224: "xtell", 4353: "f5-iquery", 4369: "epmd", 4373: "remctl", 4500: "ipsec-nat-t", 4557: "fax", 4559: "hylafax", 4569: "iax", 4600: "distmp3", 4691: "mtn", 4899: "radmin-port", 4949: "munin", 5002: "rfe", 5050: "mmcc", 5051: "enbd-cstatd", 5052: "enbd-sstatd", 5060: "sip", 5061: "sip-tls", 5151: "pcrd", 5190: "aol", 5222: "xmpp-client", 5269: "xmpp-server", 5308: "cfengine", 5351: "nat-pmp", 5353: "mdns", 5354: "noclog", 5355: "hostmon", 5357: "wsdapi", 5432: "postgresql", 5555: "rplay", 5556: "freeciv", 5631: "pc-anywhere", 5666: "nrpe", 5667: "nsca", 5672: "amqp", 5674: "mrtd", 5675: "bgpsim", 5680: "canna", 5688: "ggz", 5800: "vnc", 5900: "vnc", 5901: "vnc-1", 5902: "vnc-2", 5903: "vnc-3", 6000: "x11", 6001: "x11-1", 6002: "x11-2", 6003: "x11-3", 6004: "x11-4", 6005: "x11-5", 6006: "x11-6", 6007: "x11-7", 6346: "gnutella-svc", 6347: "gnutella-rtr", 6379: "redis", 6444: "sge-qmaster", 6445: "sge-execd", 6446: "mysql-proxy", 6514: "syslog-tls", 6566: "sane-port", 6667: "ircd", 7000: "afs3-fileserver", 7001: "afs3-callback", 7002: "afs3-prserver", 7003: "afs3-vlserver", 7004: "afs3-kaserver", 7005: "afs3-volser", 7006: "afs3-errors", 7007: "afs3-bos", 7008: "afs3-update", 7009: "afs3-rmtsys", 7100: "font-service", 7547: "cwmp", 8021: "zope-ftp", 8080: "http-alt", 8081: "tproxy", 8088: "omniorb", 8118: "privoxy", 8338: "maltrail", 8339: "tsusen", 8443: "https-alt", 8990: "clc-build-daemon", 9098: "xinetd", 9101: "bacula-dir", 9102: "bacula-fd", 9103: "bacula-sd", 9200: "wap-wsp", 9359: "mandelspawn", 9418: "git", 9667: "xmms2", 9673: "zope", 10000: "webmin", 10050: "zabbix-agent", 10051: "zabbix-trapper", 10080: "amanda", 10081: "kamanda", 10082: "amandaidx", 10083: "amidxtape", 10809: "nbd", 11112: "dicom", 11201: "smsqp", 11211: "memcached", 11371: "hkp", 13720: "bprd", 13721: "bpdbm", 13722: "bpjava-msvc", 13724: "vnetd", 13782: "bpcd", 13783: "vopied", 15345: "xpilot", 17001: "sgi-cmsd", 17002: "sgi-crsd", 17003: "sgi-gcd", 17004: "sgi-cad", 17185: "vxworks", 17500: "db-lsp", 20011: "isdnlog", 20012: "vboxd", 22125: "dcap", 22128: "gsidcap", 22273: "wnn6", 24554: "binkp", 27017: "mongo", 27374: "asp", 30865: "csync2", 53413: "netis", 57000: "dircproxy", 60177: "tfido", 60179: "fido" };
|
|
var SEARCH_TIP_TIMER = 0;
|
|
var DRAW_SPARKLINES_TIMER = 0;
|
|
var PAPAPARSE_COMPLETE_TIMER = 0;
|
|
var REPORT_URL = "http://23.254.203.53/report.php" // NOTE: Right click / Report false positive
|
|
var SEARCH_TIP_URL = "https://www.vincentde.com/search?q=${query}"
|
|
|
|
//var SEARCH_TIP_URL = "https://www.searchencrypt.com/search/?q=%22${query}%22"; // Reference: https://kinsta.com/blog/alternative-search-engines/
|
|
//var SEARCH_TIP_URL = "https://duckduckgo.com/?q=${query}";
|
|
//var SEARCH_TIP_URL = "https://www.google.com/cse?cx=011750002002865445766%3Ay5klxdomj78&ie=UTF-8&q=${query}";
|
|
var DAY_SUFFIXES = { 1: "st", 2: "nd", 3: "rd" };
|
|
var DOT_COLUMNS = [ LOG_COLUMNS.SENSOR, LOG_COLUMNS.SRC_PORT, LOG_COLUMNS.SRC_IP, LOG_COLUMNS.DST_IP, LOG_COLUMNS.DST_PORT, LOG_COLUMNS.TRAIL, LOG_COLUMNS.PROTO ];
|
|
var DATA_CONDENSING_COLUMNS = [ LOG_COLUMNS.SRC_PORT, LOG_COLUMNS.DST_IP, LOG_COLUMNS.DST_PORT, LOG_COLUMNS.PROTO ];
|
|
var SPARKLINE_COLOR = "#ff0000";
|
|
var NONCE_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
var NONCE_LENGTH = 12;
|
|
var CHUNK_SIZE = 20 * 1024 * 1024; // 20MB
|
|
var CTRL_CLICK_PRESSED = false;
|
|
var CTRL_DATES = [];
|
|
var PREFERRED_TRAIL_COLORS = { DNS: "#3366cc", IP: "#dc3912", URL: "#ffad33", UA: "#9900cc" };
|
|
var SEVERITY = { LOW: 1, MEDIUM: 2, HIGH: 3 };
|
|
var SEVERITY_COLORS = { 1: "#8ba8c0", 2: "#f0ad4e", 3: "#d9534f"};
|
|
var CHART_TOOLTIP_FORMAT = "<%= datasetLabel %>: <%= value %>";
|
|
var INFO_SEVERITY_KEYWORDS = { "malware": SEVERITY.HIGH, "adversary": SEVERITY.HIGH, "ransomware": SEVERITY.HIGH, "reputation": SEVERITY.LOW, "attacker": SEVERITY.LOW, "spammer": SEVERITY.LOW, "compromised": SEVERITY.LOW, "crawler": SEVERITY.LOW, "scanning": SEVERITY.LOW }
|
|
var STORAGE_KEY_ACTIVE_STATUS_BUTTON = "STORAGE_KEY_ACTIVE_STATUS_BUTTON";
|
|
var STORAGE_KEY_EDIT_ALIASES = "STORAGE_KEY_EDIT_ALIASES";
|
|
var STORAGE_KEY_HIDDEN_THREATS = "STORAGE_KEY_HIDDEN_THREATS";
|
|
var COMMA_ENCODE_TRAIL_TYPES = { UA: true, URL: true};
|
|
var TOOLTIP_FOLDING_REGEX = /([^\s]{60})/g;
|
|
var REPLACE_SINGLE_CLOUD_WITH_BRACES = false;
|
|
var IP_ALIASES = {};
|
|
var DEMO = false;
|
|
|
|
$("body").loader("show");
|
|
$("#graph_close").on("click", graphClose);
|
|
$("#spanToggleHeatmap").on("click", toggleHeatmap);
|
|
$("#logo").on("click", resetView);
|
|
|
|
for (var column in LOG_COLUMNS) if (LOG_COLUMNS.hasOwnProperty(column)) LOG_COLUMNS_SIZE++;
|
|
|
|
var _ = {};
|
|
for (var i = 0; i < DATA_CONDENSING_COLUMNS.length; i++) {
|
|
_[DATA_CONDENSING_COLUMNS[i]] = true;
|
|
}
|
|
DATA_CONDENSING_COLUMNS = _;
|
|
|
|
window.onkeydown = function(event) {
|
|
CTRL_DATES.length = 0;
|
|
CTRL_CLICK_PRESSED = event.ctrlKey;
|
|
};
|
|
|
|
window.onkeyup = function(event) {
|
|
CTRL_CLICK_PRESSED = false;
|
|
};
|
|
|
|
+// Handle window resize to adjust table
|
|
$(window).resize(function() {
|
|
if (typeof $.fn.dataTable !== 'undefined') {
|
|
$('#details').DataTable().columns.adjust();
|
|
}
|
|
});
|
|
|
|
// Retrieve (and parse) log data
|
|
$(document).ready(function() {
|
|
$("#noscript").remove();
|
|
// assign buttons
|
|
$("#btnDrawThreats").on("click", function() {
|
|
drawInfo("Threats");
|
|
});
|
|
$("#btnDrawEvents").on("click", function() {
|
|
drawInfo("Events");
|
|
});
|
|
$("#btnDrawSeverity").on("click", function() {
|
|
drawInfo("Severity");
|
|
});
|
|
$("#btnDrawSources").on("click", function() {
|
|
drawInfo("Sources");
|
|
});
|
|
$("#btnDrawTrails").on("click", function() {
|
|
drawInfo("Trails");
|
|
});
|
|
|
|
// Reference: http://tosbourn.com/a-fix-for-window-location-origin-in-internet-explorer/
|
|
if (!window.location.origin)
|
|
window.location.origin = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port: '');
|
|
|
|
DEMO = !window.location.origin.startsWith('http') || (window.location.origin == "https://maltraildemo.github.io");
|
|
|
|
if (!DEMO) {
|
|
initCalHeatmap();
|
|
initDialogs();
|
|
}
|
|
|
|
Papa.SCRIPT_PATH = "/js/papaparse.min.js"
|
|
Papa.RemoteChunkSize = CHUNK_SIZE; // 10 MB (per one chunk request)
|
|
|
|
Chart.defaults.global.tooltipFontFamily = DEFAULT_FONT_FAMILY;
|
|
Chart.defaults.global.tooltipTitleFontFamily = DEFAULT_FONT_FAMILY;
|
|
Chart.defaults.global.scaleLabel = "<%=numberWithCommas(value)%>";
|
|
Chart.defaults.global.scaleFontFamily = DEFAULT_FONT_FAMILY;
|
|
Chart.defaults.global.animationSteps = 10;
|
|
|
|
$("#header_container").sticky({ topSpacing: 0 });
|
|
$("#graph_close").css("left", CHART_WIDTH / 2 - 11)
|
|
|
|
init(location.origin + "/events?date=" + formatDate(new Date()), new Date());
|
|
|
|
// Reference: https://stackoverflow.com/a/20471268
|
|
$(document).bind("mousedown", function (e) {
|
|
if (!$(e.target).parents(".custom-menu").length > 0) {
|
|
$(".custom-menu").hide(100);
|
|
}
|
|
});
|
|
|
|
$(".custom-menu li").click(function() {
|
|
var table = $("#details").dataTable();
|
|
var hidden_threats = $.jStorage.get(STORAGE_KEY_HIDDEN_THREATS, {});
|
|
var threat = $(CONTEXT_MENU_ROW).find("td:first").text();
|
|
|
|
switch ($(this).attr("data-action")) {
|
|
case "hide_threat":
|
|
hidden_threats[threat] = true;
|
|
HIDDEN_THREAT_COUNT += 1;
|
|
$.jStorage.set(STORAGE_KEY_HIDDEN_THREATS, hidden_threats);
|
|
table.api().row(CONTEXT_MENU_ROW).remove().draw();
|
|
break;
|
|
case "report_false_positive":
|
|
$.ajax({
|
|
type: "POST",
|
|
url: REPORT_URL,
|
|
data: $(CONTEXT_MENU_ROW).find("td").map(function() { return $(this).text(); }).get().join('|'),
|
|
success: function(result) { alertify.success("Threat successfully reported to developers"); }
|
|
})
|
|
break;
|
|
}
|
|
|
|
$(".custom-menu").hide(100);
|
|
});
|
|
});
|
|
|
|
function initDialogs() {
|
|
var options = {
|
|
autoOpen: false,
|
|
resizable: false,
|
|
//autoResize: true,
|
|
width: "auto",
|
|
modal: true,
|
|
buttons: {
|
|
Cancel: function() {
|
|
$(this).dialog("close");
|
|
},
|
|
"Log In": function() {
|
|
var SHA256 = new Hashes.SHA256;
|
|
var nonce = generateNonce();
|
|
|
|
$.ajax({
|
|
type: "POST",
|
|
url: "login",
|
|
dataType: "text",
|
|
data: "username=" + $(this).find("#username")[0].value.trim() + "&hash=" + SHA256.hex(SHA256.hex($(this).find("#password")[0].value.trim()) + nonce) + "&nonce=" + nonce,
|
|
cache: false,
|
|
beforeSend: function() {
|
|
$("input").prop("disabled", true);
|
|
$(".ui-dialog-buttonpane button").button("disable");
|
|
},
|
|
complete: function(response) {
|
|
$("input").prop("disabled", false);
|
|
$(".ui-dialog-buttonpane button").button("enable");
|
|
if(response.status === 401) {
|
|
alertify.error("Wrong username and/or password");
|
|
$("#login_dialog input").val("");
|
|
$("#login_dialog").effect("highlight", { color: 'red' }, 500);
|
|
$("#username").focus();
|
|
}
|
|
else if (response.status === 0) {
|
|
alertify.error("Network connection issue");
|
|
}
|
|
else {
|
|
window.location.href = "/";
|
|
}
|
|
}
|
|
});
|
|
}
|
|
},
|
|
close: function(event, ui) {
|
|
$(this).dialog('destroy').remove();
|
|
}
|
|
};
|
|
|
|
$("#login_link").click(function() {
|
|
$("body").loader("hide");
|
|
if ($("#login_dialog").length === 0) {
|
|
$('<div id="login_dialog" title="Authentication"><table><tbody><tr><td style="display: inline-block !important">Username:</td><td style="display: inline-block !important"><input id="username" name="username"></td></tr><tr><td style="display: inline-block !important">Password:</td><td style="display: inline-block !important"><input id="password" name="password" type="password" autocomplete="off"></td></tr></tbody></table></div>').appendTo('body').dialog(options);
|
|
} else {
|
|
$("#login_dialog").dialog("open");
|
|
}
|
|
$("#login_dialog input").val("");
|
|
$("#login_dialog").dialog("open")
|
|
.keyup(function(e) {
|
|
// Reference: http://stackoverflow.com/questions/868889/submit-jquery-ui-dialog-on-enter
|
|
if (e.keyCode === $.ui.keyCode.ENTER) {
|
|
$(this).parent().find('.ui-dialog-buttonpane button:last').click();
|
|
return false;
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
$(window).resize(function() {
|
|
$("#login_dialog").dialog("option", "position", {my: "center", at: "center", of: window});
|
|
});
|
|
|
|
function checkAuthentication() {
|
|
$.ajax({
|
|
type: "GET",
|
|
url: "whoami",
|
|
dataType: "text",
|
|
cache: false,
|
|
complete: function(response) {
|
|
if(response.status === 404) {
|
|
document.title = "Maltrail";
|
|
document.body.hidden = true;
|
|
throw new Error("Maltrail should be accessed ONLY at its server instance's address (e.g. http://127.0.0.1:8338)");
|
|
}
|
|
else if ((response.status === 200) && (typeof response.responseText !== "undefined") && (response.responseText.length > 0)) {
|
|
_USER = response.responseText;
|
|
if (_USER !== '?') {
|
|
$("#login_link").html("Log Out (" + _USER + ")");
|
|
$("#login_link").off("click");
|
|
$("#login_link").click(function() {
|
|
window.location.href = "logout";
|
|
});
|
|
}
|
|
else {
|
|
$("#login_link").html("");
|
|
}
|
|
|
|
if (window.location.search) {
|
|
var refresh = window.location.search.match(/refresh=(\d+)/);
|
|
if ((refresh !== null) && (parseInt(refresh[1]) > 0)) {
|
|
setTimeout(function(){
|
|
window.location.reload(true);
|
|
}, 1000 * parseInt(refresh[1]));
|
|
}
|
|
}
|
|
}
|
|
else if (window.location.origin.startsWith('http')) {
|
|
_USER = "";
|
|
document.title = "Maltrail (unauthorized)";
|
|
setTimeout(function() {
|
|
$("#login_link").click();
|
|
}, 1000);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function toggleHeatmap() {
|
|
if ($("#heatmap_container").is(":visible"))
|
|
$("#heatmap_container").hide();
|
|
else {
|
|
$("#heatmap_container").removeClass("hidden").show();
|
|
$(".graph-legend").attr("width", 14); // dirty patch for ugly legend width (missing one pixel)
|
|
scrollTo("#header_container-sticky-wrapper");
|
|
}
|
|
}
|
|
|
|
function graphClose() {
|
|
$("#chart_area").empty();
|
|
resetStatusButtons();
|
|
}
|
|
|
|
function initCalHeatmap() {
|
|
var start = new Date();
|
|
//start.setYear(start.getYear() - 1);
|
|
start.setDate(start.getDate() - 90);
|
|
var cal = new CalHeatMap();
|
|
|
|
try {
|
|
cal.init({
|
|
domain: "month",
|
|
subdomain: "day",
|
|
itemSelector: "#cal-heatmap",
|
|
range: 4,
|
|
cellSize: 13,
|
|
legendCellSize: 13,
|
|
legendHorizontalPosition: "right",
|
|
legendVerticalPosition: "center",
|
|
legendOrientation: "vertical",
|
|
maxDate: new Date(),
|
|
itemName: ["event", "events"],
|
|
domainLabelFormat: "%Y-%m",
|
|
legend: [ 500, 1000, 5000, 10000 ], // more than 4 will make it unusable (>last are not colorized)
|
|
legendMargin: [ 0, 0, 0, 20 ],
|
|
label: {
|
|
position: "bottom"
|
|
},
|
|
data: window.location.origin + "/counts?from={{d:start}}&to={{d:end}}",
|
|
highlight: [ "now" ],
|
|
subDomainTitleFormat: {
|
|
empty: "No events on {date}",
|
|
filled: "~{count} (total) events on {date}"
|
|
},
|
|
start: start,
|
|
onClick: function(date, nb) {
|
|
this.highlight(date);
|
|
if (!CTRL_CLICK_PRESSED) {
|
|
this.highlight(date);
|
|
query(date);
|
|
}
|
|
else {
|
|
CTRL_DATES.push(date);
|
|
this.highlight(CTRL_DATES);
|
|
if (CTRL_DATES.length === 2)
|
|
query(date, CTRL_DATES[0]);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
catch(err) {
|
|
}
|
|
finally {
|
|
$("#heatmap-previous").on("click", function() {
|
|
cal.previous();
|
|
});
|
|
|
|
$("#heatmap-next").on("click", function() {
|
|
cal.next();
|
|
});
|
|
}
|
|
}
|
|
|
|
function charTrim(str, chr) {
|
|
while (str.substr(0, 1) === chr)
|
|
str = str.substr(1);
|
|
while (str.substr(str.length - 1) === chr)
|
|
str = str.substr(0, str.length - 1);
|
|
return str;
|
|
}
|
|
|
|
// Reference: http://stackoverflow.com/questions/2901102/how-to-print-a-number-with-commas-as-thousands-separators-in-javascript
|
|
function numberWithCommas(x) {
|
|
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
}
|
|
|
|
// Reference: http://en.wikipedia.org/wiki/Private_network
|
|
function isLocalAddress(ip) {
|
|
if (ip.startsWith("10.") || ip.startsWith("192.168.") || ip.startsWith("127."))
|
|
return true;
|
|
else if (ip.startsWith("172.")) {
|
|
var _ = parseInt(ip.split(".")[1]);
|
|
return ((_ >= 16) && (_ <= 31));
|
|
}
|
|
else if (ip === "::1")
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
// Reference: https://www.w3resource.com/javascript-exercises/javascript-array-exercise-4.php
|
|
var last = function(array, n) {
|
|
if (array == null)
|
|
return void 0;
|
|
|
|
if (n == null)
|
|
return array[array.length - 1];
|
|
|
|
return array.slice(Math.max(array.length - n, 0));
|
|
};
|
|
|
|
var entityMap = {
|
|
"&": "&",
|
|
"<": "<",
|
|
">": ">",
|
|
'"': '"',
|
|
"'": ''',
|
|
"/": '/'
|
|
};
|
|
|
|
// Reference: http://stackoverflow.com/a/12034334
|
|
function escapeHtml(string) {
|
|
return String(string).replace(/[&<>"'\/]/g, function (s) {
|
|
return entityMap[s];
|
|
});
|
|
}
|
|
|
|
// Reference: http://24ways.org/2010/calculating-color-contrast/
|
|
function getContrast50(hexcolor) {
|
|
return (parseInt(hexcolor, 16) > 0xffffff / 2) ? "black": "white";
|
|
}
|
|
|
|
// Reference: http://stackoverflow.com/questions/340209/generate-colors-between-red-and-green-for-a-power-meter
|
|
function getPercentageColor(percentage) {
|
|
var power = percentage / 100.0;
|
|
|
|
if ((0 <= power) && (power < 0.5)) {
|
|
green = 1.0;
|
|
red = 2 * power;
|
|
}
|
|
if ((0.5 <= power) && (power <= 1)) {
|
|
red = 1.0;
|
|
green = 1.0 - 2 * (power - 0.5);
|
|
}
|
|
red = Math.round(red * 255);
|
|
green = Math.round(green * 255);
|
|
|
|
return "#" + pad(red.toString(16), 2) + pad(green.toString(16), 2) + "00";
|
|
}
|
|
|
|
function getContrastYIQ(hexcolor){
|
|
if (hexcolor.charAt(0) === "#")
|
|
hexcolor = hexcolor.slice(1);
|
|
|
|
var r = parseInt(hexcolor.substr(0, 2), 16);
|
|
var g = parseInt(hexcolor.substr(2, 2), 16);
|
|
var b = parseInt(hexcolor.substr(4, 2), 16);
|
|
var yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000;
|
|
|
|
return (yiq >= 128) ? "black" : "white";
|
|
}
|
|
|
|
function getTagHtml(tag) {
|
|
var retval = "";
|
|
|
|
if (tag.length > 0) {
|
|
var color = getHashColor(tag);
|
|
retval = String.prototype.concat.apply("", ['<span class="tag ', getContrastYIQ(color), '-label-text" style="background-color: ', color, '">', tag, '</span>']);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
function getHashColor(value) {
|
|
return "#" + pad(value.hashCode().toString(16), 6).substring(0, 6);
|
|
}
|
|
|
|
// Reference: http://stackoverflow.com/a/6969486
|
|
function escapeRegExp(str) {
|
|
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
|
}
|
|
|
|
function getThreatUID(threat) { // e.g. 192.168.0.1~>shv4.no-ip.biz
|
|
if (threat.startsWith(FLOOD_THREAT_PREFIX))
|
|
return pad(threat.hashCode().toString(16), 6).substr(0, 6) + FLOOD_UID_SUFFIX;
|
|
else if (threat.indexOf(DGA_THREAT_INFIX) > -1)
|
|
return pad(threat.hashCode().toString(16), 6).substr(0, 6) + DGA_UID_SUFFIX;
|
|
else if (threat.indexOf(DNS_EXHAUSTION_THREAT_INFIX) > -1)
|
|
return pad(threat.hashCode().toString(16), 6).substr(0, 6) + DNS_EXHAUSTION_UID_SUFFIX;
|
|
else
|
|
return pad(threat.hashCode().toString(16), 8);
|
|
}
|
|
|
|
function resetView() {
|
|
var table = $("#details").dataTable();
|
|
|
|
$("body").loader("show");
|
|
|
|
$("#main_container").toggleClass("hidden", true);
|
|
$("#heatmap_container").hide();
|
|
$("#calendar_container").hide();
|
|
|
|
table.fnFilter("");
|
|
graphClose();
|
|
|
|
window.location.href = window.location.href;
|
|
}
|
|
|
|
function init(url, from, to) {
|
|
var csv = "";
|
|
|
|
document.title = "Maltrail (loading...)";
|
|
$("body").loader("show");
|
|
$("#main_container").toggleClass("hidden", true);
|
|
$("#heatmap_container").hide();
|
|
$("#calendar_container").hide();
|
|
|
|
$(".alertify-log").remove();
|
|
|
|
_THREATS = {};
|
|
_SOURCES = {};
|
|
_SOURCE_EVENTS = {};
|
|
_TRAILS = {};
|
|
_FLOOD_TRAILS = {};
|
|
_HOURS = {};
|
|
_SEVERITY_COUNT = {}
|
|
TRAIL_TYPES = {};
|
|
|
|
_DATASET.length = 0;
|
|
_TOTAL_EVENTS = 0;
|
|
_CHUNK_COUNT = 0;
|
|
|
|
for (var severity in SEVERITY)
|
|
_SEVERITY_COUNT[SEVERITY[severity]] = 0;
|
|
|
|
if (DEMO) {
|
|
$(".bottom").html($(".bottom").html().replace(/ \(.+\)/, ""));
|
|
|
|
csv = getDemoCSV();
|
|
|
|
$("#login_link").toggleClass("hidden", true);
|
|
$("#login_splitter").toggleClass("hidden", true);
|
|
}
|
|
else {
|
|
$("#login_link").toggleClass("hidden", false);
|
|
$("#login_splitter").toggleClass("hidden", false);
|
|
|
|
$.ajax({
|
|
type: "GET",
|
|
url: "ping",
|
|
dataType: "text",
|
|
cache: false,
|
|
complete: function(response) {
|
|
if ((typeof response.responseText === "undefined") || (response.responseText.length === 0))
|
|
alertify.error("No connection to the server");
|
|
else
|
|
checkAuthentication();
|
|
}
|
|
});
|
|
}
|
|
|
|
Papa.parse(DEMO ? csv : url, {
|
|
download: !DEMO,
|
|
delimiter: ' ',
|
|
//newline: '\n',
|
|
worker: !DEMO,
|
|
skipEmptyLines: true,
|
|
chunk: function(results) {
|
|
var title = document.title.replace(/\s?\.\s?/g, '.');
|
|
var parts = title.split('.');
|
|
var total = results.data.length;
|
|
var _ = _CHUNK_COUNT % parts.length;
|
|
var trailSources = { };
|
|
|
|
if (_ < parts.length - 1)
|
|
parts[_] += " ";
|
|
else
|
|
parts[_] = " " + parts[_];
|
|
|
|
document.title = parts.join('.');
|
|
_CHUNK_COUNT += 1;
|
|
|
|
for (var i = 0; i < total; i++) {
|
|
var data = results.data[i];
|
|
|
|
if (data.length !== LOG_COLUMNS_SIZE)
|
|
continue;
|
|
|
|
var trail = data[LOG_COLUMNS.TRAIL];
|
|
var type = data[LOG_COLUMNS.TYPE];
|
|
|
|
if (type.match(/^[A-Z]+$/) === null)
|
|
continue;
|
|
|
|
if (!(type in TRAIL_TYPES))
|
|
TRAIL_TYPES[type] = PREFERRED_TRAIL_COLORS[type] || getHashColor(type);
|
|
|
|
if (type in COMMA_ENCODE_TRAIL_TYPES)
|
|
trail = trail.replace(/\,/g, ",");
|
|
|
|
trail = data[LOG_COLUMNS.TRAIL] = trail.replace(/\\\(/g, "(").replace(/\\\)/g, ")")
|
|
var _ = trail.replace(/\([^)]+\)/g, "");
|
|
|
|
if (!(_ in trailSources))
|
|
trailSources[_] = {};
|
|
|
|
trailSources[_][data[LOG_COLUMNS.SRC_IP]] = true;
|
|
|
|
_ += " (" + type + ")";
|
|
if (!(_ in _TRAILS))
|
|
_TRAILS[_] = 1;
|
|
else
|
|
_TRAILS[_] += 1;
|
|
}
|
|
|
|
for (var _ in trailSources) {
|
|
if (Object.size(trailSources[_]) > FLOOD_TRAIL_THRESHOLD)
|
|
_FLOOD_TRAILS[charTrim(_, '.')] = true;
|
|
}
|
|
|
|
for (var i = 0; i < results.data.length; i++) {
|
|
var threat_text, threat_data, match, _;
|
|
var data = results.data[i]
|
|
|
|
if (data.length !== LOG_COLUMNS_SIZE)
|
|
continue;
|
|
|
|
var time = data[LOG_COLUMNS.TIME];
|
|
var src_ip = data[LOG_COLUMNS.SRC_IP];
|
|
var type = data[LOG_COLUMNS.TYPE];
|
|
var info = data[LOG_COLUMNS.INFO];
|
|
var reference = data[LOG_COLUMNS.REFERENCE];
|
|
|
|
if (type.match(/^[A-Z]+$/) === null)
|
|
continue;
|
|
|
|
_ = data[LOG_COLUMNS.TRAIL];
|
|
_ = charTrim(charTrim(_.replace(/\([^)]+\)/g, ""), ' '), '.');
|
|
|
|
var flood = _ in _FLOOD_TRAILS;
|
|
var dga = info.indexOf(DGA_THREAT_INFIX) > -1;
|
|
var dns_exhaustion = info.indexOf(DNS_EXHAUSTION_THREAT_INFIX) > -1;
|
|
var heuristic = reference.indexOf(HEURISTIC_THREAT_INFIX) > -1;
|
|
|
|
if (dns_exhaustion)
|
|
threat_text = info + THREAT_INFIX + _;
|
|
else if (flood)
|
|
threat_text = FLOOD_THREAT_PREFIX + THREAT_INFIX + _;
|
|
else if (dga)
|
|
threat_text = src_ip + THREAT_INFIX + info;
|
|
else if (heuristic)
|
|
threat_text = src_ip + THREAT_INFIX + _ + info;
|
|
else
|
|
threat_text = src_ip + THREAT_INFIX + _;
|
|
|
|
_TOTAL_EVENTS += 1;
|
|
|
|
if (!(threat_text in _THREATS))
|
|
threat_data = _THREATS[threat_text] = [1, [time], time, time, data]; // count, times, minTime, maxTime, (threat)data
|
|
else {
|
|
match = time.match(/ (\d+:\d+)/);
|
|
|
|
threat_data = _THREATS[threat_text];
|
|
threat_data[0] += 1;
|
|
|
|
if (match !== null)
|
|
reduced = time.match(/ (\d+:\d+)/)[1];
|
|
else
|
|
reduced = time;
|
|
|
|
if (reduced != last(threat_data[1]))
|
|
threat_data[1].push(reduced);
|
|
|
|
if (time < threat_data[2])
|
|
threat_data[2] = time;
|
|
else if (time > threat_data[3])
|
|
threat_data[3] = time;
|
|
}
|
|
|
|
_ = threat_data[4];
|
|
|
|
for (var j = 0; j < DOT_COLUMNS.length; j++) {
|
|
var column = DOT_COLUMNS[j];
|
|
var condensed = (column in DATA_CONDENSING_COLUMNS) && data[column].contains(',');
|
|
if (condensed || (data[column] !== _[column])) {
|
|
if (typeof _[column] === "string") {
|
|
var original = _[column];
|
|
_[column] = {};
|
|
|
|
if (condensed) {
|
|
var parts = original.split(',');
|
|
for (var k = 0; k < Math.min(parts.length, MAX_CONDENSED_ITEMS); k++) {
|
|
_[column][parts[k]] = true;
|
|
}
|
|
|
|
if (parts.length > MAX_CONDENSED_ITEMS)
|
|
_[column]["..."] = true;
|
|
}
|
|
else
|
|
_[column][original] = true;
|
|
}
|
|
|
|
if (typeof data[column] === "string") {
|
|
if (condensed) {
|
|
var parts = data[column].split(',');
|
|
|
|
for (var k = 0; k < parts.length; k++) {
|
|
if (Object.keys(_[column]).length >= MAX_CONDENSED_ITEMS)
|
|
_[column]["..."] = true;
|
|
else
|
|
_[column][parts[k]] = true;
|
|
}
|
|
}
|
|
else {
|
|
if (Object.keys(_[column]).length < MAX_CONDENSED_ITEMS)
|
|
_[column][data[column]] = true;
|
|
else
|
|
_[column]["..."] = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!(src_ip in _SOURCES))
|
|
_SOURCES[src_ip] = 1;
|
|
else
|
|
_SOURCES[src_ip] += 1;
|
|
|
|
if (!(src_ip in _SOURCE_EVENTS)) {
|
|
_SOURCE_EVENTS[src_ip] = {};
|
|
|
|
for (var key in TRAIL_TYPES)
|
|
_SOURCE_EVENTS[src_ip][key] = 0;
|
|
}
|
|
_SOURCE_EVENTS[src_ip][type] += 1;
|
|
|
|
match = time.match(/(\d{4})-(\d{2})-(\d{2})\ (\d{2}):(\d{2}):(\d{2})/);
|
|
|
|
if (match !== null) {
|
|
var date = new Date(parseInt(match[1]), parseInt(match[2]) - 1, parseInt(match[3]), parseInt(match[4]), parseInt(match[5]), parseInt(match[6]));
|
|
var hour = Math.floor(date.getTime() / 60 / 60 / 1000);
|
|
|
|
if (!(hour in _HOURS)) {
|
|
_HOURS[hour] = {};
|
|
|
|
for (var item in TRAIL_TYPES)
|
|
_HOURS[hour][item] = 0;
|
|
}
|
|
|
|
_HOURS[hour][type] += 1;
|
|
|
|
if (!(threat_text in _HOURS[hour]))
|
|
_HOURS[hour][threat_text] = 0;
|
|
|
|
_HOURS[hour][threat_text] += 1;
|
|
}
|
|
}
|
|
},
|
|
complete: function() {
|
|
clearTimeout(PAPAPARSE_COMPLETE_TIMER);
|
|
PAPAPARSE_COMPLETE_TIMER = setTimeout(function() {
|
|
var hidden_threats = $.jStorage.get(STORAGE_KEY_HIDDEN_THREATS, {});
|
|
|
|
// threat sensor first_time last_time count src_ip src_port dst_ip dst_port proto type trail info reference tags
|
|
for (var threat_text in _THREATS) {
|
|
var threatUID = getThreatUID(threat_text);
|
|
var threat_data = _THREATS[threat_text];
|
|
|
|
// Skip invalid threat data
|
|
if (!threat_data || threat_data.length < 5) {
|
|
continue;
|
|
}
|
|
|
|
var count = threat_data[0];
|
|
var times = threat_data[1];
|
|
var minTime = threat_data[2];
|
|
var maxTime = threat_data[3];
|
|
var sparkline_data = [];
|
|
var data = threat_data[4];
|
|
var row = [];
|
|
var severity = SEVERITY.MEDIUM;
|
|
|
|
var stored_locally = $.jStorage.get(threatUID);
|
|
var tagData = "";
|
|
|
|
if (threatUID in hidden_threats) {
|
|
HIDDEN_THREAT_COUNT += 1;
|
|
continue;
|
|
}
|
|
|
|
if (stored_locally !== null)
|
|
tagData = stored_locally.tagData;
|
|
|
|
for (var i = 0; i < DOT_COLUMNS.length; i++) {
|
|
var column = DOT_COLUMNS[i];
|
|
|
|
if (typeof data[column] !== "string") {
|
|
var _ = [];
|
|
|
|
for (var entry in data[column]) {
|
|
if ((column === LOG_COLUMNS.TRAIL) && (data[LOG_COLUMNS.TYPE] === "IP") && (entry.indexOf('(') === -1))
|
|
continue;
|
|
_.push(entry.replace(DATA_PARTS_DELIMITER, DATA_PARTS_DELIMITER.replace(" ", "")));
|
|
}
|
|
|
|
if ((column === LOG_COLUMNS.SRC_PORT) || (column === LOG_COLUMNS.DST_PORT))
|
|
_.sort(function(a, b) {
|
|
a = parseInt(a);
|
|
b = parseInt(b);
|
|
return a < b ? -1 : (a > b ? 1 : 0);
|
|
});
|
|
else if ((column === LOG_COLUMNS.SRC_IP) || (column === LOG_COLUMNS.DST_IP))
|
|
_.sort(function(a, b) {
|
|
a = _ipSortingValue(a);
|
|
b = _ipSortingValue(b);
|
|
return a < b ? -1 : (a > b ? 1 : 0);
|
|
});
|
|
else
|
|
_.sort();
|
|
|
|
data[column] = _.join(DATA_PARTS_DELIMITER);
|
|
}
|
|
}
|
|
|
|
var min_ = null;
|
|
var max_ = null;
|
|
var _ = [];
|
|
|
|
for (var hour in _HOURS) {
|
|
if (min_ === null)
|
|
min_ = hour;
|
|
else
|
|
min_ = Math.min(min_, hour);
|
|
|
|
if (max_ === null)
|
|
max_ = hour;
|
|
else
|
|
max_ = Math.max(max_, hour);
|
|
}
|
|
|
|
if ((min_ !== null) && (max_ !== null)) {
|
|
var ms = 60 * 60 * 1000;
|
|
min_ = dayStart(min_ * ms) / ms;
|
|
max_ = dayEnd(max_ * ms) / ms;
|
|
|
|
for (var hour = min_; hour <= max_; hour++) {
|
|
if (!(hour in _HOURS))
|
|
_HOURS[hour] = {};
|
|
}
|
|
}
|
|
|
|
for (var hour in _HOURS)
|
|
_.push([hour >>> 0, _HOURS[hour][threat_text]]);
|
|
|
|
_.sort(function(a, b) {
|
|
a = a[0];
|
|
b = b[0];
|
|
|
|
return a < b ? -1 : (a > b ? 1 : 0);
|
|
});
|
|
|
|
for (var i = 0; i < 24; i++)
|
|
sparkline_data.push(0);
|
|
|
|
var total_days = Math.round(_.length / 24);
|
|
for (var i = 0; i < _.length; i++) {
|
|
sparkline_data[Math.floor(i / total_days)] += (_[i][1] | 0);
|
|
//_MAX_SPARKLINE_PER_HOUR = Math.max(_MAX_SPARKLINE_PER_HOUR, _[i][1] | 0);
|
|
}
|
|
|
|
if (data[LOG_COLUMNS.REFERENCE].contains("(custom)"))
|
|
severity = SEVERITY.HIGH;
|
|
else if (data[LOG_COLUMNS.REFERENCE].contains("(remote custom)"))
|
|
severity = SEVERITY.HIGH;
|
|
else if (data[LOG_COLUMNS.INFO].contains("potential malware site"))
|
|
severity = SEVERITY.MEDIUM;
|
|
else if (data[LOG_COLUMNS.REFERENCE].contains("malwaredomainlist"))
|
|
severity = SEVERITY.HIGH;
|
|
else if (data[LOG_COLUMNS.INFO].contains("malware distribution"))
|
|
severity = SEVERITY.MEDIUM;
|
|
else if (data[LOG_COLUMNS.INFO].contains("mass scanner"))
|
|
severity = SEVERITY.LOW;
|
|
else {
|
|
for (var keyword in INFO_SEVERITY_KEYWORDS)
|
|
if (data[LOG_COLUMNS.INFO].contains(keyword)) {
|
|
severity = INFO_SEVERITY_KEYWORDS[keyword];
|
|
break;
|
|
}
|
|
}
|
|
|
|
_SEVERITY_COUNT[severity] += 1;
|
|
|
|
row = [threatUID, data[LOG_COLUMNS.SENSOR], [count, times], severity, minTime, maxTime, sparkline_data.join(","), data[LOG_COLUMNS.SRC_IP], data[LOG_COLUMNS.SRC_PORT], data[LOG_COLUMNS.DST_IP], data[LOG_COLUMNS.DST_PORT], data[LOG_COLUMNS.PROTO], data[LOG_COLUMNS.TYPE], data[LOG_COLUMNS.TRAIL], data[LOG_COLUMNS.INFO], data[LOG_COLUMNS.REFERENCE], tagData];
|
|
|
|
_DATASET.push(row);
|
|
}
|
|
|
|
if (DEMO) {
|
|
alertify.log("Showing demo data");
|
|
|
|
document.title = "Maltrail (demo)";
|
|
$("#period_label").html("(demo)");
|
|
}
|
|
else {
|
|
if (_DATASET.length > 0)
|
|
alertify.success("Processed " + numberWithCommas(_TOTAL_EVENTS) + " events");
|
|
else
|
|
alertify.log("No events found");
|
|
|
|
var period = "";
|
|
|
|
if (typeof from !== "undefined") {
|
|
period += formatDate(from);
|
|
if (typeof to !== "undefined")
|
|
period += "_" + formatDate(to);
|
|
}
|
|
|
|
if (document.title.indexOf("unauthorized") === -1)
|
|
document.title = "Maltrail (" + period + ")";
|
|
|
|
scrollTo("#main_container");
|
|
|
|
var _ = moment(dayStart(from)).from(dayStart(new Date()));
|
|
if (_.indexOf("seconds") != -1)
|
|
_ = "today";
|
|
|
|
$("#period_label").html("<b>" + period + "</b> (" + _ + ")");
|
|
}
|
|
|
|
if (HIDDEN_THREAT_COUNT > 0)
|
|
alertify.log(HIDDEN_THREAT_COUNT + " threats are hidden in details table");
|
|
|
|
try {
|
|
initDetails();
|
|
initVisual();
|
|
}
|
|
catch(err) {
|
|
alert(err);
|
|
}
|
|
|
|
$("#main_container").toggleClass("hidden", false);
|
|
$("#main_container").children().toggleClass("hidden", false); // Reference: http://stackoverflow.com/a/4740050
|
|
$(".dynamicsparkline").parent().children().toggleClass("hidden", false);
|
|
|
|
$.sparkline_display_visible();
|
|
|
|
$("#chart_area").empty();
|
|
|
|
if (jQuery.isEmptyObject(_HOURS))
|
|
$("li.status-button").css("cursor", "default");
|
|
else
|
|
$("li.status-button").css("cursor", "pointer");
|
|
|
|
if ($.jStorage.get(STORAGE_KEY_ACTIVE_STATUS_BUTTON) !== null)
|
|
drawInfo($.jStorage.get(STORAGE_KEY_ACTIVE_STATUS_BUTTON))
|
|
else
|
|
resetStatusButtons();
|
|
|
|
$("#calendar_container").show();
|
|
$("#header_container").show();
|
|
$("body").loader("hide");
|
|
}, 500);
|
|
}
|
|
});
|
|
}
|
|
|
|
function resetStatusButtons() {
|
|
$.jStorage.deleteKey(STORAGE_KEY_ACTIVE_STATUS_BUTTON);
|
|
$("li.status-button").each(function() {
|
|
$(this).css("text-shadow", "-1px -1px 0 rgba(0, 0, 0, 0.50), -1px 1px 0 rgba(0, 0, 0, 0.50), 1px 1px 0 rgba(0, 0, 0, 0.50), 1px -1px 0 rgba(0, 0, 0, 0.50)");
|
|
$(this).css("border", "3px solid rgba(0, 0, 0, 0.50)");
|
|
});
|
|
$("#graph_close").hide();
|
|
}
|
|
|
|
function scrollTo(id) {
|
|
if ($(id).length > 0)
|
|
$("html, body").animate({
|
|
scrollTop: $(id).offset().top
|
|
}, 300);
|
|
}
|
|
|
|
function addrToInt(value) {
|
|
var _ = value.split('.');
|
|
return (_[0] << 24) + (_[1] << 16) + (_[2] << 8) + _[3];
|
|
}
|
|
|
|
function makeMask(bits) {
|
|
return 0xffffffff ^ (1 << 32 - bits) - 1;
|
|
}
|
|
|
|
function netmaskValidate(netmask) {
|
|
if (!netmask || typeof netmask !== 'string') {
|
|
return false;
|
|
}
|
|
var match = netmask.match(/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/\d{1,2}$/);
|
|
return match !== null;
|
|
}
|
|
|
|
function searchTipToTab(query) {
|
|
var win = window.open(SEARCH_TIP_URL.replace("${query}", query), '_blank');
|
|
|
|
// Reference: http://stackoverflow.com/a/19851803
|
|
if(win) {
|
|
// Browser has allowed it to be opened
|
|
win.focus();
|
|
} else {
|
|
// Browser has blocked it
|
|
alert('Please allow popups for this site');
|
|
}
|
|
}
|
|
|
|
function tagInputKeyUp(event, forcedelete) {
|
|
var table, position;
|
|
var tagData = null;
|
|
var newTag = event.target.value;
|
|
|
|
if (event.target.parentNode === null)
|
|
return;
|
|
|
|
if ((typeof forcedelete !== "undefined") || (event.keyCode === 8)) { // appendFilter or Delete
|
|
if ((typeof newTag === "undefined") || (newTag.length === 0)) {
|
|
if ((event.keyCode === 8) && (_DELETE_DELETE_PRESS !== true)) {
|
|
_DELETE_DELETE_PRESS = true;
|
|
return;
|
|
}
|
|
table = $("#details").dataTable();
|
|
position = table.fnGetPosition(event.target.parentNode);
|
|
tagData = table.fnGetData(event.target.parentNode);
|
|
|
|
if (tagData.length > 0) {
|
|
if (event.target.classList.contains("tag-input")) {
|
|
var i = tagData.lastIndexOf('|');
|
|
if (i > 0)
|
|
tagData = tagData.substring(0, i);
|
|
else
|
|
tagData = "";
|
|
}
|
|
else if (event.target.classList.contains("tag")) {
|
|
var tag = event.target.innerHTML;
|
|
var regex = new RegExp("(^|\\|)" + escapeRegExp(tag) + "($|\\|)");
|
|
tagData = charTrim(tagData.replace(regex, '|'), '|');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if ((typeof event.keyCode === "undefined") || (event.keyCode === 13)) { // blur or Enter
|
|
if (newTag.length > 0) {
|
|
table = $("#details").dataTable();
|
|
newTag = newTag.replace(/[^a-zA-Z0-9_]/g, "");
|
|
position = table.fnGetPosition(event.target.parentNode);
|
|
tagData = table.fnGetData(event.target.parentNode);
|
|
|
|
if (!(new RegExp("(^|\\|)" + escapeRegExp(newTag) + "($|\\|)").test(tagData)))
|
|
tagData = tagData + '|' + newTag;
|
|
//$(event.target).before(getTagHtml(newTag));
|
|
//newTag = "";
|
|
}
|
|
}
|
|
|
|
_DELETE_DELETE_PRESS = false;
|
|
|
|
if (tagData !== null) {
|
|
var api = table.api();
|
|
var threat = table.fnGetData(position[0], DATATABLES_COLUMNS.THREAT);
|
|
var row = api.row(position[0]);
|
|
var data = row.data();
|
|
|
|
$.jStorage.set(threat, { tagData: tagData });
|
|
|
|
data[DATATABLES_COLUMNS.TAGS] = tagData;
|
|
|
|
// dirty patch for #14900 (reproducible on Chromium - blur being thrown after the Enter has been processed)
|
|
try {
|
|
if (row && typeof row.invalidate === 'function') {
|
|
row.invalidate();
|
|
}
|
|
} catch(err) {
|
|
console.log("Table redraw issue:", err.message);
|
|
}
|
|
|
|
api.draw(false);
|
|
}
|
|
}
|
|
|
|
function stopPropagation(event) {
|
|
if (event.stopPropagation) {
|
|
event.stopPropagation(); // W3C model
|
|
} else {
|
|
event.cancelBubble = true; // IE model
|
|
}
|
|
}
|
|
|
|
function _sort(obj) {
|
|
var tuples = [];
|
|
|
|
for (var key in obj) {
|
|
if (typeof obj[key] !== "function")
|
|
tuples.push([key, obj[key]]);
|
|
}
|
|
|
|
tuples.sort(function(a, b) {
|
|
a = a[1];
|
|
b = b[1];
|
|
return a > b ? -1 : (a < b ? 1 : 0);
|
|
});
|
|
|
|
return tuples;
|
|
}
|
|
|
|
function _ipSortingValue(a) {
|
|
var x = "";
|
|
var match = a.match(/\d+\.\d+\.\d+\.\d+/);
|
|
if (match !== null) {
|
|
var m = match[0].split(".");
|
|
|
|
for (var i = 0; i < m.length; i++) {
|
|
var item = m[i];
|
|
|
|
if(item.length === 1) {
|
|
x += "00" + item;
|
|
} else if(item.length === 2) {
|
|
x += "0" + item;
|
|
} else {
|
|
x += item;
|
|
}
|
|
}
|
|
}
|
|
|
|
return x;
|
|
}
|
|
|
|
function _ipCompareValues(a, b) {
|
|
// Reference: http://stackoverflow.com/a/949970
|
|
return _ipSortingValue(a) - _ipSortingValue(b);
|
|
}
|
|
|
|
function copyEllipsisToClipboard(event) {
|
|
if (event.button === 0) { // left mouse button
|
|
var target = $(event.target);
|
|
var text = target.parent().title || '';
|
|
var html = target.parent().html() || '';
|
|
var left = html.search(/^<[^>]*ellipsis/) !== -1;
|
|
var common = html.replace(/<span class="(ipcat|hidden)">[^<]+<\/span>/g, "").replace(/<[^>]+>/g, "");
|
|
if (!text) {
|
|
var tooltip = $(".ui-tooltip");
|
|
if (tooltip.length > 0) {
|
|
text = tooltip.text();
|
|
|
|
if (common) {
|
|
var _ = text.split(DATA_PARTS_DELIMITER);
|
|
for (var i = 0; i < _.length; i++) {
|
|
if (left)
|
|
_[i] += common;
|
|
else {
|
|
if (!common.endsWith(' '))
|
|
_[i] = common + _[i];
|
|
}
|
|
}
|
|
text = _.join(DATA_PARTS_DELIMITER);
|
|
}
|
|
}
|
|
tooltip.remove();
|
|
}
|
|
window.prompt("Copy details to clipboard (press Ctrl+C)", text);
|
|
}
|
|
}
|
|
|
|
function copyEventsToClipboard(event) {
|
|
var target = $(event.target);
|
|
var text = target.parent().find("span").text();
|
|
window.prompt("Copy details to clipboard (press Ctrl+C)", text);
|
|
}
|
|
|
|
function appendFilter(filter, event, istag) {
|
|
try {
|
|
var table = $("#details").dataTable();
|
|
var currentFilter = table.api().search();
|
|
|
|
// Reference: http://stackoverflow.com/a/3076685
|
|
if (typeof event !== "undefined") {
|
|
stopPropagation(event);
|
|
|
|
if (event.button === 0) { // left mouse button
|
|
if (!(new RegExp("\\b" + escapeRegExp(filter) + "\\b").test(currentFilter))) {
|
|
currentFilter = currentFilter + " " + filter;
|
|
table.fnFilter(currentFilter.trim());
|
|
// table.DataTable().columns(11).search(currentFilter.trim()).draw();
|
|
}
|
|
}
|
|
else if ((istag === true) && (event.button === 1)) { // middle mouse button
|
|
tagInputKeyUp(event, true);
|
|
event.preventDefault();
|
|
}
|
|
}
|
|
else {
|
|
if (!(new RegExp("\\b" + escapeRegExp(filter) + "\\b").test(currentFilter))) {
|
|
currentFilter = currentFilter + " " + filter;
|
|
table.fnFilter(currentFilter.trim());
|
|
}
|
|
}
|
|
|
|
$(".searchtip").remove();
|
|
clearTimeout(SEARCH_TIP_TIMER);
|
|
$(".ui-tooltip").remove();
|
|
$(".custom-menu").hide();
|
|
$('#details_filter label input').get(0).scrollLeft = $('#details_filter label input').get(0).scrollWidth;
|
|
//$('#details_filter label input').get(0).focus();
|
|
}
|
|
catch(err) {
|
|
}
|
|
}
|
|
|
|
// DataTables part
|
|
function initDetails() {
|
|
var details = $("#details").dataTable( {
|
|
bDestroy: true,
|
|
bAutoWidth: false,
|
|
bStateSave: true,
|
|
data: _DATASET,
|
|
columns: [
|
|
{ "title": "threat", "type": "threat", "class": "center" },
|
|
{ "title": "sensor", "class": "center" },
|
|
{ "title": "events", "type": "events", "class": "right" },
|
|
{ "title": "severity", "type": "severity", "class": "center" },
|
|
{ "title": "first_seen", "class": "center", "sType": "date-custom" },
|
|
{ "title": "last_seen", "class": "center", "sType": "date-custom" },
|
|
{ "title": "sparkline", "type": "sparkline", "class": "center" },
|
|
{ "title": "src_ip", "type": "ip-address", "class": "right" },
|
|
{ "title": "src_port", "type": "port", "class": "center" },
|
|
{ "title": "dst_ip", "type": "ip-address", "class": "right" },
|
|
{ "title": "dst_port", "type": "port", "class": "center" },
|
|
{ "title": "proto", "class": "center" },
|
|
{ "title": "type", "class": "center" },
|
|
{ "title": "trail", "class": "trail" },
|
|
{ "title": "info" },
|
|
{ "title": "reference" },
|
|
{ "title": "tags" }
|
|
],
|
|
search: {
|
|
caseInsensitive: false
|
|
},
|
|
iDisplayLength: 25,
|
|
aLengthMenu: [ [10, 25, 50, 100, 200], [10, 25, 50, 100, 200] ],
|
|
aaSorting: [ [DATATABLES_COLUMNS.LAST_SEEN, 'desc'] ],
|
|
bDeferRender: true,
|
|
searchDelay: 500,
|
|
columnDefs: [
|
|
{
|
|
orderData: [DATATABLES_COLUMNS.SPARKLINE, DATATABLES_COLUMNS.SEVERITY, DATATABLES_COLUMNS.SRC_IP, DATATABLES_COLUMNS.EVENTS], targets: DATATABLES_COLUMNS.SPARKLINE
|
|
},
|
|
{
|
|
orderData: [DATATABLES_COLUMNS.SENSOR, DATATABLES_COLUMNS.EVENTS], targets: DATATABLES_COLUMNS.SENSOR
|
|
},
|
|
{
|
|
orderData: [DATATABLES_COLUMNS.SEVERITY, DATATABLES_COLUMNS.EVENTS], targets: DATATABLES_COLUMNS.SEVERITY
|
|
},
|
|
{
|
|
orderData: [DATATABLES_COLUMNS.INFO, DATATABLES_COLUMNS.EVENTS], targets: DATATABLES_COLUMNS.INFO
|
|
},
|
|
{
|
|
orderData: [DATATABLES_COLUMNS.REFERENCE, DATATABLES_COLUMNS.EVENTS], targets: DATATABLES_COLUMNS.REFERENCE
|
|
},
|
|
{
|
|
orderData: [DATATABLES_COLUMNS.SRC_IP, DATATABLES_COLUMNS.EVENTS], targets: DATATABLES_COLUMNS.SRC_IP
|
|
},
|
|
{
|
|
orderData: [DATATABLES_COLUMNS.DST_IP, DATATABLES_COLUMNS.EVENTS], targets: DATATABLES_COLUMNS.DST_IP
|
|
},
|
|
{
|
|
orderData: [DATATABLES_COLUMNS.TYPE, DATATABLES_COLUMNS.EVENTS], targets: DATATABLES_COLUMNS.TYPE
|
|
},
|
|
{
|
|
orderSequence: [ "desc", "asc" ],
|
|
targets: [ DATATABLES_COLUMNS.EVENTS, DATATABLES_COLUMNS.SEVERITY, DATATABLES_COLUMNS.LAST_SEEN, DATATABLES_COLUMNS.SPARKLINE ]
|
|
},
|
|
// {
|
|
// orderable: false,
|
|
// targets: DATATABLES_COLUMNS.SPARKLINE
|
|
// },
|
|
{
|
|
render: function (data, type, row) {
|
|
var name = "";
|
|
if (data === SEVERITY.LOW)
|
|
name = "low"
|
|
else if (data === SEVERITY.HIGH)
|
|
name = "high"
|
|
else if (data === SEVERITY.MEDIUM)
|
|
name = "medium";
|
|
var retval = "<span class='severity' style='background-color: " + SEVERITY_COLORS[data] + "' value='" + data + "'>" + name + "</span>"
|
|
return retval;
|
|
},
|
|
targets: DATATABLES_COLUMNS.SEVERITY
|
|
},
|
|
{
|
|
render: function (data, type, row) {
|
|
if (data.indexOf(',') > -1) {
|
|
var parts = data.split(DATA_PARTS_DELIMITER);
|
|
for (var i = 0; i < parts.length; i++) {
|
|
if (parts[i] in PORT_NAMES)
|
|
parts[i] = parts[i] + " (" + PORT_NAMES[parts[i]] + ")";
|
|
}
|
|
data = "<span title='" + parts.join(DATA_PARTS_DELIMITER) + "' class='ellipsis'></span>";
|
|
}
|
|
else {
|
|
if (data in PORT_NAMES)
|
|
data = data + " (" + PORT_NAMES[data] + ")";
|
|
}
|
|
return data;
|
|
},
|
|
targets: [ DATATABLES_COLUMNS.SRC_PORT, DATATABLES_COLUMNS.DST_PORT ]
|
|
},
|
|
{
|
|
render: function (data, type, row) {
|
|
if (data.indexOf(',') > -1)
|
|
data = "<span title='" + data + "' class='ellipsis'></span>";
|
|
else if (data in IP_ALIASES) {
|
|
data = "<span class='ipcat'>" + IP_ALIASES[data] + "</span>" + data;
|
|
}
|
|
else {
|
|
var stored = $.jStorage.get(STORAGE_KEY_EDIT_ALIASES);
|
|
if (stored !== null) {
|
|
if (data in stored)
|
|
data = "<span class='ipcat'>" + escapeHtml(stored[data]) + "</span>" + data;
|
|
}
|
|
}
|
|
return data;
|
|
},
|
|
targets: [ DATATABLES_COLUMNS.SRC_IP, DATATABLES_COLUMNS.DST_IP ]
|
|
},
|
|
{
|
|
render: function (data, type, row) {
|
|
if (data.indexOf(',') > -1)
|
|
data = "<span title='" + data + "' class='ellipsis'></span>" + "<span class='hidden'>" + data + "</span>";
|
|
return data;
|
|
},
|
|
targets: [ DATATABLES_COLUMNS.SENSOR, DATATABLES_COLUMNS.PROTO ]
|
|
},
|
|
{
|
|
render: function (data, type, row) {
|
|
var info = row[DATATABLES_COLUMNS.INFO];
|
|
|
|
data = data.replace(/</g, "<").replace(/>/g, ">");
|
|
|
|
if ((data.indexOf(',') > -1) || (data.replace(/&#\d+;/g, "|").length > LONG_TRAIL_THRESHOLD)) {
|
|
var common = "";
|
|
var title = "";
|
|
var left = false;
|
|
|
|
if ((data.indexOf('(') > -1) && (data.indexOf(')') > -1)) {
|
|
var parts = data.split(DATA_PARTS_DELIMITER);
|
|
for (var i = 0; i < parts.length; i++) {
|
|
var index = parts[i].indexOf('(');
|
|
if (index > -1) {
|
|
if (index === 0)
|
|
left = true;
|
|
common = parts[i].replace(/\(.+\)/g, "");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
var parts = data.split(DATA_PARTS_DELIMITER);
|
|
for (var i = 0; i < parts.length; i++)
|
|
parts[i] = parts[i].replace(common, "").replace(/[()]/g, ""); // replace only first occurrence
|
|
|
|
title = parts.join(DATA_PARTS_DELIMITER);
|
|
|
|
// title = data.replace(common, "").replace(/[()]/g, "");
|
|
// title = data.split(common).join("").replace(/[()]/g, "");
|
|
|
|
if (common.startsWith('.'))
|
|
title = title.split(common.substr(1)).join("");
|
|
|
|
try {
|
|
title = decodeURIComponent(title);
|
|
}
|
|
catch(err) {
|
|
}
|
|
finally {
|
|
title = title.replace(/"/g, '%22');
|
|
}
|
|
|
|
// Reference: https://stackoverflow.com/questions/3340802/add-line-break-within-tooltips
|
|
title = title.replace(TOOLTIP_FOLDING_REGEX, "$1 ");
|
|
|
|
common = '<span class="trail-text">' + common + '</span>';
|
|
|
|
if (left)
|
|
data = "<span title=\"" + title + "\" class='ellipsis'></span>" + common;
|
|
else
|
|
data = common + "<span title=\"" + title + "\" class='ellipsis'></span>";
|
|
}
|
|
else {
|
|
if (REPLACE_SINGLE_CLOUD_WITH_BRACES)
|
|
data = data.replace('(', '{').replace(')', '}');
|
|
|
|
if (info.contains("sinkholed"))
|
|
data = data.replace('(', '').replace(')', '');
|
|
|
|
data = '<span class="trail-text">' + data + '</span>';
|
|
}
|
|
|
|
return data;
|
|
},
|
|
targets: [ DATATABLES_COLUMNS.TRAIL ]
|
|
},
|
|
{
|
|
render: function ( data, type, row ) {
|
|
return '<div class="event-data">' + data[0] + '</div><span class="hidden">' + data[1].join(DATA_PARTS_DELIMITER) + '</span>';
|
|
},
|
|
targets: DATATABLES_COLUMNS.EVENTS
|
|
},
|
|
{
|
|
render: function ( data, type, row ) {
|
|
var color = data in TRAIL_TYPES ? TRAIL_TYPES[data] : getHashColor(data);
|
|
return '<span class="label-type white-label-text" style="background-color: ' + color + '">' + data + '</span>';
|
|
},
|
|
targets: DATATABLES_COLUMNS.TYPE
|
|
},
|
|
{
|
|
render: function (data, type, row) {
|
|
var parts = data.split(' ');
|
|
if (parts.length > 1) {
|
|
var day = parts[0].split('-')[2];
|
|
var dayint = parseInt(day);
|
|
var suffix = (dayint > 10 && dayint < 20) ? "th" : DAY_SUFFIXES[dayint % 10] || "th";
|
|
return "<div title='" + data + "'><span class='time-day'>" + day + "<sup>" + suffix + "</sup></span> " + parts[1].split('.')[0] + "</div>";
|
|
}
|
|
else
|
|
return data;
|
|
},
|
|
targets: [ DATATABLES_COLUMNS.FIRST_SEEN, DATATABLES_COLUMNS.LAST_SEEN ]
|
|
},
|
|
{
|
|
render: function (data, type, row) {
|
|
var value = 0;
|
|
var items = data.split(",");
|
|
for (var i = 0; i < items.length; i++)
|
|
if (items[i] !== "0")
|
|
value += 1;
|
|
return "<div class='sparkline hidden' value='" + value + "'>" + data + "</div>";
|
|
},
|
|
targets: DATATABLES_COLUMNS.SPARKLINE
|
|
},
|
|
{
|
|
render: function (data, type, row) {
|
|
return '<div class="label-type ' + getContrastYIQ(data.substring(0, 6)) + '-label-text" style="background-color: #' + data.substring(0, 6) + '">' + data + '</div>';
|
|
},
|
|
targets: DATATABLES_COLUMNS.THREAT
|
|
},
|
|
{
|
|
render: function (data, type, row) {
|
|
if (data.contains('(+')) {
|
|
var duplicates = data.match(/ \((\+\d+)\)$/);
|
|
if (duplicates !== null) {
|
|
data = data.replace(duplicates[0], "");
|
|
return ((data.substr(0, 1) != '(') ? '<i>' + data + '</i>': data) + '<span class="duplicates">' + duplicates[1] + '</span>';
|
|
}
|
|
|
|
duplicates = data.match(/ \(\+(.+)\)$/);
|
|
if (duplicates !== null) {
|
|
var items = duplicates[1].split(',');
|
|
data = data.replace(duplicates[0], "");
|
|
return ((data.substr(0, 1) != '(') ? '<i>' + data + '</i>': data) + '<span title="' + duplicates[1].replace(/,([^ ])/g, ", $1") + '" class="duplicates">+' + items.length + '</span>' + '<span class="searchable">' + duplicates[1] + "</span>";
|
|
}
|
|
}
|
|
return (data.substr(0, 1) != '(') ? '<i>' + data + '</i>': data;
|
|
},
|
|
targets: DATATABLES_COLUMNS.REFERENCE
|
|
},
|
|
{
|
|
render: function (data, type, row) {
|
|
var retval = "";
|
|
var tags = data.split('|');
|
|
for (var index in tags) {
|
|
var tag = tags[index];
|
|
if ((typeof tag !== "function") && (tag.length > 0)) {
|
|
retval += getTagHtml(tag);
|
|
}
|
|
}
|
|
retval += "<input class='tag-input' type='text'>";
|
|
return retval;
|
|
},
|
|
targets: DATATABLES_COLUMNS.TAGS
|
|
},
|
|
{
|
|
width: "1%",
|
|
targets: [ DATATABLES_COLUMNS.THREAT, DATATABLES_COLUMNS.SENSOR, DATATABLES_COLUMNS.EVENTS, DATATABLES_COLUMNS.SEVERITY, DATATABLES_COLUMNS.FIRST_SEEN, DATATABLES_COLUMNS.LAST_SEEN, DATATABLES_COLUMNS.SPARKLINE, DATATABLES_COLUMNS.SRC_IP, DATATABLES_COLUMNS.SRC_PORT, DATATABLES_COLUMNS.DST_IP, DATATABLES_COLUMNS.DST_PORT, DATATABLES_COLUMNS.PROTO, DATATABLES_COLUMNS.TYPE ]
|
|
}
|
|
],
|
|
oLanguage: {
|
|
sLengthMenu: "_MENU_ threats per page", // Reference: http://www.sprymedia.co.uk/dataTables/example_language.html
|
|
sZeroRecords: "No matching threats found",
|
|
sInfo: "Showing _START_ to _END_ of <span class='details_total'>_TOTAL_</span> threats",
|
|
sInfoEmpty: "Showing 0 to 0 of <span class='details_total'>0</span> total threats",
|
|
sInfoFiltered: "(<span style='color: red'>filtered</span> from _MAX_ total threats)",
|
|
sSearchPlaceholder: "Filter",
|
|
sSearch: ""
|
|
},
|
|
dom: 'T<"clear">lfrtip',
|
|
tableTools: {
|
|
aButtons: [
|
|
{
|
|
sExtends: "text",
|
|
sButtonText: "Clear",
|
|
fnClick: function (nButton, oConfig, oFlash) {
|
|
var table = $("#details").dataTable();
|
|
var settings = table.fnSettings();
|
|
table.fnFilter("");
|
|
// if ((settings._iDisplayLength > 0) && (settings._iDisplayLength < 30))
|
|
// $("html, body").animate({ scrollTop: $(document).height() }, "slow");
|
|
}
|
|
},
|
|
"print",
|
|
{
|
|
sExtends: "collection",
|
|
sButtonText: "Tools",
|
|
aButtons: [
|
|
{
|
|
sExtends: "text",
|
|
sButtonText: "Edit custom IP aliases",
|
|
fnClick: function ( nButton, oConfig, oFlash ) {
|
|
function _initAliases() {
|
|
$('#table_aliases').editableTableWidget();
|
|
$('#table_aliases td').on('change', function(event, newValue) {
|
|
if (event.target.cellIndex === 0)
|
|
return (newValue.match(/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) !== null);
|
|
});
|
|
};
|
|
|
|
var html = '<table id="table_aliases" class="dataTable"><thead><tr class="ui-widget-header"><th>IP</th><th>Alias</th></tr></thead><tbody>';
|
|
var stored = $.jStorage.get(STORAGE_KEY_EDIT_ALIASES);
|
|
if (stored !== null) {
|
|
$.each( stored, function( ip, alias ) {
|
|
html += '<tr class="alias"><td>' + ip + '</td><td>' + alias + '</td></tr>'
|
|
});
|
|
}
|
|
html += '</tbody></table>'
|
|
|
|
$('<div id="dialog-aliases" title="Aliases"></div>').appendTo('body')
|
|
.html(html)
|
|
.dialog({
|
|
resizable: false,
|
|
width: "auto",
|
|
height: "auto",
|
|
modal: true,
|
|
buttons: {
|
|
Add: function() {
|
|
$('#table_aliases tr:last').after('<tr class="alias"><td> </td><td> </td></tr>');
|
|
_initAliases();
|
|
$("#table_aliases tr:last td:first").focus().click();
|
|
},
|
|
Done: function() {
|
|
$(this).dialog("close");
|
|
$('#dialog-aliases').remove();
|
|
window.location.reload(false);
|
|
}
|
|
},
|
|
open: function(event, ui) {
|
|
_initAliases();
|
|
},
|
|
close: function(event, ui) {
|
|
var result = {};
|
|
$(this).find(".alias").each(function() {
|
|
var ip = $(this).find("td").eq(0).text();
|
|
var alias = $(this).find("td").eq(1).text();
|
|
if ($.trim(ip) && $.trim(alias))
|
|
result[ip] = alias;
|
|
});
|
|
$.jStorage.set(STORAGE_KEY_EDIT_ALIASES, result);
|
|
|
|
$(this).dialog("destroy").remove();
|
|
}
|
|
});
|
|
}
|
|
},
|
|
{
|
|
sExtends: "text",
|
|
sButtonText: "Flush local storage",
|
|
fnClick: function ( nButton, oConfig, oFlash ) {
|
|
$('<div id="dialog-confirm" title="Flush local storage?"></div>').appendTo('body')
|
|
.html('<p><span class="ui-icon ui-icon-alert" style="float:left; margin:0 7px 20px 0;"></span>These items will be permanently deleted and<br>won\'t be recoverable. Are you sure?</p>')
|
|
.dialog({
|
|
resizable: false,
|
|
width: "auto",
|
|
height: "auto",
|
|
modal: true,
|
|
buttons: {
|
|
Cancel: function() {
|
|
$(this).dialog("close");
|
|
},
|
|
"Delete all items": function() {
|
|
$(this).dialog("close");
|
|
$.jStorage.flush();
|
|
location.reload();
|
|
}
|
|
},
|
|
close: function(event, ui) {
|
|
$(this).dialog('destroy').remove();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
fnStateSaveParams: function (oSettings, oData) {
|
|
oData.start = 0;
|
|
},
|
|
fnDrawCallback: function(oSettings) {
|
|
$(".ui-tooltip").remove();
|
|
$(".custom-menu").hide();
|
|
clearTimeout(DRAW_SPARKLINES_TIMER);
|
|
$(".sparkline:contains(',')").sparkline('html', { type: 'bar', barWidth: 2, barColor: SPARKLINE_COLOR, disableInteraction: false, tooltipClassname: "sparkline-tooltip" }); //, chartRangeMin: 0, chartRangeMax: _MAX_SPARKLINE_PER_HOUR });
|
|
|
|
$(".tag-input").keyup(function(event) {
|
|
tagInputKeyUp(event);
|
|
}).blur(function(event) {
|
|
tagInputKeyUp(event);
|
|
});
|
|
|
|
try {
|
|
var sparklines = $(".sparkline");
|
|
(function drawSparklines() {
|
|
var hidden = sparklines.filter(".hidden");
|
|
hidden.filter(':lt(10)').removeClass("hidden");
|
|
$.sparkline_display_visible();
|
|
if (hidden.length) {
|
|
DRAW_SPARKLINES_TIMER = setTimeout(drawSparklines, 100);
|
|
}
|
|
}
|
|
)();
|
|
}
|
|
catch(err) {
|
|
}
|
|
},
|
|
fnRowCallback: function(nRow, aData, iDisplayIndex, iDisplayIndexFull) {
|
|
function nslookup(event, ui) {
|
|
var elem = $(this);
|
|
var html = elem.parent().html();
|
|
var match = html.match(/\d+\.\d+\.\d+\.\d+/) || html.match(/[\w:]*:[\w:]*:[\w:]*/);
|
|
|
|
if (match !== null) {
|
|
var ip = match[0];
|
|
|
|
$.ajax("https://stat.ripe.net/data/dns-chain/data.json?resource=" + ip, { dataType:"jsonp", ip: ip})
|
|
.done(function(json) {
|
|
var _ = json.data.reverse_nodes[this.ip];
|
|
if ((typeof _ === "undefined") || (_.length === 0) || (_ === "localhost")) {
|
|
_ = "-";
|
|
}
|
|
_ = String(_);
|
|
if (_.length > 40) {
|
|
_ = _.substring(0, 40) + "<br>" + _.substring(40);
|
|
}
|
|
var msg = "<p><b>" + escapeHtml(_) + "</b></p>";
|
|
ui.tooltip.find(".ui-tooltip-content").html(msg + "please wait...");
|
|
|
|
$.ajax("https://stat.ripe.net/data/whois/data.json?resource=" + ip, { dataType:"jsonp", ip: ip, msg: msg })
|
|
.done(function(json) {
|
|
// Reference: http://bugs.jqueryui.com/ticket/8740#comment:21
|
|
var found = null;
|
|
var msg = "";
|
|
|
|
if (json.data.records !== undefined)
|
|
for (var i = json.data.records.length - 1; i >= 0 ; i--) {
|
|
if ((json.data.records[i][0].key.toLowerCase().indexOf("inetnum") != -1) || (json.data.records[i][0].key.toLowerCase().indexOf("netrange") != -1)){
|
|
found = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found !== null) {
|
|
for (var j = 0; j < json.data.records[found].length; j++) {
|
|
msg += json.data.records[found][j].key + ": " + json.data.records[found][j].value;
|
|
msg += "<br>";
|
|
}
|
|
msg = msg.replace(/(-\+)+/g, "--").replace(/(-\+)+/g, "--");
|
|
}
|
|
|
|
ui.tooltip.find(".ui-tooltip-content").html(this.msg + msg);
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
$('[title]', nRow).tooltip();
|
|
|
|
$.each([DATATABLES_COLUMNS.TRAIL], function(index, value) {
|
|
var cell = $('td:eq(' + value + ')', nRow);
|
|
|
|
if (cell === null)
|
|
return false;
|
|
|
|
var html = cell.html();
|
|
|
|
if (html === null)
|
|
return false;
|
|
|
|
if (html.indexOf('ipcat') > -1)
|
|
return false;
|
|
|
|
var match = html.match(/\d+\.\d+\.\d+\.\d+/);
|
|
if (match === null)
|
|
return false;
|
|
|
|
var interval = null;
|
|
var img = "";
|
|
var ip = match[0];
|
|
|
|
if (!DEMO && !isLocalAddress(ip)) {
|
|
if (!(ip in CHECK_IP)) {
|
|
CHECK_IP[ip] = null;
|
|
$.ajax("/check_ip?address=" + ip, { dataType: "jsonp", ip: ip, cell: cell })
|
|
.done(function(json) {
|
|
var span_ip = $(json.ipcat.length > 0 ? "<span class='ipcat'></span>" : "<span class='ipcat hidden'></span>").html(json.ipcat);
|
|
CHECK_IP[this.ip] = json;
|
|
this.cell.append(span_ip);
|
|
if (json.worst_asns === "true")
|
|
this.cell.append($("<span class='worst_asns' title='malicious ASN'></span>").tooltip());
|
|
});
|
|
}
|
|
else if (CHECK_IP[ip] !== null) {
|
|
var json = CHECK_IP[ip];
|
|
var span_ip = $(json.ipcat.length > 0 ? "<span class='ipcat'></span>" : "<span class='ipcat hidden'></span>").html(json.ipcat);
|
|
cell.append(span_ip);
|
|
if (json.worst_asns === "true")
|
|
cell.append($("<span class='worst_asns' title='malicious ASN'></span>").tooltip());
|
|
}
|
|
else {
|
|
interval = setInterval(function(ip, cell) {
|
|
if (typeof cell === "undefined")
|
|
clearInterval(interval);
|
|
else {
|
|
html = cell.html();
|
|
if (CHECK_IP[ip] !== null) {
|
|
if (html.indexOf("ipcat") === -1) {
|
|
var json = CHECK_IP[ip];
|
|
var span_ip = $(json.ipcat.length > 0 ? "<span class='ipcat'></span>" : "<span class='ipcat hidden'></span>").html(json.ipcat);
|
|
cell.append(span_ip);
|
|
if (json.worst_asns === "true")
|
|
cell.append($("<span class='worst_asns' title='malicious ASN'></span>").tooltip());
|
|
}
|
|
clearInterval(interval);
|
|
}
|
|
}
|
|
}, 500, ip, cell);
|
|
}
|
|
}
|
|
});
|
|
|
|
$.each([DATATABLES_COLUMNS.SRC_IP, DATATABLES_COLUMNS.DST_IP], function(index, value) {
|
|
var cell = $('td:eq(' + value + ')', nRow);
|
|
|
|
if (cell === null)
|
|
return false;
|
|
|
|
var html = cell.html();
|
|
|
|
if (html === null)
|
|
return false;
|
|
|
|
if ((html.indexOf('flag') > -1) || (html.indexOf('lan') > -1) || (html.indexOf(',') > -1) || (html.indexOf('ellipsis') > -1))
|
|
return false;
|
|
|
|
var match = html.match(/\d+\.\d+\.\d+\.\d+/) || html.match(/[\w:]*:[\w:]*:[\w:]*/);
|
|
if (match === null)
|
|
return false;
|
|
|
|
var interval = null;
|
|
var img = "";
|
|
var ip = match[0];
|
|
var options = { content: "please wait...", open: nslookup, position: { my: "left center", at: "right+10 top-50" } };
|
|
|
|
if (!isLocalAddress(ip)) {
|
|
if (!(ip in IP_COUNTRY)) {
|
|
IP_COUNTRY[ip] = null;
|
|
$.ajax("https://stat.ripe.net/data/geoloc/data.json?resource=" + ip, { dataType:"jsonp", ip: ip, html: html, cell: cell })
|
|
.done(function(json) {
|
|
var span_ip = $("<span title=''/>").html(this.html + " ");
|
|
var country = null;
|
|
|
|
try {
|
|
country = json.data.located_resources[0].locations[0].country.toLowerCase().split('-')[0];
|
|
}
|
|
catch(err) {
|
|
try {
|
|
country = json.data.located_resources[0].location.toLowerCase().split('-')[0];
|
|
}
|
|
catch(err) {
|
|
}
|
|
}
|
|
|
|
if ((country !== null) && (country !== "ano")) {
|
|
IP_COUNTRY[this.ip] = country;
|
|
img = '<img src="images/blank.gif" class="flag flag-' + IP_COUNTRY[this.ip] + '" title="' + IP_COUNTRY[this.ip].toUpperCase() + '">';
|
|
}
|
|
else {
|
|
IP_COUNTRY[this.ip] = "unknown";
|
|
img = '<img src="images/blank.gif" class="flag flag-unknown" title="UNKNOWN">';
|
|
}
|
|
|
|
span_ip.tooltip(options);
|
|
this.cell.html("").append(span_ip).append($(img).tooltip());
|
|
});
|
|
}
|
|
else if (IP_COUNTRY[ip] !== null) {
|
|
img = ' <img src="images/blank.gif" class="flag flag-' + IP_COUNTRY[ip] + '" title="' + IP_COUNTRY[ip].toUpperCase() + '">';
|
|
|
|
var span_ip = $("<span title=''/>").html(html + " ");
|
|
span_ip.tooltip(options);
|
|
|
|
if (typeof cell !== "undefined")
|
|
cell.html("").append(span_ip).append($(img).tooltip());
|
|
}
|
|
else {
|
|
interval = setInterval(function(ip, cell) {
|
|
if (typeof cell === "undefined")
|
|
clearInterval(interval);
|
|
else {
|
|
html = cell.html();
|
|
if (typeof html === "undefined")
|
|
clearInterval(interval);
|
|
else if (IP_COUNTRY[ip] !== null) {
|
|
if (html.indexOf("flag-") === -1) {
|
|
img = ' <img src="images/blank.gif" class="flag flag-' + IP_COUNTRY[ip] + '" title="' + IP_COUNTRY[ip].toUpperCase() + '">';
|
|
|
|
var span_ip = $("<span title=''/>").html(html + " ");
|
|
span_ip.tooltip(options);
|
|
|
|
cell.html("").append(span_ip).append($(img).tooltip());
|
|
}
|
|
clearInterval(interval);
|
|
}
|
|
}
|
|
}, 1000, ip, cell);
|
|
}
|
|
}
|
|
else {
|
|
img = '<img src="images/lan.gif" height="11px" style="margin-bottom: 0px" title="LAN">';
|
|
cell.html(html + " ").append($(img).tooltip());
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
$(details).find("thead").addClass("noselect");
|
|
|
|
details.off("mouseenter"); // clear previous
|
|
details.on("mouseenter", ".trail-text", function(event) {
|
|
var cell = event.target;
|
|
if (event.target.classList.contains("trail-text")) {
|
|
clearTimeout(SEARCH_TIP_TIMER);
|
|
SEARCH_TIP_TIMER = setTimeout(function(cell, event) {
|
|
if ((event.buttons === 0) && ($(".ui-tooltip").length === 0)) {
|
|
var query = cell[0].innerHTML.replace(/<span class="ipcat.+span>/g, "").replace(/<[^>]+>/g, "").replace(/[\d.]+ \(([^)]+)\)/, "$1").replace(/[()]/g, "").replace(/^www\./g, "").replace(/:\d+$/, "").replace(/^([^\/]*\.[^\/]*)\/.+/, "$1").trim();
|
|
$(".searchtip").remove();
|
|
$("body").append(
|
|
$('<div class="ui-tooltip searchtip"><div><img src="images/newtab.png" style="cursor: pointer" title="open in new tab"><img src="images/close.png" style="cursor: pointer; width: 16px; height: 16px" title="close"></div><iframe src="' + SEARCH_TIP_URL.replace("${query}", query) + '"></iframe><div>')
|
|
.css('position', 'absolute')
|
|
.show()
|
|
.position({ my: "right+10 top-200", of: event })
|
|
.on("mouseleave", function(){
|
|
$(".searchtip").remove();
|
|
})
|
|
);
|
|
$(".searchtip img[title='open in new tab']").on("click", function() { searchTipToTab(query); });
|
|
$(".searchtip img[title='close']").on("click", function() { $(".searchtip").remove(); });
|
|
}
|
|
}, 2000, $(this), event);
|
|
|
|
$(cell).on("mouseleave", function(mouseenter) {
|
|
clearTimeout(SEARCH_TIP_TIMER);
|
|
});
|
|
}
|
|
});
|
|
|
|
details.off("click", ".trail");
|
|
details.on("click", ".trail", function(event) {
|
|
clearTimeout(SEARCH_TIP_TIMER);
|
|
});
|
|
|
|
details.off("dblclick"); // clear previous
|
|
details.on("dblclick", "td", function (){
|
|
var table = $("#details").dataTable();
|
|
var filter = "";
|
|
|
|
if ($(this).find(".info-input").length > 0)
|
|
filter = $(this).find(".info-input")[0].value;
|
|
else if ($(this).find(".time-day").length > 0)
|
|
filter = $(this).find("div")[0].lastChild.textContent;
|
|
else if ($(this).find(".trail-text").length > 0)
|
|
filter = $(this).find(".trail-text")[0].lastChild.textContent.replace(/\([^)]+\)/g, "");
|
|
else if ($(this).find(".duplicates").length > 0)
|
|
filter = this.innerHTML.replace(/<span.+/g, " ").replace(/<.+?>/g, " ");
|
|
else if (this.innerHTML.indexOf("ellipsis") > -1) {
|
|
match = this.innerHTML.match(/title=["']([^"']+)/);
|
|
if (match !== null)
|
|
filter = match[1].replace(/,\ /g, " ");
|
|
else {
|
|
var tooltip = $(".ui-tooltip");
|
|
if (tooltip.length > 0)
|
|
filter = tooltip.html().replace(/,\ /g, " ");
|
|
}
|
|
}
|
|
else {
|
|
filter = this.innerHTML.replace(/<span class="ipcat">.+?<\/span>/g, "").replace(/<.+?>/g, " ");
|
|
}
|
|
|
|
filter = filter.replace(/\s+/g, " ").trim();
|
|
filter = $('<div/>').html(filter).text();
|
|
appendFilter(filter);
|
|
});
|
|
|
|
details.off("mouseup"); // clear previous
|
|
details.on("mouseup", "td", function (event) {
|
|
if (event.button === 0) { // left mouse button
|
|
if (event.target.classList.contains("tag"))
|
|
appendFilter(event.target.innerHTML, event, true);
|
|
else if (event.target.classList.toString().startsWith("severity"))
|
|
appendFilter(event.target.innerHTML, event);
|
|
else if (event.target.classList.contains("label-type"))
|
|
appendFilter(event.target.innerHTML, event);
|
|
}
|
|
else if (event.button === 1) { // middle mouse button
|
|
if (event.target.classList.contains("tag"))
|
|
appendFilter(event.target.innerHTML, event, true);
|
|
}
|
|
else if (event.button === 2) { // right mouse button/click
|
|
stopPropagation(event);
|
|
}
|
|
});
|
|
|
|
details.on("mouseup", ".event-data", copyEventsToClipboard);
|
|
details.on("mouseup", ".ellipsis", copyEllipsisToClipboard);
|
|
|
|
details.off("contextmenu"); // clear previous
|
|
details.on("contextmenu", "td", function (event) {
|
|
event.preventDefault();
|
|
|
|
$(".ui-tooltip").remove();
|
|
CONTEXT_MENU_ROW = $(event.target).closest("tr");
|
|
|
|
// Show contextmenu
|
|
$(".custom-menu").finish().toggle(100).
|
|
|
|
// In the right position (the mouse)
|
|
css({
|
|
top: event.pageY + "px",
|
|
left: event.pageX + "px"
|
|
});
|
|
});
|
|
}
|
|
|
|
function generateNonce() {
|
|
if (window.crypto && window.crypto.getRandomValues) {
|
|
const array = new Uint32Array(1);
|
|
const result = [];
|
|
for (let i = 0; i < NONCE_LENGTH; i++) {
|
|
crypto.getRandomValues(array);
|
|
result.push(NONCE_ALPHABET.charAt(array[0] % NONCE_ALPHABET.length));
|
|
}
|
|
return result.join('');
|
|
} else {
|
|
let retval = "";
|
|
for(let i = 0; i < NONCE_LENGTH; i++)
|
|
retval += NONCE_ALPHABET.charAt(Math.floor(Math.random() * NONCE_ALPHABET.length));
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
String.prototype.hashCode = function() {
|
|
return murmurhash3_32_gc(this, 13);
|
|
};
|
|
|
|
|
|
// Reference: http://stackoverflow.com/a/1026087
|
|
String.prototype.capitalizeFirstLetter = function() {
|
|
return this.charAt(0).toUpperCase() + this.slice(1);
|
|
};
|
|
|
|
// Reference: http://stackoverflow.com/a/12710609
|
|
Array.prototype.insert = function (index, item) {
|
|
this.splice(index, 0, item);
|
|
};
|
|
|
|
Array.prototype.clean = function(deleteValue) {
|
|
for (var i = 0; i < this.length; i++) {
|
|
if (this[i] === deleteValue) {
|
|
this.splice(i, 1);
|
|
i--;
|
|
}
|
|
}
|
|
return this;
|
|
};
|
|
|
|
if (typeof String.prototype.startsWith !== "function") {
|
|
String.prototype.startsWith = function (str){
|
|
return this.indexOf(str) === 0;
|
|
};
|
|
}
|
|
|
|
if (typeof String.prototype.contains !== "function") {
|
|
String.prototype.contains = function (str){
|
|
return this.indexOf(str) !== -1;
|
|
};
|
|
}
|
|
|
|
// Reference: http://stackoverflow.com/a/2548133
|
|
if (typeof String.prototype.endsWith !== "function") {
|
|
String.prototype.endsWith = function(suffix) {
|
|
return this.indexOf(suffix, this.length - suffix.length) !== -1;
|
|
};
|
|
}
|
|
|
|
// Reference: http://stackoverflow.com/a/6700
|
|
Object.size = function(obj) {
|
|
var size = 0, key;
|
|
for (key in obj) {
|
|
if (obj.hasOwnProperty(key))
|
|
size++;
|
|
}
|
|
return size;
|
|
};
|
|
|
|
jQuery.fn.dataTableExt.afnFiltering.push(
|
|
function(settings, data, dataIndex) {
|
|
return true;
|
|
}
|
|
);
|
|
|
|
jQuery.extend(jQuery.fn.dataTableExt.oSort, {
|
|
// Reference: http://cdn.datatables.net/plug-ins/3cfcc339e89/sorting/date-euro.js
|
|
"date-custom-pre": function ( a ) {
|
|
var x;
|
|
if ( $.trim(a) !== '' ) {
|
|
// extract timestamp from "<div title='yyyy-mm-dd hh:mm:ss.ususus'><span class='time-day'>dd<sup>th</sup></span> hh:mm:ss</div>"
|
|
var frTimestamp = $.trim(a).split("'")[1];
|
|
|
|
var frDatea = frTimestamp.split(' ');
|
|
var frTimea = frDatea[1].split('.')[0].split(':');
|
|
var frUseca = frDatea[1].split('.')[1];
|
|
var frDatea2 = frDatea[0].split('-');
|
|
|
|
x = (frDatea2[0] + frDatea2[1] + frDatea2[2] + frTimea[0] + frTimea[1] + frTimea[2] + frUseca) * 1;
|
|
}
|
|
else {
|
|
x = Infinity;
|
|
}
|
|
return x;
|
|
},
|
|
"date-custom-asc": function ( a, b ) {
|
|
return a - b;
|
|
},
|
|
"date-custom-desc": function ( a, b ) {
|
|
return b - a;
|
|
}
|
|
});
|
|
|
|
jQuery.extend(jQuery.fn.dataTableExt.oSort, {
|
|
// Reference: https://cdn.datatables.net/plug-ins/3cfcc339e89/sorting/ip-address.js
|
|
"ip-address-pre": function (a) {
|
|
return _ipSortingValue(a);
|
|
},
|
|
|
|
"ip-address-asc": function ( a, b ) {
|
|
return ((a < b) ? -1 : ((a > b) ? 1 : 0));
|
|
},
|
|
|
|
"ip-address-desc": function ( a, b ) {
|
|
return ((a < b) ? 1 : ((a > b) ? -1 : 0));
|
|
}
|
|
});
|
|
|
|
jQuery.extend(jQuery.fn.dataTableExt.oSort, {
|
|
"events-pre": function (a) {
|
|
return parseInt(a.replace(/<[^>]+>/g, ""));
|
|
},
|
|
|
|
"events-asc": function ( a, b ) {
|
|
return ((a < b) ? -1 : ((a > b) ? 1 : 0));
|
|
},
|
|
|
|
"events-desc": function ( a, b ) {
|
|
return ((a < b) ? 1 : ((a > b) ? -1 : 0));
|
|
}
|
|
});
|
|
|
|
jQuery.extend(jQuery.fn.dataTableExt.oSort, {
|
|
"severity-pre": function (a) {
|
|
var x = 0;
|
|
var match = a.match(/value=["'](\d+)/);
|
|
|
|
if (match !== null) {
|
|
x = parseInt(match[1]);
|
|
}
|
|
|
|
return x;
|
|
},
|
|
|
|
"severity-asc": function ( a, b ) {
|
|
return ((a < b) ? -1 : ((a > b) ? 1 : 0));
|
|
},
|
|
|
|
"severity-desc": function ( a, b ) {
|
|
return ((a < b) ? 1 : ((a > b) ? -1 : 0));
|
|
}
|
|
});
|
|
|
|
jQuery.extend(jQuery.fn.dataTableExt.oSort, {
|
|
// Reference: https://cdn.datatables.net/plug-ins/3cfcc339e89/sorting/ip-address.js
|
|
"sparkline-pre": function ( a ) {
|
|
return parseInt($(a).attr("value"));
|
|
},
|
|
|
|
"sparkline-asc": function ( a, b ) {
|
|
return ((a < b) ? -1 : ((a > b) ? 1 : 0));
|
|
},
|
|
|
|
"sparkline-desc": function ( a, b ) {
|
|
return ((a < b) ? 1 : ((a > b) ? -1 : 0));
|
|
}
|
|
});
|
|
|
|
jQuery.extend(jQuery.fn.dataTableExt.oSort, {
|
|
// Reference: https://cdn.datatables.net/plug-ins/3cfcc339e89/sorting/ip-address.js
|
|
"port-pre": function ( a ) {
|
|
var x = 0;
|
|
var match = a.match(/\d+/);
|
|
|
|
if (match !== null)
|
|
x = parseInt(match[0]);
|
|
|
|
return x;
|
|
},
|
|
|
|
"port-asc": function ( a, b ) {
|
|
return ((a < b) ? -1 : ((a > b) ? 1 : 0));
|
|
},
|
|
|
|
"port-desc": function ( a, b ) {
|
|
return ((a < b) ? 1 : ((a > b) ? -1 : 0));
|
|
}
|
|
});
|
|
|
|
jQuery.extend(jQuery.fn.dataTableExt.oSort, {
|
|
"threat-pre": function ( a ) {
|
|
var x = "";
|
|
var match = a.match(/([0-9a-fA-F]{8})/);
|
|
|
|
if (match !== null)
|
|
//x = parseInt(match[1], 16);
|
|
x = match[1];
|
|
|
|
return x;
|
|
},
|
|
|
|
"threat-asc": function ( a, b ) {
|
|
return ((a < b) ? -1 : ((a > b) ? 1 : 0));
|
|
},
|
|
|
|
"threat-desc": function ( a, b ) {
|
|
return ((a < b) ? 1 : ((a > b) ? -1 : 0));
|
|
}
|
|
});
|
|
|
|
function setChartScale(options, maxValue) {
|
|
if (maxValue > 0) {
|
|
stepValue = Math.pow(10, Math.floor(Math.log(maxValue) / Math.LN10));
|
|
options.scaleOverride = true;
|
|
options.scaleStartValue = 0;
|
|
while ((stepValue > 0) && (stepValue === Math.floor(stepValue))) {
|
|
options.scaleStepWidth = stepValue;
|
|
options.scaleSteps = Math.ceil(maxValue / stepValue);
|
|
if ((options.scaleSteps >= 5) || (stepValue === 1))
|
|
break;
|
|
else
|
|
stepValue = stepValue / 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
function drawInfo(type) {
|
|
//var color = $("#" + type.toLowerCase() + "_count").parent()[0].style["background-color"];
|
|
//$("li.status-button").css("opacity", "0.6");
|
|
$("#chart_area").empty();
|
|
|
|
if (jQuery.isEmptyObject(_HOURS))
|
|
return;
|
|
|
|
if ($("#" + type.toLowerCase() + "_count").parent()[0].style.border.contains("white")) {
|
|
resetStatusButtons();
|
|
return;
|
|
}
|
|
|
|
resetStatusButtons();
|
|
|
|
$("#" + type.toLowerCase() + "_count").parent().css("text-shadow", "none");
|
|
$("#" + type.toLowerCase() + "_count").parent().css("border", "3px solid white");
|
|
|
|
$("#graph_close").removeClass("hidden").show();
|
|
|
|
$.jStorage.set(STORAGE_KEY_ACTIVE_STATUS_BUTTON, type);
|
|
|
|
if (type === "Events") {
|
|
var ticks = {};
|
|
var labels = [];
|
|
var first = true;
|
|
var datasets = [];
|
|
var total_days = Math.round(Object.size(_HOURS) / 24);
|
|
|
|
for (var type in TRAIL_TYPES) {
|
|
var _ = [];
|
|
|
|
ticks[type] = [];
|
|
|
|
for (var hour in _HOURS)
|
|
_.push([hour >>> 0, _HOURS[hour][type]]);
|
|
|
|
_.sort(function(a, b) {
|
|
a = a[0];
|
|
b = b[0];
|
|
|
|
return a < b ? -1 : (a > b ? 1 : 0);
|
|
});
|
|
|
|
for (var i = 0; i < _.length; i++) {
|
|
var date = new Date(_[i][0] * 60 * 60 * 1000);
|
|
if (first) {
|
|
if (i % total_days === 0) {
|
|
var label = "";
|
|
if (total_days > 2) {
|
|
label += pad(date.getFullYear(), 4) + "-" + pad(date.getMonth(), 2) + "-" + pad(date.getDate(), 2) + " ";
|
|
}
|
|
label += pad(date.getHours(), 2) + "h";
|
|
labels.push(label);
|
|
}
|
|
else
|
|
labels.push("");
|
|
}
|
|
|
|
ticks[type].push(_[i][1] | 0);
|
|
}
|
|
|
|
for (var i = ticks[type].length - 1; i >= 0; i--) {
|
|
if (ticks[type][i] === 0)
|
|
ticks[type][i] = null;
|
|
else
|
|
break;
|
|
}
|
|
|
|
first = false;
|
|
|
|
datasets.push(
|
|
{
|
|
label: type,
|
|
strokeColor: TRAIL_TYPES[type],
|
|
pointColor: TRAIL_TYPES[type],
|
|
pointHighlightFill: "#fff",
|
|
data: ticks[type]
|
|
}
|
|
);
|
|
}
|
|
|
|
var data = {
|
|
labels: labels,
|
|
datasets: datasets
|
|
};
|
|
var options = {
|
|
//scaleGridLineColor: "#abb2b4",
|
|
scaleShowVerticalLines: false,
|
|
scaleShowHorizontalLines: false, // because StackedBar doesn't show them properly
|
|
datasetFill: false,
|
|
bezierCurve: false,
|
|
pointDotRadius: Math.max(1, 5 - total_days),
|
|
//scaleShowGridLines: false
|
|
//tooltipTemplate: "<%if (label){%><%=label.replace(/[^0-9]/, '')%>:00h-<%=label.replace(/[^0-9]/, '')%>:59h: <%}%><%= value %> events",
|
|
tooltipTemplate: CHART_TOOLTIP_FORMAT,
|
|
multiTooltipTemplate: CHART_TOOLTIP_FORMAT, // Reference: http://stackoverflow.com/a/24622442
|
|
pointHitDetectionRadius: 5
|
|
};
|
|
|
|
//setChartScale(options, _MAX_EVENTS_PER_HOUR);
|
|
|
|
var ctx = $('<canvas id="chart_canvas" width="' + CHART_WIDTH + '" height="' + CHART_HEIGHT + '"></canvas>').appendTo('#chart_area')[0].getContext("2d");
|
|
var chart = new Chart(ctx);
|
|
var line = chart.Line(data, options);
|
|
|
|
chart.canvas.onclick = function(evt) {
|
|
var activePoints = line.getPointsAtEvent(evt);
|
|
|
|
if (activePoints.length > 0) {
|
|
var filter = '" ' + activePoints[0].label.replace(/[^0-9]/g, "") + ':"';
|
|
var table = $("#details").dataTable();
|
|
|
|
table.fnFilter(filter);
|
|
scrollTo("#details");
|
|
}
|
|
};
|
|
}
|
|
else if (type === "Trails") {
|
|
var data = [];
|
|
var other = 0;
|
|
var _ = {};
|
|
|
|
for (var type in TRAIL_TYPES)
|
|
_[type] = [];
|
|
|
|
var threshold = 0;
|
|
for (var i = 0; i < _TRAILS_SORTED.length; i++)
|
|
threshold += _TRAILS_SORTED[i][1];
|
|
|
|
threshold = threshold / 100;
|
|
|
|
for (var i = 0; i < _TRAILS_SORTED.length; i++) {
|
|
var item = _TRAILS_SORTED[i];
|
|
|
|
if (item[1] >= threshold) {
|
|
var match = item[0].match(/(.*?)\ \(([^)]+)\)/);
|
|
var type = match[2];
|
|
var count = item[1];
|
|
|
|
data.push({value: count, label: item[0], color: type in TRAIL_TYPES ? TRAIL_TYPES[type] : getHashColor(type)});
|
|
}
|
|
else
|
|
other += item[1];
|
|
}
|
|
|
|
if (other > 0)
|
|
data.push({ value: other, label: "Other (<1%)", color: OTHER_COLOR });
|
|
|
|
var pie = new d3pie("chart_area", {
|
|
header: {
|
|
title: {
|
|
fontSize: 1,
|
|
font: DEFAULT_FONT_FAMILY
|
|
},
|
|
subtitle: {
|
|
color: "#999999",
|
|
fontSize: 1,
|
|
font: DEFAULT_FONT_FAMILY
|
|
},
|
|
titleSubtitlePadding: 0
|
|
},
|
|
footer: {
|
|
color: "#999999",
|
|
fontSize: 1,
|
|
font: DEFAULT_FONT_FAMILY,
|
|
location: "bottom-left"
|
|
},
|
|
size: {
|
|
canvasHeight: CHART_HEIGHT,
|
|
canvasWidth: CHART_WIDTH
|
|
},
|
|
data: {
|
|
content: data
|
|
},
|
|
labels: {
|
|
inner: {
|
|
hideWhenLessThanPercentage: 101
|
|
},
|
|
mainLabel: {
|
|
font: DEFAULT_FONT_FAMILY,
|
|
fontSize: PIE_FONT_SIZE
|
|
},
|
|
percentage: {
|
|
color: "#ffffff",
|
|
font: DEFAULT_FONT_FAMILY,
|
|
decimalPlaces: 0
|
|
},
|
|
value: {
|
|
color: "#adadad",
|
|
font: DEFAULT_FONT_FAMILY,
|
|
fontSize: PIE_FONT_SIZE
|
|
},
|
|
lines: {
|
|
enabled: true
|
|
}
|
|
},
|
|
tooltips: {
|
|
enabled: true,
|
|
type: "placeholder",
|
|
string: "{label}: {value}, {percentage}%",
|
|
styles: {
|
|
font: DEFAULT_FONT_FAMILY
|
|
}
|
|
},
|
|
effects: {
|
|
load: {
|
|
speed: 500
|
|
},
|
|
pullOutSegmentOnClick: {
|
|
effect: "none"
|
|
}
|
|
},
|
|
misc: {
|
|
gradient: {
|
|
enabled: true,
|
|
percentage: 100
|
|
}
|
|
},
|
|
callbacks: {
|
|
onClickSegment: function(a) {
|
|
var filter = a.data.label.replace(/\(([A-Z]+)\)/g, "$1");
|
|
|
|
if (!filter.startsWith("Other")) {
|
|
var table = $("#details").dataTable();
|
|
|
|
table.fnFilter(filter);
|
|
scrollTo("#details");
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
else if (type === "Sources") {
|
|
var labels = [];
|
|
var values = [];
|
|
var maxValue = 0;
|
|
var datasets = {};
|
|
var options = {
|
|
scaleShowVerticalLines: false,
|
|
scaleShowHorizontalLines: false,
|
|
tooltipTemplate: CHART_TOOLTIP_FORMAT,
|
|
multiTooltipTemplate: CHART_TOOLTIP_FORMAT // Reference: http://stackoverflow.com/a/24622442
|
|
};
|
|
|
|
for (var key in TRAIL_TYPES)
|
|
datasets[key] = { fillColor: TRAIL_TYPES[key], label: key, data: [] };
|
|
|
|
for (var i = 0; i < _TOP_SOURCES.length; i++) {
|
|
labels.push(_TOP_SOURCES[i][0]);
|
|
maxValue = Math.max(_TOP_SOURCES[i][1], maxValue);
|
|
|
|
for (var key in TRAIL_TYPES)
|
|
datasets[key].data.push(_SOURCE_EVENTS[_TOP_SOURCES[i][0]][key]);
|
|
}
|
|
|
|
_ = [];
|
|
for (var key in TRAIL_TYPES) {
|
|
_.insert(0, datasets[key]); // StackedBar is drawing from last to first (for some strange reason)
|
|
}
|
|
|
|
var data = {
|
|
labels: labels,
|
|
datasets: _
|
|
};
|
|
|
|
var ctx = $('<canvas id="chart_canvas" width="' + CHART_WIDTH + '" height="' + CHART_HEIGHT + '"></canvas>').appendTo('#chart_area')[0].getContext("2d");
|
|
var chart = new Chart(ctx);
|
|
var bar = chart.StackedBar(data, options);
|
|
|
|
chart.canvas.onclick = function(evt) {
|
|
var activeBars = bar.getBarsAtEvent(evt);
|
|
|
|
if (activeBars.length > 0) {
|
|
var filter = activeBars[0].label;
|
|
|
|
if (filter.toLowerCase() != "other") {
|
|
var table = $("#details").dataTable();
|
|
|
|
table.fnFilter(filter);
|
|
scrollTo("#details");
|
|
}
|
|
}
|
|
};
|
|
}
|
|
else if (type === "Severity") {
|
|
var data = [];
|
|
|
|
for (var severity in _SEVERITY_COUNT) {
|
|
var count = _SEVERITY_COUNT[severity];
|
|
var color = SEVERITY_COLORS[severity];
|
|
var label = "";
|
|
for (var key in SEVERITY)
|
|
if (SEVERITY[key] == severity)
|
|
label = key.toLowerCase();
|
|
data.push({value: count, label: label, color: color})
|
|
}
|
|
|
|
var pie = new d3pie("chart_area", {
|
|
"header": {
|
|
"title": {
|
|
"fontSize": 1,
|
|
"font": DEFAULT_FONT_FAMILY
|
|
},
|
|
"subtitle": {
|
|
"color": "#999999",
|
|
"fontSize": 1,
|
|
"font": DEFAULT_FONT_FAMILY
|
|
},
|
|
"titleSubtitlePadding": 0
|
|
},
|
|
"footer": {
|
|
"color": "#999999",
|
|
"fontSize": 1,
|
|
"font": DEFAULT_FONT_FAMILY,
|
|
"location": "bottom-left"
|
|
},
|
|
"size": {
|
|
"canvasHeight": CHART_HEIGHT,
|
|
"canvasWidth": CHART_WIDTH
|
|
},
|
|
"data": {
|
|
"content": data
|
|
},
|
|
"labels": {
|
|
"inner": {
|
|
"hideWhenLessThanPercentage": 101
|
|
},
|
|
"mainLabel": {
|
|
"font": DEFAULT_FONT_FAMILY,
|
|
"fontSize": PIE_FONT_SIZE
|
|
},
|
|
"percentage": {
|
|
"color": "#ffffff",
|
|
"font": DEFAULT_FONT_FAMILY,
|
|
"decimalPlaces": 0
|
|
},
|
|
"value": {
|
|
"color": "#adadad",
|
|
"font": DEFAULT_FONT_FAMILY,
|
|
"fontSize": PIE_FONT_SIZE
|
|
},
|
|
"lines": {
|
|
"enabled": true
|
|
}
|
|
},
|
|
"tooltips": {
|
|
"enabled": true,
|
|
"type": "placeholder",
|
|
"string": "{label}: {value}, {percentage}%",
|
|
"styles": {
|
|
"font": DEFAULT_FONT_FAMILY
|
|
}
|
|
},
|
|
"effects": {
|
|
"load": {
|
|
"speed": 500
|
|
},
|
|
"pullOutSegmentOnClick": {
|
|
"effect": "none"
|
|
}
|
|
},
|
|
"misc": {
|
|
"gradient": {
|
|
"enabled": true,
|
|
"percentage": 100
|
|
}
|
|
},
|
|
"callbacks": {
|
|
onClickSegment: function(a) {
|
|
var filter = a.data.label;
|
|
var table = $("#details").dataTable();
|
|
|
|
table.fnFilter(filter);
|
|
scrollTo("#details");
|
|
}
|
|
}
|
|
});
|
|
}
|
|
else if (type === "Threats") {
|
|
var data = [];
|
|
var threshold = 0;
|
|
for (var i = 0; i < _THREATS_SORTED.length; i++)
|
|
threshold += _THREATS_SORTED[i][1];
|
|
|
|
threshold = threshold / 100;
|
|
|
|
var other = 0;
|
|
for (var i = 0; i < _THREATS_SORTED.length; i++) {
|
|
var item = _THREATS_SORTED[i];
|
|
|
|
if (item[1] >= threshold) {
|
|
//var threat = item[0];
|
|
var threat = item[0].replace(/\ \([^)]+\)/g, "");
|
|
var count = item[1];
|
|
var color = "#" + threat.substr(0, 6);
|
|
|
|
data.push({value: count, label: threat, color: color})
|
|
}
|
|
else
|
|
other += item[1];
|
|
}
|
|
|
|
if (other > 0)
|
|
data.push({value: other, label: "Other (<1%)", color: OTHER_COLOR})
|
|
|
|
var pie = new d3pie("chart_area", {
|
|
"header": {
|
|
"title": {
|
|
"fontSize": 1,
|
|
"font": DEFAULT_FONT_FAMILY
|
|
},
|
|
"subtitle": {
|
|
"color": "#999999",
|
|
"fontSize": 1,
|
|
"font": DEFAULT_FONT_FAMILY
|
|
},
|
|
"titleSubtitlePadding": 0
|
|
},
|
|
"footer": {
|
|
"color": "#999999",
|
|
"fontSize": 1,
|
|
"font": DEFAULT_FONT_FAMILY,
|
|
"location": "bottom-left"
|
|
},
|
|
"size": {
|
|
"canvasHeight": CHART_HEIGHT,
|
|
"canvasWidth": CHART_WIDTH
|
|
},
|
|
"data": {
|
|
"content": data
|
|
},
|
|
"labels": {
|
|
"inner": {
|
|
"hideWhenLessThanPercentage": 101
|
|
},
|
|
"mainLabel": {
|
|
"font": DEFAULT_FONT_FAMILY,
|
|
"fontSize": PIE_FONT_SIZE
|
|
},
|
|
"percentage": {
|
|
"color": "#ffffff",
|
|
"font": DEFAULT_FONT_FAMILY,
|
|
"decimalPlaces": 0
|
|
},
|
|
"value": {
|
|
"color": "#adadad",
|
|
"font": DEFAULT_FONT_FAMILY,
|
|
"fontSize": PIE_FONT_SIZE
|
|
},
|
|
"lines": {
|
|
"enabled": true
|
|
}
|
|
},
|
|
"tooltips": {
|
|
"enabled": true,
|
|
"type": "placeholder",
|
|
"string": "{label}: {value}, {percentage}%",
|
|
"styles": {
|
|
"font": DEFAULT_FONT_FAMILY
|
|
}
|
|
},
|
|
"effects": {
|
|
"load": {
|
|
"speed": 500
|
|
},
|
|
"pullOutSegmentOnClick": {
|
|
"effect": "none"
|
|
}
|
|
},
|
|
"misc": {
|
|
"gradient": {
|
|
"enabled": true,
|
|
"percentage": 100
|
|
}
|
|
},
|
|
"callbacks": {
|
|
onClickSegment: function(a) {
|
|
var filter = a.data.label.substr(0, 8);
|
|
|
|
if (!filter.startsWith("Other")) {
|
|
var table = $("#details").dataTable();
|
|
|
|
table.fnFilter(filter);
|
|
scrollTo("#details");
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function initVisual() {
|
|
var sparklines = {}
|
|
var min_ = null;
|
|
var max_ = null;
|
|
var sliceColors = [];
|
|
var total = {};
|
|
var data = [];
|
|
var other = 0;
|
|
var severityMax = null;
|
|
var severityMaxName = null;
|
|
var severitySum = 0;
|
|
|
|
_MAX_EVENTS_PER_HOUR = 0;
|
|
_TRAILS_SORTED = _sort(_TRAILS);
|
|
|
|
for (var type in TRAIL_TYPES) {
|
|
sparklines[type] = [];
|
|
total[type] = 0;
|
|
}
|
|
|
|
// Severity sparkline
|
|
for (var severity in _SEVERITY_COUNT) {
|
|
if (_SEVERITY_COUNT[severity] * severity > severityMax) {
|
|
severityMax = _SEVERITY_COUNT[severity] * severity;
|
|
for (var key in SEVERITY)
|
|
if (SEVERITY[key] == severity)
|
|
severityMaxName = key.toLowerCase();
|
|
}
|
|
severitySum += _SEVERITY_COUNT[severity];
|
|
data.push(_SEVERITY_COUNT[severity]);
|
|
sliceColors.push(SEVERITY_COLORS[severity])
|
|
}
|
|
|
|
if (severitySum === 0) {
|
|
data = [1];
|
|
sliceColors = [OTHER_COLOR];
|
|
}
|
|
|
|
total["Severity"] = severityMaxName;
|
|
|
|
var options = { type: 'pie', sliceColors: sliceColors, minSpotColor: "", maxSpotColor: "", spotColor: "", highlightSpotColor: "", highlightLineColor: "", tooltipClassname: "", width: '30', height: '30', offset: -90, disableInteraction: true };
|
|
|
|
$("#severity_sparkline").sparkline(data, options);
|
|
|
|
|
|
// Trails sparkline
|
|
var threshold = 0;
|
|
for (var i = 0; i < _TRAILS_SORTED.length; i++)
|
|
threshold += _TRAILS_SORTED[i][1];
|
|
|
|
threshold = threshold / 100;
|
|
options.sliceColors = [];
|
|
data = [];
|
|
|
|
for (var i = 0; i < _TRAILS_SORTED.length; i++) {
|
|
if (_TRAILS_SORTED[i][1] >= threshold) {
|
|
var type = _TRAILS_SORTED[i][0].match(/\(([A-Z]+)\)/)[1];
|
|
data.push(_TRAILS_SORTED[i][1]);
|
|
options.sliceColors.push(type in TRAIL_TYPES ? TRAIL_TYPES[type] : getHashColor(type));
|
|
}
|
|
else
|
|
other += _TRAILS_SORTED[i][1];
|
|
}
|
|
|
|
if (other > 0) {
|
|
data.push(other);
|
|
options.sliceColors.push(OTHER_COLOR);
|
|
}
|
|
|
|
if (data.length === 0) {
|
|
data.push(1);
|
|
options.sliceColors.push(OTHER_COLOR);
|
|
}
|
|
|
|
total["Trails"] = _TRAILS_SORTED.length;
|
|
|
|
$("#trails_sparkline").sparkline(data, options);
|
|
|
|
// Threats sparkline
|
|
var _ = {};
|
|
for (var threat in _THREATS) {
|
|
var threatUID = getThreatUID(threat);
|
|
var count = _THREATS[threat][0];
|
|
var type = _THREATS[threat][4][LOG_COLUMNS.TYPE];
|
|
total[type] += count;
|
|
_[threatUID] = count;
|
|
}
|
|
|
|
_THREATS_SORTED = _sort(_);
|
|
var other = 0;
|
|
|
|
data = [];
|
|
options.sliceColors = [];
|
|
|
|
var threshold = 0;
|
|
for (var i = 0; i < _THREATS_SORTED.length; i++)
|
|
threshold += _THREATS_SORTED[i][1];
|
|
|
|
threshold = threshold / 100;
|
|
|
|
for (var i = 0; i < _THREATS_SORTED.length; i++) {
|
|
if (_THREATS_SORTED[i][1] >= threshold) {
|
|
data.push(_THREATS_SORTED[i][1]);
|
|
options.sliceColors.push("#" + _THREATS_SORTED[i][0].substr(0, 6));
|
|
}
|
|
else
|
|
other += _THREATS_SORTED[i][1];
|
|
}
|
|
|
|
if (other > 0) {
|
|
data.push(other);
|
|
options.sliceColors.push(OTHER_COLOR);
|
|
}
|
|
|
|
if (data.length === 0) {
|
|
data.push(1);
|
|
options.sliceColors.push(OTHER_COLOR);
|
|
}
|
|
|
|
total["Threats"] = _THREATS_SORTED.length;
|
|
$("#threats_sparkline").sparkline(data, options);
|
|
|
|
// Events sparklines
|
|
for (var hour in _HOURS) {
|
|
if (min_ === null)
|
|
min_ = hour;
|
|
else
|
|
min_ = Math.min(min_, hour);
|
|
|
|
if (max_ === null)
|
|
max_ = hour;
|
|
else
|
|
max_ = Math.max(max_, hour);
|
|
}
|
|
|
|
if ((min_ !== null) && (max_ !== null)) {
|
|
var ms = 60 * 60 * 1000;
|
|
min_ = dayStart(min_ * ms) / ms;
|
|
max_ = dayEnd(max_ * ms) / ms;
|
|
|
|
for (var hour = min_; hour <= max_; hour++) {
|
|
if (!(hour in _HOURS)) {
|
|
for (var key in sparklines)
|
|
sparklines[key].push(0);
|
|
|
|
_HOURS[hour] = {};
|
|
for (var type in TRAIL_TYPES)
|
|
_HOURS[hour][type] = 0;
|
|
}
|
|
else
|
|
for (var key in sparklines) {
|
|
_MAX_EVENTS_PER_HOUR = Math.max(_MAX_EVENTS_PER_HOUR, _HOURS[hour][key] | 0);
|
|
sparklines[key].push(_HOURS[hour][key] | 0);
|
|
}
|
|
}
|
|
|
|
for (var key in sparklines) {
|
|
for (var i = sparklines[key].length - 1; i >= 0; i--) {
|
|
if (sparklines[key][i] === 0)
|
|
sparklines[key][i] = null;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for (var key in sparklines) {
|
|
sparklines[key].push(0);
|
|
sparklines[key].push(0);
|
|
}
|
|
}
|
|
|
|
// Sources sparkline
|
|
_ = [];
|
|
_TOP_SOURCES = [];
|
|
total["Sources"] = Object.size(_SOURCES);
|
|
|
|
sorted = _sort(_SOURCES);
|
|
other = 0;
|
|
|
|
var top = {};
|
|
var ips = [];
|
|
var zero = [];
|
|
other_events = {};
|
|
|
|
for (var key in TRAIL_TYPES) {
|
|
zero.push(0);
|
|
other_events[key] = 0;
|
|
}
|
|
|
|
for (var i = 0; i < sorted.length; i++) {
|
|
if (i < MAX_SOURCES_ITEMS) {
|
|
top[sorted[i][0]] = sorted[i][1];
|
|
ips.push(sorted[i][0]);
|
|
}
|
|
else {
|
|
for (var key in TRAIL_TYPES)
|
|
other_events[key] += _SOURCE_EVENTS[sorted[i][0]][key];
|
|
other += sorted[i][1];
|
|
}
|
|
}
|
|
|
|
ips.sort(_ipCompareValues);
|
|
_ = [zero];
|
|
|
|
for (var i = 0; i < ips.length; i++) {
|
|
var _type_counts = [];
|
|
|
|
for (var key in TRAIL_TYPES)
|
|
_type_counts.push(_SOURCE_EVENTS[ips[i]][key] | 0);
|
|
|
|
_.push(_type_counts);
|
|
_TOP_SOURCES.push([ips[i], _type_counts]);
|
|
}
|
|
|
|
if (other > 0) {
|
|
var _other_counts = [];
|
|
|
|
for (var key in TRAIL_TYPES)
|
|
_other_counts.push(other_events[key] | 0);
|
|
|
|
_.push(_other_counts);
|
|
_TOP_SOURCES.push(["Other", other]);
|
|
_SOURCE_EVENTS["Other"] = other_events;
|
|
}
|
|
|
|
_.push(zero); // because of 0 value display problem
|
|
|
|
var barWidth = Math.min(6, Math.max(2, Math.floor((SPARKLINE_WIDTH / (_.length + 2)) - 1)));
|
|
|
|
var barColors = [];
|
|
for (var key in TRAIL_TYPES)
|
|
barColors.push(TRAIL_TYPES[key]);
|
|
|
|
$('#sources_sparkline').sparkline(_, { width: SPARKLINE_WIDTH, height: '30', type: 'bar', barColor: '#ffffff', barWidth: barWidth, disableInteraction: true, zeroColor: "rgba(0, 0, 0, 0)", stackedBarColor: barColors});
|
|
|
|
var options = { fillColor: false, minSpotColor: "", maxSpotColor: "", spotColor: "", highlightSpotColor: "", highlightLineColor: "", tooltipClassname: "", chartRangeMin: 0, chartRangeMax: _MAX_EVENTS_PER_HOUR, width: SPARKLINE_WIDTH, height: '30', lineWidth: 2 };
|
|
|
|
total["Events"] = 0;
|
|
|
|
var found = false;
|
|
for (var key in total) {
|
|
if (key.toUpperCase() === key) {
|
|
options.lineColor = TRAIL_TYPES[key];
|
|
$('#events_sparkline').sparkline(sparklines[key], options);
|
|
options.composite = true;
|
|
total["Events"] += total[key];
|
|
found = true;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
$('#events_sparkline').sparkline([], options);
|
|
}
|
|
else {
|
|
options.lineColor = "rgba(255, 255, 255, 0.25)";
|
|
options.lineWidth = 1;
|
|
$('#events_sparkline').sparkline([0, 0], options);
|
|
}
|
|
|
|
sum = 0;
|
|
$("[id$=_count]").each(function() {
|
|
sum += parseInt(total[$(this).attr("id").replace(/_count/, "").capitalizeFirstLetter()]) | 0;
|
|
});
|
|
|
|
for (var key in total)
|
|
$("#" + key.toLowerCase() + "_count").html((sum > 0) ? numberWithCommas(total[key]) : '-')
|
|
}
|
|
|
|
function timestamp(str){
|
|
return new Date(str).getTime();
|
|
}
|
|
|
|
function pad(n, width, z) {
|
|
z = z || '0';
|
|
n = n + '';
|
|
|
|
return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
|
|
}
|
|
|
|
function formatDate(value) {
|
|
return value.getFullYear() + "-" + pad(value.getMonth() + 1, 2) + "-" + pad(value.getDate(), 2);
|
|
};
|
|
|
|
function parseDate(value) {
|
|
var retval = new Date(0);
|
|
var match = value.match(/(\d{4})-(\d{2})-(\d{2})/);
|
|
|
|
if (match !== null)
|
|
retval = new Date(match[1], match[2] - 1, match[3]);
|
|
|
|
return retval;
|
|
};
|
|
|
|
function getParameterByName(name) {
|
|
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
|
|
|
|
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
|
|
results = regex.exec(location.search);
|
|
|
|
return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
|
|
}
|
|
|
|
function dayStart(tick_seconds) {
|
|
var value = new Date(tick_seconds);
|
|
|
|
value.setHours(0);
|
|
value.setMinutes(0);
|
|
value.setSeconds(0);
|
|
value.setMilliseconds(0);
|
|
|
|
return Math.floor(value.getTime());
|
|
}
|
|
|
|
function dayEnd(tick_seconds) {
|
|
var value = new Date(tick_seconds);
|
|
|
|
value.setHours(23);
|
|
value.setMinutes(59);
|
|
value.setSeconds(59);
|
|
value.setMilliseconds(999);
|
|
|
|
return Math.floor(value.getTime());
|
|
}
|
|
|
|
$(document).keyup(function(e){
|
|
var key = e.which || e.keyCode;
|
|
|
|
if (key === 37) { // left
|
|
$("#details_previous").click();
|
|
}
|
|
else if (key === 39) { // right
|
|
$("#details_next").click();
|
|
}
|
|
});
|
|
|
|
$(document).ready(function() {
|
|
var from = dayStart(new Date().getTime());
|
|
var to = dayStart(new Date().getTime());
|
|
|
|
if (getParameterByName("from"))
|
|
from = dayStart(parseDate(getParameterByName("from")));
|
|
if (getParameterByName("to"))
|
|
to = dayStart(parseDate(getParameterByName("to")));
|
|
});
|
|
|
|
function query(date1, date2) {
|
|
if (date2 === undefined) {
|
|
var url = location.origin + "/events?date=" + formatDate(date1);
|
|
|
|
init(url, date1);
|
|
}
|
|
else {
|
|
var d1, d2;
|
|
if (date1 < date2) {
|
|
d1 = date1; d2 = date2;
|
|
}
|
|
else {
|
|
d1 = date2; d2 = date1;
|
|
}
|
|
var url = location.origin + "/events?date=" + formatDate(d1) + "_" + formatDate(d2);
|
|
|
|
init(url, d1, d2);
|
|
}
|
|
}
|
|
|