2 typeof define === "function" ? function (m) { define("kismet-ui-dot11-js", m); } :
3 typeof exports === "object" ? function (m) { module.exports = m(); } :
4 function(m){ this.kismet_ui_alerts = m(); }
11 var local_uri_prefix = "";
12 if (typeof(KISMET_URI_PREFIX) !== 'undefined')
13 local_uri_prefix = KISMET_URI_PREFIX;
15 exports.load_complete = 0;
17 function severity_to_string(sev) {
34 function severity_to_color(sev) {
35 if (kismet_theme.theme === 'dark') {
38 return ["#015761", "#FFFFFF"];
40 return ["#5f6100", "#FFFFFF"];
42 return ["#706500", "#FFFFFF"];
44 return ["#B9770E", "#FFFFFF"];
46 return ["#5c010a", "#FFFFFF"];
48 return ["UNKNOWN", "#FFFFFF"];
53 return ["#03e3fc", "#000000"];
55 return ["#fbff00", "#000000"];
57 return ["#fce303", "#000000"];
59 return ["#fcba03", "#000000"];
61 return ["#fc031c", "#000000"];
63 return ["UNKNOWN", "#000000"];
71 var alert_status_element;
72 var AlertColumns = new Array();
74 exports.AddAlertColumn = function(id, options) {
77 sTitle: options.sTitle,
82 if ('field' in options) {
83 coldef.field = options.field;
86 if ('fields' in options) {
87 coldef.fields = options.fields;
90 if ('description' in options) {
91 coldef.description = options.description;
94 if ('name' in options) {
95 coldef.name = options.name;
98 if ('orderable' in options) {
99 coldef.bSortable = options.orderable;
102 if ('visible' in options) {
103 coldef.bVisible = options.visible;
105 coldef.bVisible = true;
108 if ('selectable' in options) {
109 coldef.user_selectable = options.selectable;
111 coldef.user_selectable = true;
114 if ('searchable' in options) {
115 coldef.bSearchable = options.searchable;
118 if ('width' in options) {
119 coldef.width = options.width;
123 if (typeof(coldef.field) === 'string') {
124 var fs = coldef.field.split('/');
125 f = fs[fs.length - 1];
126 } else if (Array.isArray(coldef.field)) {
130 coldef.mData = function(row, type, set) {
131 return kismet.ObjectByString(row, f);
134 if ('renderfunc' in options) {
135 coldef.mRender = options.renderfunc;
138 if ('drawfunc' in options) {
139 coldef.kismetdrawfunc = options.drawfunc;
142 AlertColumns.push(coldef);
145 exports.GetAlertColumns = function(showall = false) {
146 var ret = new Array();
148 var order = kismet.getStorage('kismet.alerttable.columns', []);
150 if (order.length == 0) {
151 // sort invisible columns to the end
152 for (var i in AlertColumns) {
153 if (!AlertColumns[i].bVisible)
155 ret.push(AlertColumns[i]);
158 for (var i in AlertColumns) {
159 if (AlertColumns[i].bVisible)
161 ret.push(AlertColumns[i]);
167 for (var oi in order) {
173 var sc = AlertColumns.find(function(e, i, a) {
174 if (e.kismetId === o.id)
179 if (sc != undefined && sc.user_selectable) {
185 // Fallback if no columns were selected somehow
186 if (ret.length == 0) {
187 // sort invisible columns to the end
188 for (var i in AlertColumns) {
189 if (!AlertColumns[i].bVisible)
191 ret.push(AlertColumns[i]);
194 for (var i in AlertColumns) {
195 if (AlertColumns[i].bVisible)
197 ret.push(AlertColumns[i]);
204 for (var sci in AlertColumns) {
205 var sc = AlertColumns[sci];
207 var rc = ret.find(function(e, i, a) {
208 if (e.kismetId === sc.kismetId)
213 if (rc == undefined) {
222 for (var sci in AlertColumns) {
223 if (!AlertColumns[sci].user_selectable) {
224 ret.push(AlertColumns[sci]);
231 exports.GetAlertColumnMap = function(columns) {
234 for (var ci in columns) {
235 var fields = new Array();
237 if ('field' in columns[ci])
238 fields.push(columns[ci]['field']);
240 if ('fields' in columns[ci])
241 fields.push.apply(fields, columns[ci]['fields']);
249 exports.GetAlertFields = function(selected) {
250 var rawret = new Array();
251 var cols = exports.GetAlertColumns();
253 for (var i in cols) {
254 if ('field' in cols[i])
255 rawret.push(cols[i]['field']);
257 if ('fields' in cols[i])
258 rawret.push.apply(rawret, cols[i]['fields']);
262 var ret = rawret.filter(function(item, pos, self) {
263 return self.indexOf(item) == pos;
270 function ScheduleAlertSummary() {
272 if (kismet_ui.window_visible && alert_element.is(":visible")) {
273 var dt = alert_element.DataTable();
275 // Save the state. We can't use proper state saving because it seems to break
276 // the table position
277 kismet.putStorage('kismet.base.alerttable.order', JSON.stringify(dt.order()));
278 kismet.putStorage('kismet.base.alertttable.search', JSON.stringify(dt.search()));
280 // Snapshot where we are, because the 'don't reset page' in ajax.reload
281 // DOES still reset the scroll position
283 'top': $(dt.settings()[0].nScrollBody).scrollTop(),
284 'left': $(dt.settings()[0].nScrollBody).scrollLeft()
286 dt.ajax.reload(function(d) {
287 // Restore our scroll position
288 $(dt.settings()[0].nScrollBody).scrollTop( prev_pos.top );
289 $(dt.settings()[0].nScrollBody).scrollLeft( prev_pos.left );
297 // Set our timer outside of the datatable callback so that we get called even
298 // if the ajax load fails
299 alertTid = setTimeout(ScheduleAlertSummary, 2000);
302 function InitializeAlertTable() {
303 var cols = exports.GetAlertColumns();
304 var colmap = exports.GetAlertColumnMap(cols);
305 var fields = exports.GetAlertFields();
313 if ($.fn.dataTable.isDataTable(alert_element)) {
314 alert_element.DataTable().destroy();
315 alert_element.empty();
319 .on('xhr.dt', function(e, settings, json, xhr) {
320 json = kismet.sanitizeObject(json);
323 if (json['recordsFiltered'] != json['recordsTotal'])
324 alert_status_element.html(`${json['recordsTotal']} alerts (${json['recordsFiltered']} shown after filter)`);
326 alert_status_element.html(`${json['recordsTotal']} alerts`);
341 loadingIndicator: true,
344 url: local_uri_prefix + "alerts/alerts_view.json",
346 json: JSON.stringify(json)
352 order: [ [ 0, "desc" ] ],
353 createdRow: function(row, data, index) {
354 row.id = data['kismet.alert.hash'];
356 drawCallback: function(settings) {
361 }).every(function(rowIdx, tableLoop, rowLoop) {
362 for (var c in AlertColumns) {
363 var col = AlertColumns[c];
365 if (!('kismetdrawfunc') in col)
369 col.kismetdrawfunc(col, dt, this);
375 $('td', this.node()).css('background-color', severity_to_color(this.data()['kismet.alert.severity'])[0]);
376 $('td', this.node()).css('color', severity_to_color(this.data()['kismet.alert.severity'])[1]);
381 var alert_dt = alert_element.DataTable();
384 var saved_order = kismet.getStorage('kismet.base.alerttable.order', "");
385 if (saved_order !== "")
386 alert_dt.order(JSON.parse(saved_order));
388 // Restore the search
389 var saved_search = kismet.getStorage('kismet.base.alerttable.search', "");
390 if (saved_search !== "")
391 alert_dt.search(JSON.parse(saved_search));
393 // Set an onclick handler to spawn the device details dialog
394 $('tbody', alert_element).on('click', 'tr', function () {
395 exports.AlertDetailWindow(this.id);
398 $('tbody', alert_element)
399 .on( 'mouseenter', 'td', function () {
400 var alert_dt = alert_element.DataTable();
402 if (typeof(alert_dt.cell(this).index()) === 'undefined')
405 var colIdx = alert_dt.cell(this).index().column;
406 var rowIdx = alert_dt.cell(this).index().row;
408 // Remove from all cells
409 $(alert_dt.cells().nodes()).removeClass('kismet-highlight');
410 // Highlight the td in this row
411 $('td', alert_dt.row(rowIdx).nodes()).addClass('kismet-highlight');
417 kismet_ui_tabpane.AddTab({
420 createCallback: function(div) {
423 class: 'resize_wrapper',
428 class: 'stripe hover nowrap',
436 style: 'padding-bottom: 10px;',
440 alert_element = $('#alerts_dt', div);
441 alert_status_element = $('#alerts_status', div);
443 InitializeAlertTable();
444 ScheduleAlertSummary();
449 exports.AddAlertColumn('col_header', {
451 field: 'kismet.alert.header',
455 exports.AddAlertColumn('col_class', {
457 field: 'kismet.alert.class',
461 exports.AddAlertColumn('col_severity', {
463 field: 'kismet.alert.severity',
465 renderfunc: function(d, t, r, m) {
466 return severity_to_string(d);
470 exports.AddAlertColumn('col_time', {
472 field: 'kismet.alert.timestamp',
474 renderfunc: function(d, t, r, m) {
475 return kismet_ui_base.renderLastTime(d, t, r, m);
479 exports.AddAlertColumn('col_tx', {
480 sTitle: 'Transmitter',
481 field: 'kismet.alert.transmitter_mac',
482 name: 'Transmitter MAC',
483 renderfunc: function(d, t, r, m) {
484 if (d === "00:00:00:00:00:00")
486 return kismet.censorMAC(d);
490 exports.AddAlertColumn('col_sx', {
492 field: 'kismet.alert.source_mac',
494 renderfunc: function(d, t, r, m) {
495 if (d === "00:00:00:00:00:00")
497 return kismet.censorMAC(d);
501 exports.AddAlertColumn('col_dx', {
502 sTitle: 'Destination',
503 field: 'kismet.alert.dest_mac',
504 name: 'Destination MAC',
505 renderfunc: function(d, t, r, m) {
506 if (d === "00:00:00:00:00:00")
508 if (d === "FF:FF:FF:FF:FF:FF")
511 return kismet.censorMAC(d);
515 exports.AddAlertColumn('content', {
517 field: 'kismet.alert.text',
518 name: 'Alert content',
519 renderfunc: function(d, t, r, m) {
520 return kismet.censorMAC(d);
524 exports.AddAlertColumn('hash_hidden', {
526 field: 'kismet.alert.hash',
532 exports.AlertDetails = new Array();
534 exports.AddAlertDetail = function(id, title, pos, options) {
535 kismet_ui.AddDetail(exports.AlertDetails, id, title, pos, options);
538 exports.AlertDetailWindow = function(key) {
539 kismet_ui.DetailWindow(key, "Alert Details",
544 function(panel, options) {
545 var content = panel.content;
549 window['storage_detail_' + key] = {};
550 window['storage_detail_' + key]['foobar'] = 'bar';
552 panel.updater = function() {
553 if (kismet_ui.window_visible) {
554 $.get(local_uri_prefix + "alerts/by-id/" + key + "/alert.json")
555 .done(function(fulldata) {
556 fulldata = kismet.sanitizeObject(fulldata);
558 $('.loadoops', panel.content).hide();
560 panel.headerTitle(`Alert: ${fulldata['kismet.alert.header']}`);
562 var accordion = $('div#accordion', content);
564 if (accordion.length == 0) {
565 accordion = $('<div />', {
569 content.append(accordion);
572 var detailslist = exports.AlertDetails;
574 for (var dii in detailslist) {
575 var di = detailslist[dii];
578 if ('filter' in di.options &&
579 typeof(di.options.filter) === 'function') {
580 if (di.options.filter(fulldata) == false) {
585 var vheader = $('h3#header_' + di.id, accordion);
587 if (vheader.length == 0) {
588 vheader = $('<h3>', {
589 id: "header_" + di.id,
593 accordion.append(vheader);
596 var vcontent = $('div#' + di.id, accordion);
598 if (vcontent.length == 0) {
599 vcontent = $('<div>', {
602 accordion.append(vcontent);
605 // Do we have pre-rendered content?
606 if ('render' in di.options &&
607 typeof(di.options.render) === 'function') {
608 vcontent.html(di.options.render(fulldata));
611 if ('draw' in di.options && typeof(di.options.draw) === 'function') {
612 di.options.draw(fulldata, vcontent, options, 'storage_alert_' + key);
615 if ('finalize' in di.options &&
616 typeof(di.options.finalize) === 'function') {
617 di.options.finalize(fulldata, vcontent, options, 'storage_alert_' + key);
620 accordion.accordion({ heightStyle: 'fill' });
622 .fail(function(jqxhr, texterror) {
623 content.html("<div class=\"loadoops\" style=\"padding: 10px;\"><h1>Oops!</h1><p>An error occurred loading alert details for key <code>" + key +
624 "</code>: HTTP code <code>" + jqxhr.status + "</code>, " + texterror + "</div>");
627 panel.timerid = setTimeout(function() { panel.updater(); }, 1000);
630 panel.timerid = setTimeout(function() { panel.updater(); }, 1000);
638 function(panel, options) {
639 clearTimeout(panel.timerid);
640 panel.active = false;
641 window['storage_alert_' + key] = {};
645 exports.AddAlertDetail("alert", "Alert", 0, {
646 draw: function(data, target, options, storage) {
647 target.devicedata(data, {
651 field: 'kismet.alert.header',
654 help: 'Alert type / identifier; each alert has a unique type name.',
657 field: 'kismet.alert.class',
660 help: 'Each alert has a class, such as spoofing, denial of service, known exploit, etc.',
663 field: 'kismet.alert.severity',
666 draw: function(opts) {
667 return severity_to_string(opts['value']);
669 help: 'General severity of alert; in increasing severity, alerts are categorized as info, low, medium, high, and critical.',
672 field: 'kismet.alert.timestamp',
675 draw: function(opts) {
676 console.log(Math.floor(opts['value']));
677 return kismet_ui.RenderTrimmedTime({'value': Math.floor(opts['value'])});
681 field: 'kismet.alert.location/kismet.common.location.geopoint',
682 filter: function(opts) {
684 return opts['data']['kismet.alert.location']['kismet.common.location.fix'] >= 2;
690 draw: function(opts) {
692 if (opts['value'][1] == 0 || opts['value'][0] == 0)
693 return "<i>Unknown</i>";
695 return kismet.censorLocation(opts['value'][1]) + ", " + kismet.censorLocation(opts['value'][0]);
697 return "<i>Unknown</i>";
700 help: 'Location where alert occurred, either as the location of the Kismet server at the time of the alert or as the location of the packet, if per-packet location was available.',
703 field: 'kismet.alert.text',
704 title: 'Alert content',
705 draw: function(opts) {
706 return kismet.censorMAC(opts['value']);
708 help: 'Human-readable alert content',
711 groupTitle: 'Addresses',
713 filter: function(opts) {
714 return opts['data']['kismet.alert.transmitter_mac'] != '00:00:00:00:00:00' ||
715 opts['data']['kismet.alert.source_mac'] != '00:00:00:00:00:00' ||
716 opts['data']['kismet.alert.dest_mac'] != '00:00:00:00:00:00';
720 field: 'kismet.alert.source_mac',
722 filter: function(opts) {
723 return opts['value'] !== '00:00:00:00:00:00';
725 draw: function(opts) {
726 return kismet.censorMAC(opts['value']);
728 help: 'MAC address of the source of the packet triggering this alert.',
731 field: 'kismet.alert.transmitter_mac',
732 title: 'Transmitter',
733 filter: function(opts) {
734 return opts['value'] !== '00:00:00:00:00:00' &&
735 opts['data']['kismet.alert.source_mac'] !== opts['value'];
737 draw: function(opts) {
738 return kismet.censorMAC(opts['value']);
740 help: 'MAC address of the transmitter of the packet triggering this alert, if not the same as the source. On Wi-Fi this is typically the BSSID of the AP',
743 field: 'kismet.alert.dest_mac',
744 title: 'Destination',
745 filter: function(opts) {
746 return opts['value'] !== '00:00:00:00:00:00';
748 draw: function(opts) {
749 if (opts['value'] === 'FF:FF:FF:FF:FF:FF')
750 return '<i>All / Broadcast</i>'
751 return kismet.censorMAC(opts['value']);
753 help: 'MAC address of the destionation the packet triggering this alert.',
762 exports.AddAlertDetail("devel", "Dev/Debug Options", 10000, {
763 render: function(data) {
764 return 'Alert JSON: <a href="alerts/by-id/' + data['kismet.alert.hash'] + '/alert.prettyjson" target="_new">link</a><br />';
767 exports.load_complete = 1;