<?php namespace App\Http\Controllers; use Cylab\Mark\Client; use App\Mark; use App\Feedback; use Illuminate\Support\Facades\Auth; class MarkController extends Controller { private function server() : Client { return Mark::get(); } public function error() { return view('app.error'); } public function status() { $status = $this->server()->status(); $status_history = $this->server()->history(); $history_memory = $this->extractPoints($status_history, "memory_used"); $history_load = $this->extractPoints($status_history, "load"); $history_jobs_executed = $this->extractPoints($status_history, "executor_jobs_executed"); $history_jobs_execution_rate = $this->computeExecutionRate($history_jobs_executed); $history_db_records = $this->extractPoints($status_history, "db_data_count"); $history_db_records_rate = $this->computeExecutionRate($history_db_records); $history_db_evidences = $this->extractPoints($status_history, "db_evidence_count"); $history_db_evidences_rate = $this->computeExecutionRate($history_db_evidences); $sources = $this->server()->sources(); return view("app.status", [ "status" => $status, "history_memory" => $history_memory, "history_load" => $history_load, "history_jobs_execution_rate" => $history_jobs_execution_rate, "history_db_records_rate" => $history_db_records_rate, "history_db_evidences_rate" => $history_db_evidences_rate, "sources" => $sources]); } public function dbStatus() { $db_status = $this->server()->status(); $summary = ["db.data.count" => $db_status["db.data.count"], "db.data.size" => $db_status["db.data.size"], "db.evidence.count" => $db_status["db.evidence.count"], "db.evidence.size" => $db_status["db.evidence.size"]]; $now = \Carbon\Carbon::now(); $file_name = $now->toISOString() . "_db_status.json"; $headers = array( "Content-type" => "text/json", "Content-Disposition" => "attachment; filename=$file_name", "Pragma" => "no-cache", "Cache-Control" => "must-revalidate, post-check=0, pre-check=0", "Expires" => "0" ); $content = json_encode($summary); return response($content, 200, $headers); } public function lastData() { return view("app.lastdata", ["data" => $this->server()->findLastData()]); } public function lastEvidences() { return view( "app.lastevidences", ["evidences" => $this->server()->findLastEvidences()] ); } public function timelineData(int $period) { //get a list of all detector labels $detectors = $this->server()->activation(); return ["evidences" => $this->server()->findEvidenceForPeriodAndInterval($period, 600), "agents" => $detectors, "period" => $period, "interval" => 600] ; } public function timeline(int $period) { //get the timeline data $data = $this->timelineData($period); return view( "app.timeline", $data ); } public function pause() { $this->server()->pause(); return redirect(action('MarkController@status')); } public function resume() { $this->server()->resume(); return redirect(action('MarkController@status')); } public function reload() { $this->server()->reload(); return redirect(action('MarkController@status')); } /** * Find * @param string $label * @return type the detector corresponding to this label. * Otherwize redirect to the detectors graph. */ private function findDetector(string $label) { $detectors = $this->server()->activation(); $detector = null; foreach ($detectors as $d) { if ($d->label == $label) { $detector = $d; break; } } if ($detector === null) { session()->flash( 'error', $label . ' is not a detector and does not produce a ranking' ); abort(redirect(action('MarkController@rankingHome'))); } return $detector; } public function ranking(string $label) { $detector = $this->findDetector($label); $evidences = $this->server()->findEvidence($label); $now = \Carbon\Carbon::now(); return view( "app.ranking", [ "label" => $label, "evidences" => $evidences, "detector" => $detector, "now" => $now] ); } public function bubble(string $label) { return view('app.bubble', ["label" => $label]); } public function radar(string $label) { return view('app.radar', ["label" => $label]); } public function rankingCSV(string $label) { $evidences = $this->server()->findEvidence($label); $now = \Carbon\Carbon::now(); $file_name = $now->toISOString() . "_$label.csv"; $headers = array( "Content-type" => "text/csv", "Content-Disposition" => "attachment; filename=$file_name", "Pragma" => "no-cache", "Cache-Control" => "must-revalidate, post-check=0, pre-check=0", "Expires" => "0" ); $columns = ['id', 'subject', 'score', 'time']; $callback = function () use ($evidences, $columns) { $file = fopen('php://output', 'w'); fputcsv($file, $columns); foreach ($evidences as $ev) { fputcsv($file, [ $ev->id, $ev->subject(), $ev->score, $ev->time ]); } fclose($file); }; return response()->stream($callback, 200, $headers); } public function rankingJSON(string $label) { $evidences = $this->server()->findEvidence($label); $summary = []; foreach ($evidences as $ev) { $summary[] = [ "id" => $ev->id, "subject" => $ev->subject, "score" => $ev->score, "time" => $ev->time ]; } $now = \Carbon\Carbon::now(); $file_name = $now->toISOString() . "_$label.json"; $headers = array( "Content-type" => "text/json", "Content-Disposition" => "attachment; filename=$file_name", "Pragma" => "no-cache", "Cache-Control" => "must-revalidate, post-check=0, pre-check=0", "Expires" => "0" ); $content = json_encode($summary); return response($content, 200, $headers); } public function rankingHome() { $activation_graph_elements = []; $detectors = $this->server()->activation(); // nodes foreach ($detectors as $detector) { // node $activation_graph_elements[] = ["data" => ["id" => $detector->label]]; // edges $sources = $this->findSources($detectors, $detector); foreach ($sources as $source) { $activation_graph_elements[] = ["data" => [ "id" => rand(), "source" => $source, "target" => $detector->label]]; } if (count($sources) == 0) { // the trigger_label for this detector is a data source // => create node + edge $activation_graph_elements[] = ["data" => ["id" => $detector->trigger_label]]; $activation_graph_elements[] = ["data" => [ "id" => rand(), "source" => $detector->trigger_label, "target" => $detector->label]]; } } return view("app.detectors", ["activation_graph_elements" => $activation_graph_elements]); } public function dashboardHome() { $graph_elements = []; $detectors = $this->server()->activation(); // detectors foreach ($detectors as $detector) { //$evidences = $this->rankingJSON($detector->label); // add the name of the agents and the evidences linked to it $graph_elements[] = ["name" => $detector->label]; } return view("app.dashboard", ["graph_elements" => $graph_elements]); } private function findSources(array $all_detectors, $detector) { $pattern = '/' . $detector->trigger_label . '/'; $sources = []; foreach ($all_detectors as $d) { if (preg_match($pattern, $d->label)) { $sources[] = $d->label; } } return $sources; } public function extractPoints(array $evidences, string $field) : array { $points = []; foreach ($evidences as $evidence) { // used by status history // this is dirty :-( // status history should return an array of objects if (is_array($evidence)) { $points[] = new \App\TimePoint( $evidence["time"], $evidence[$field] ); } else { $points[] = new \App\TimePoint( $evidence->time, $evidence->$field ); } } return $points; } public function evidence(string $id) { $time_window = 3600; // in seconds $ev = $this->server()->findEvidenceById($id); $since = $ev->time - $time_window * 1000; $references = []; foreach ($ev->references as $id) { $references[] = $this->server()->findEvidenceById($id); } $history = $this->server()->findEvidenceSince($ev->label, $ev->subject, $since); $feedback = Feedback::findByReportId($ev->id); return view( 'app.evidence', [ "evidence" => $ev, "history" => $history, "history_points" => $this->extractPoints($history, "score"), "feedback" => $feedback, "references" => $references] ); } /** * Mark an evidence as false alarm (whitelist). * @param string $id */ public function falseAlarm(string $id) { $feedback = new Feedback(); $feedback->report_id = $id; $feedback->is_true_alert = false; $feedback->user_id = Auth::id(); $feedback->save(); return redirect(action('MarkController@evidence', ['id' => $id])); } /** * Mark an evidence as false alarm (whitelist). * @param string $id */ public function trueDetection(string $id) { $feedback = new Feedback(); $feedback->report_id = $id; $feedback->is_true_alert = true; $feedback->user_id = Auth::id(); $feedback->save(); return redirect(action('MarkController@evidence', ['id' => $id])); } public function evidenceData(string $id, $data_id) { $ev = $this->server()->findEvidenceById($id); $query = $ev->requests[$data_id]; $data = $this->server()->findDataByQuery($query); return view('app.data', [ "evidence" => $ev, "query" => $query, "data" => $data ]); } /** * Compute the number of jobs executed per minute. * * @param array $history_jobs_executed * @return array */ private function computeExecutionRate(array $history_jobs_executed) : array { if (count($history_jobs_executed) < 2) { // not enough points to compute execution rate... return []; } $points = []; $last_point = $history_jobs_executed[0]; foreach ($history_jobs_executed as $point) { $delta_t_minutes = ($point->t - $last_point->t) / 60000; // this point is less than a minute after the last computed point if ($delta_t_minutes < 1) { continue; } $delta_value = $point->y - $last_point->y; if ($delta_value < 0) { $delta_value = 0; } $points[] = new \App\TimePoint( $point->t, round($delta_value / $delta_t_minutes) ); $last_point = $point; } return $points; } }