-
Notifications
You must be signed in to change notification settings - Fork 138
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DetectX Module #916
DetectX Module #916
Changes from 6 commits
a151635
b12a5d3
95b47d6
24f24bb
bd12293
bad971a
185a977
fdb1473
6db1564
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
DetectX Module | ||
============== | ||
|
||
Module pulls data from DetectX results json. It does not **run** DetectX, admins are responsible for creating their own launch daemon that runs DetectX and provides the results data to: | ||
``` | ||
/usr/local/munki/preflight.d/cache/detectx.json | ||
``` | ||
|
||
Example Launch Daemon | ||
``` xml | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>Label</key> | ||
<string>DetectX All User Run</string> | ||
<key>ProgramArguments</key> | ||
<array> | ||
<string>/Applications/DetectX Swift.app/Contents/MacOS/DetectX Swift</string> | ||
<string>search</string> | ||
<string>-aj</string> | ||
<string>/usr/loca/munki/preflight.d/cache/detectx.json</string> | ||
</array> | ||
<key>RunAtLoad</key> | ||
<true/> | ||
</dict> | ||
</plist> | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
<?php | ||
|
||
/** | ||
* homebrew module class | ||
* | ||
* @package munkireport | ||
* @author tuxudo | ||
**/ | ||
class Detectx_controller extends Module_controller | ||
{ | ||
|
||
/*** Protect methods with auth! ****/ | ||
function __construct() | ||
{ | ||
// Store module path | ||
$this->module_path = dirname(__FILE__); | ||
} | ||
|
||
/** | ||
* Default method | ||
* @author tuxudo | ||
* | ||
**/ | ||
function index() | ||
{ | ||
echo "You've loaded the detectx module!"; | ||
} | ||
public function get_stats() | ||
{ | ||
$obj = new View(); | ||
if (! $this->authorized()) { | ||
$obj->view('json', array('msg' => 'Not authorized')); | ||
return; | ||
} | ||
|
||
$queryobj = new Detectx_model(); | ||
$sql = "SELECT COUNT(1) as total, | ||
COUNT(CASE WHEN `status` = 'Clean' THEN 1 END) AS Clean, | ||
COUNT(CASE WHEN `status` = 'Infected' THEN 1 END) AS Infected | ||
FROM detectx"; | ||
$obj->view('json', array('msg' => current($queryobj->query($sql)))); | ||
} | ||
|
||
/** | ||
* Retrieve data in json format | ||
* | ||
**/ | ||
public function get_data($serial_number = '') | ||
{ | ||
$obj = new View(); | ||
|
||
if (! $this->authorized()) { | ||
$obj->view('json', array('msg' => 'Not authorized')); | ||
return; | ||
} | ||
|
||
$queryobj = new Detectx_model; | ||
$detectx_tab = array(); | ||
foreach($queryobj->retrieve_records($serial_number) as $detectxEntry) { | ||
$detectx_tab[] = $detectxEntry->rs; | ||
} | ||
|
||
$obj->view('json', array('msg' => $detectx_tab)); | ||
} | ||
|
||
} // END class Detectx_controller |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
<?php | ||
class Detectx_model extends \Model { | ||
|
||
function __construct($serial='') | ||
{ | ||
parent::__construct('id', 'detectx'); //primary key, tablename | ||
$this->rs['id'] = ''; | ||
$this->rs['serial_number'] = $serial; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add a UNIQUE rt statement: |
||
$this->rs['searchdate'] = 0; $this->rt['searchdate'] = 'BIGINT'; | ||
$this->rs['numberofissues'] = 0; | ||
$this->rs['status'] = ''; | ||
$this->rs['issues'] = ''; $this->rt['issues'] = 'TEXT'; | ||
|
||
|
||
// Schema version, increment when creating a db migration | ||
$this->schema_version = 0; | ||
|
||
// Add indexes | ||
$this->idx[] = array('numberofissues'); | ||
$this->idx[] = array('status'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would add an index on search date too. |
||
|
||
// Create table if it does not exist | ||
$this->create_table(); | ||
|
||
$this->serial_number = $serial; | ||
} | ||
|
||
// ------------------------------------------------------------------------ | ||
|
||
|
||
/** | ||
* Process data sent by postflight | ||
* | ||
* @param string data | ||
* @author wardsparadox | ||
* based on homebrew by tuxudo | ||
**/ | ||
function process($json) | ||
{ | ||
// Check if data was uploaded | ||
if ( ! $json ){ | ||
throw new Exception("Error Processing Request: No JSON file found", 1); | ||
} | ||
// Delete previous set | ||
$this->deleteWhere('serial_number=?', $this->serial_number); | ||
|
||
// Process json into object thingy | ||
$data = json_decode($json, true); | ||
$this->searchdate = strtotime($data['searchdate']); | ||
$len = count($data['issues']); | ||
if ($len > 0) | ||
{ | ||
foreach($data['issues'] as $issue){ | ||
$this->status = "Infected"; | ||
$this->issues .= ($issue . ";"); | ||
$this->numberofissues += 1; | ||
} | ||
} | ||
else { | ||
$this->status = "Clean"; | ||
$this->issues = 'No Issues Detected'; | ||
$this->numberofissues = 0; | ||
|
||
} | ||
$this->save(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
"listing": { | ||
"reporttitle": "DetectX Report", | ||
"clienttitle": "DetectX", | ||
"clienttab": "DetectX", | ||
"issues": "Issues", | ||
"numberofissues": "Number of Issues", | ||
"searchdate": "Search Date", | ||
"status": "Status" | ||
}, | ||
"widget": { | ||
"title": "DetectX Status", | ||
"clean": "Clean", | ||
"infected": "Infected" | ||
}, | ||
"title": "DetectX" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?php | ||
// | ||
return array( | ||
'client_tabs' => array( | ||
'detectx-tab' => array('view' => 'detectx_tab', 'i18n' => 'detectx.clienttitle', 'badge' => 'detectx-cnt'), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. detectx.clienttitle does not exist in the locale.json. You may want to change that to detectx.title (this controls the name in the dropdown in the client detail page) |
||
), | ||
'listings' => array( | ||
'detectx' => array('view' => 'detectx_listing', 'i18n' => 'detectx.clienttitle'), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. detectx.clienttitle does not exist in the locale.json. You may want to change that to detectx.title (this controls the name in the dropdown in the listing menu) |
||
), | ||
'widgets' => array( | ||
'detectx' => array('view' => 'detectx_widget'), | ||
), | ||
|
||
); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#!/bin/bash | ||
|
||
# detectx controller | ||
#CTL="${BASEURL}index.php?/module/detectx/" | ||
# Set preference to include this file in the preflight check | ||
setreportpref "detectx" "${CACHEPATH}detectx.json" | ||
if [ ! -f "${CACHEPATH}detectx.json" ]; then | ||
touch "${CACHEPATH}detectx.json" | ||
fi |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
#!/bin/bash | ||
|
||
# Remove homebrew.json file | ||
rm -f "${CACHEPATH}detectx.json" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
<?php $this->view('partials/head'); ?> | ||
|
||
<?php //Initialize models needed for the table | ||
new Machine_model; | ||
new Reportdata_model; | ||
new Detectx_model; | ||
?> | ||
|
||
<div class="container"> | ||
<div class="row"> | ||
<div class="col-lg-12"> | ||
|
||
<h3><span data-i18n="detectx.listing.reporttitle"></span> <span id="total-count" class='label label-primary'>…</span></h3> | ||
|
||
<table class="table table-striped table-condensed table-bordered"> | ||
|
||
<thead> | ||
<tr> | ||
<th data-i18n="listing.computername" data-colname='machine.computer_name'></th> | ||
<th data-i18n="serial" data-colname='reportdata.serial_number'></th> | ||
<th data-i18n="detectx.listing.searchdate" data-colname='detectx.searchdate'></th> | ||
<th data-i18n="detectx.listing.status" data-colname='detectx.status'></th> | ||
<th data-i18n="detectx.listing.numberofissues" data-colname='detectx.numberofissues'></th> | ||
</tr> | ||
</thead> | ||
|
||
<tbody> | ||
<tr> | ||
<td data-i18n="listing.loading" colspan="10" class="dataTables_empty"></td> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. colspan= should be equal to the total amount of columns in the listing. |
||
</tr> | ||
</tbody> | ||
|
||
</table> | ||
</div> <!-- /span 12 --> | ||
</div> <!-- /row --> | ||
</div> <!-- /container --> | ||
|
||
<script type="text/javascript"> | ||
|
||
$(document).on('appUpdate', function(e){ | ||
|
||
var oTable = $('.table').DataTable(); | ||
oTable.ajax.reload(); | ||
return; | ||
|
||
}); | ||
|
||
$(document).on('appReady', function(e, lang) { | ||
|
||
// Get modifiers from data attribute | ||
var mySort = [], // Initial sort | ||
hideThese = [], // Hidden columns | ||
col = 0, // Column counter | ||
runtypes = [], // Array for runtype column | ||
columnDefs = [{ visible: false, targets: hideThese }]; //Column Definitions | ||
|
||
$('.table th').map(function(){ | ||
|
||
columnDefs.push({name: $(this).data('colname'), targets: col}); | ||
|
||
if($(this).data('sort')){ | ||
mySort.push([col, $(this).data('sort')]) | ||
} | ||
|
||
if($(this).data('hide')){ | ||
hideThese.push(col); | ||
} | ||
|
||
col++ | ||
}); | ||
|
||
oTable = $('.table').dataTable( { | ||
ajax: { | ||
url: appUrl + '/datatables/data', | ||
type: "POST", | ||
data: function(d){ | ||
d.mrColNotEmpty = "searchdate"; | ||
|
||
// Check for column in search | ||
if(d.search.value){ | ||
$.each(d.columns, function(index, item){ | ||
if(item.name == 'detectx.' + d.search.value){ | ||
d.columns[index].search.value = '> 0'; | ||
} | ||
}); | ||
|
||
} | ||
|
||
if(d.search.value.match(/^\d+\.\d+(\.(\d+)?)?$/)){ | ||
var search = d.search.value.split('.').map(function(x){return ('0'+x).slice(-2)}).join(''); | ||
d.search.value = search; | ||
} | ||
} | ||
}, | ||
dom: mr.dt.buttonDom, | ||
buttons: mr.dt.buttons, | ||
order: mySort, | ||
columnDefs: columnDefs, | ||
createdRow: function( nRow, aData, iDataIndex ) { | ||
// Update name in first column to link | ||
var name=$('td:eq(0)', nRow).html(); | ||
if(name == ''){name = "No Name"}; | ||
var sn=$('td:eq(1)', nRow).html(); | ||
var link = mr.getClientDetailLink(name, sn, '#tab_detectx-tab'); | ||
$('td:eq(0)', nRow).html(link); | ||
var checkin = parseInt($('td:eq(2)', nRow).html()); | ||
var date = new Date(checkin * 1000); | ||
$('td:eq(2)', nRow).html('<span title="'+i18n.t('detectx.listing.searchdate')+" "+moment(date).format('llll')+'">'+moment(date).fromNow()+'</span>'); | ||
} | ||
}); | ||
}); | ||
</script> | ||
|
||
<?php $this->view('partials/foot'); ?> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
<div id="detectx-tab"></div> | ||
<h2 data-i18n="detectx.listing.clienttab"></h2> | ||
|
||
<script> | ||
$(document).on('appReady', function(){ | ||
$.getJSON(appUrl + '/module/detectx/get_data/' + serialNumber, function(data){ | ||
// Set count to detectx's number of issues | ||
$('#detectx-cnt').text(data[0].numberofissues); | ||
var skipThese = ['id','serial_number']; | ||
$.each(data, function(i,d){ | ||
|
||
// Generate rows from data | ||
var rows = '' | ||
for (var prop in d){ | ||
// Skip skipThese | ||
if(skipThese.indexOf(prop) == -1){ | ||
if(prop == 'searchdate'){ | ||
var reporteddate = d[prop]; | ||
var date = new Date(reporteddate * 1000) | ||
rows = rows + '<tr><th>'+i18n.t('detectx.listing.'+prop)+'</th><td>'+moment(date).format('llll')+'</td></tr>'; | ||
} | ||
else { | ||
if(prop == 'issues'){ | ||
var issue = ''; | ||
issue = d[prop].split(';').join('<br />'); | ||
rows = rows + '<tr><th>'+i18n.t('detectx.listing.'+prop)+'</th><td>'+issue+'</td></tr>'; | ||
} | ||
else{ | ||
rows = rows + '<tr><th>'+i18n.t('detectx.listing.'+prop)+'</th><td>'+d[prop]+'</td></tr>'; | ||
} | ||
} | ||
} | ||
} | ||
$('#detectx-tab') | ||
.append($('<table style="max-width: 900px">') | ||
.addClass('table table-responsive table-striped table-condensed') | ||
.append($('<tbody>') | ||
.append(rows))) | ||
}) | ||
|
||
}); | ||
}); | ||
</script> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Path is missing an 'l', should be
/usr/local/munki/preflight.d/cache/detectx.json