mirror of
https://github.com/valitydev/wazuh-kibana-app.git
synced 2024-11-07 02:15:24 +00:00
commit
d780082803
@ -31,6 +31,7 @@
|
||||
"ansicolors": "^0.3.2",
|
||||
"install": "^0.10.1",
|
||||
"js-yaml": "3.10.0",
|
||||
"json2csv": "^4.1.2",
|
||||
"lodash": "3.10.1",
|
||||
"needle": "^2.0.1",
|
||||
"node-cron": "^1.1.2",
|
||||
|
@ -56,6 +56,7 @@ import 'plugins/wazuh/services/data-handler.js';
|
||||
import 'plugins/wazuh/services/app-state.js';
|
||||
import 'plugins/wazuh/services/api-tester.js';
|
||||
import 'plugins/wazuh/services/pattern-handler.js';
|
||||
import 'plugins/wazuh/services/csv-request.js';
|
||||
|
||||
// Set up routes and views
|
||||
import 'plugins/wazuh/services/routes.js';
|
||||
|
@ -10,10 +10,11 @@
|
||||
* Find more information about this on the LICENSE file.
|
||||
*/
|
||||
import * as modules from 'ui/modules'
|
||||
import CsvGenerator from './csv-generator'
|
||||
|
||||
const app = modules.get('app/wazuh', []);
|
||||
|
||||
app.controller('agentsPreviewController', function ($scope, $rootScope, $routeParams, genericReq, apiReq, appState, Agents, $location, errorHandler) {
|
||||
app.controller('agentsPreviewController', function ($scope, $rootScope, $routeParams, genericReq, apiReq, appState, Agents, $location, errorHandler, csvReq) {
|
||||
$scope.loading = true;
|
||||
$scope.agents = Agents;
|
||||
$scope.status = 'all';
|
||||
@ -80,6 +81,18 @@ app.controller('agentsPreviewController', function ($scope, $rootScope, $routePa
|
||||
}
|
||||
}
|
||||
|
||||
$scope.downloadCsv = async () => {
|
||||
try {
|
||||
const currentApi = JSON.parse(appState.getCurrentAPI()).id;
|
||||
const output = await csvReq.fetch('/agents', currentApi, $scope.agents ? $scope.agents.filters : null);
|
||||
const csvGenerator = new CsvGenerator(output.csv, 'agents.csv');
|
||||
csvGenerator.download(true);
|
||||
} catch (error) {
|
||||
errorHandler.handle(error,'Download CSV');
|
||||
if(!$rootScope.$$phase) $rootScope.$digest();
|
||||
}
|
||||
}
|
||||
|
||||
const load = async () => {
|
||||
try{
|
||||
const data = await Promise.all([
|
||||
|
34
public/controllers/csv-generator.js
Normal file
34
public/controllers/csv-generator.js
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Wazuh app - CSV file generator
|
||||
* Copyright (C) 2018 Wazuh, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Find more information about this on the LICENSE file.
|
||||
*/
|
||||
export default class CsvGenerator {
|
||||
|
||||
constructor (dataArray, fileName) {
|
||||
this.dataArray = dataArray;
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
getLinkElement (linkText) {
|
||||
return this.linkElement = this.linkElement || $('<a>' + (linkText || '') + '</a>', {
|
||||
href: 'data:attachment/csv;base64,' + encodeURI(btoa(this.dataArray)),
|
||||
target: '_blank',
|
||||
download: this.fileName
|
||||
});
|
||||
}
|
||||
|
||||
download (removeAfterDownload) {
|
||||
this.getLinkElement().css('display', 'none').appendTo('body');
|
||||
this.getLinkElement()[0].click();
|
||||
if (removeAfterDownload) {
|
||||
this.getLinkElement().remove();
|
||||
}
|
||||
}
|
||||
}
|
@ -11,12 +11,13 @@
|
||||
*/
|
||||
import beautifier from 'plugins/wazuh/utils/json-beautifier';
|
||||
import * as modules from 'ui/modules'
|
||||
import CsvGenerator from './csv-generator'
|
||||
|
||||
const app = modules.get('app/wazuh', []);
|
||||
|
||||
// Groups preview controller
|
||||
app.controller('groupsPreviewController',
|
||||
function ($scope, $rootScope, $location, apiReq, Groups, GroupFiles, GroupAgents, errorHandler) {
|
||||
function ($scope, $rootScope, $location, apiReq, Groups, GroupFiles, GroupAgents, errorHandler, csvReq, appState) {
|
||||
const reloadWatcher = $rootScope.$watch('groupsIsReloaded',() => {
|
||||
delete $rootScope.groupsIsReloaded;
|
||||
$scope.lookingGroup = false;
|
||||
@ -31,6 +32,19 @@ function ($scope, $rootScope, $location, apiReq, Groups, GroupFiles, GroupAgents
|
||||
$scope.groupAgents = GroupAgents;
|
||||
$scope.groupFiles = GroupFiles;
|
||||
|
||||
$scope.downloadCsv = async dataProvider => {
|
||||
try {
|
||||
const path = $scope[dataProvider] ? $scope[dataProvider].path : null;
|
||||
const currentApi = JSON.parse(appState.getCurrentAPI()).id;
|
||||
const output = await csvReq.fetch(path, currentApi, $scope[dataProvider] ? $scope[dataProvider].filters : null);
|
||||
const csvGenerator = new CsvGenerator(output.csv, 'groups.csv');
|
||||
csvGenerator.download(true);
|
||||
} catch (error) {
|
||||
errorHandler.handle(error,'Download CSV');
|
||||
if(!$rootScope.$$phase) $rootScope.$digest();
|
||||
}
|
||||
}
|
||||
|
||||
// Store a boolean variable to check if come from agents
|
||||
const fromAgents = ('comeFrom' in $rootScope) && ('globalAgent' in $rootScope) && $rootScope.comeFrom === 'agents';
|
||||
|
||||
|
@ -10,11 +10,12 @@
|
||||
* Find more information about this on the LICENSE file.
|
||||
*/
|
||||
import * as modules from 'ui/modules'
|
||||
import CsvGenerator from './csv-generator'
|
||||
|
||||
const app = modules.get('app/wazuh', []);
|
||||
|
||||
// Logs controller
|
||||
app.controller('managerLogController', function ($scope, $rootScope, Logs, apiReq, errorHandler) {
|
||||
app.controller('managerLogController', function ($scope, $rootScope, Logs, apiReq, errorHandler, csvReq, appState) {
|
||||
$scope.searchTerm = '';
|
||||
$scope.loading = true;
|
||||
$scope.logs = Logs;
|
||||
@ -48,8 +49,20 @@ app.controller('managerLogController', function ($scope, $rootScope, Logs, apiRe
|
||||
if(!$scope.$$phase) $scope.$digest();
|
||||
}
|
||||
|
||||
$scope.downloadCsv = async () => {
|
||||
try {
|
||||
const currentApi = JSON.parse(appState.getCurrentAPI()).id;
|
||||
const output = await csvReq.fetch('/manager/logs', currentApi, $scope.logs ? $scope.logs.filters : null);
|
||||
const csvGenerator = new CsvGenerator(output.csv, 'logs.csv');
|
||||
csvGenerator.download(true);
|
||||
} catch (error) {
|
||||
errorHandler.handle(error,'Download CSV');
|
||||
if(!$rootScope.$$phase) $rootScope.$digest();
|
||||
}
|
||||
}
|
||||
|
||||
const initialize = async () => {
|
||||
try{
|
||||
try{
|
||||
await $scope.logs.nextPage();
|
||||
const data = await apiReq.request('GET', '/manager/logs/summary', {});
|
||||
$scope.summary = data.data.data;
|
||||
|
@ -10,10 +10,11 @@
|
||||
* Find more information about this on the LICENSE file.
|
||||
*/
|
||||
import * as modules from 'ui/modules'
|
||||
import CsvGenerator from './csv-generator'
|
||||
|
||||
const app = modules.get('app/wazuh', []);
|
||||
|
||||
app.controller('rulesController', function ($scope, $rootScope, Rules, RulesRelated, RulesAutoComplete, errorHandler, genericReq, appState) {
|
||||
app.controller('rulesController', function ($scope, $rootScope, Rules, RulesRelated, RulesAutoComplete, errorHandler, genericReq, appState, csvReq) {
|
||||
|
||||
$scope.setRulesTab = tab => $rootScope.globalsubmenuNavItem2 = tab;
|
||||
|
||||
@ -66,6 +67,18 @@ app.controller('rulesController', function ($scope, $rootScope, Rules, RulesRela
|
||||
}
|
||||
};
|
||||
|
||||
$scope.downloadCsv = async () => {
|
||||
try {
|
||||
const currentApi = JSON.parse(appState.getCurrentAPI()).id;
|
||||
const output = await csvReq.fetch('/rules', currentApi, $scope.rules ? $scope.rules.filters : null);
|
||||
const csvGenerator = new CsvGenerator(output.csv, 'rules.csv');
|
||||
csvGenerator.download(true);
|
||||
} catch (error) {
|
||||
errorHandler.handle(error,'Download CSV');
|
||||
if(!$rootScope.$$phase) $rootScope.$digest();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function takes back to the list but adding a group filter
|
||||
*/
|
||||
@ -169,7 +182,7 @@ app.controller('rulesController', function ($scope, $rootScope, Rules, RulesRela
|
||||
});
|
||||
});
|
||||
|
||||
app.controller('decodersController', function ($scope, $rootScope, $sce, Decoders, DecodersRelated, DecodersAutoComplete, errorHandler, genericReq, appState) {
|
||||
app.controller('decodersController', function ($scope, $rootScope, $sce, Decoders, DecodersRelated, DecodersAutoComplete, errorHandler, genericReq, appState, csvReq) {
|
||||
$scope.setRulesTab = tab => $rootScope.globalsubmenuNavItem2 = tab;
|
||||
|
||||
//Initialization
|
||||
@ -257,6 +270,18 @@ app.controller('decodersController', function ($scope, $rootScope, $sce, Decoder
|
||||
}
|
||||
}
|
||||
|
||||
$scope.downloadCsv = async () => {
|
||||
try {
|
||||
const currentApi = JSON.parse(appState.getCurrentAPI()).id;
|
||||
const output = await csvReq.fetch('/decoders', currentApi, $scope.decoders ? $scope.decoders.filters : null);
|
||||
const csvGenerator = new CsvGenerator(output.csv, 'decoders.csv');
|
||||
csvGenerator.download(true);
|
||||
} catch (error) {
|
||||
errorHandler.handle(error,'Download CSV');
|
||||
if(!$rootScope.$$phase) $rootScope.$digest();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function changes to the decoder detail view
|
||||
*/
|
||||
|
27
public/services/csv-request.js
Normal file
27
public/services/csv-request.js
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Wazuh app - API request service
|
||||
* Copyright (C) 2018 Wazuh, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Find more information about this on the LICENSE file.
|
||||
*/
|
||||
import * as modules from 'ui/modules'
|
||||
|
||||
const app = modules.get('app/wazuh', []);
|
||||
|
||||
app.service('csvReq', function (genericReq) {
|
||||
return {
|
||||
fetch: async (path, id, filters = null) => {
|
||||
try {
|
||||
const output = await genericReq.request('POST','/api/wazuh-api/csv',{ path, id, filters });
|
||||
return output.data;
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
@ -20,6 +20,7 @@ app.service('errorHandler', function ( Notifier, appState, $location) {
|
||||
if(error.data && error.data.errorData && error.data.errorData.message) return error.data.errorData.message;
|
||||
if(error.errorData && error.errorData.message) return error.errorData.message;
|
||||
if(error.data && typeof error.data === 'string') return error.data;
|
||||
if(error.data && error.data.error && typeof error.data.error === 'string') return error.data.error;
|
||||
if(error.data && error.data.message && typeof error.data.message === 'string') return error.data.message;
|
||||
if(error.data && error.data.message && error.data.message.msg && typeof error.data.message.msg === 'string') return error.data.message.msg;
|
||||
if(error.data && error.data.data && typeof error.data.data === 'string') return error.data.data;
|
||||
@ -119,7 +120,7 @@ app.service('errorHandler', function ( Notifier, appState, $location) {
|
||||
if(error.extraMessage) text = error.extraMessage;
|
||||
text = location ? location + '. ' + text : text;
|
||||
if(!silent){
|
||||
if(isWarning) notify.warning(text);
|
||||
if(isWarning || (text && typeof text === 'string' && text.toLowerCase().includes('no results'))) notify.warning(text);
|
||||
else notify.error(text);
|
||||
}
|
||||
if(goSettings) $location.path('/settings');
|
||||
|
@ -109,4 +109,8 @@
|
||||
class="no-lateral-padding"
|
||||
full="'agent'">
|
||||
</wz-table>
|
||||
<div layout="row" class="wz-margin-top-10">
|
||||
<span flex></span>
|
||||
<a class="small" id="btnDownload" ng-click="downloadCsv()">Formatted <i aria-hidden="true" class="fa fa-download"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -52,9 +52,12 @@
|
||||
{name:'MD5 agent.conf'}
|
||||
]">
|
||||
</wz-table-header>
|
||||
<wz-table ng-if="!lookingGroup" layout="row" flex func="loadGroup(index)" data="groups" keys="[{col:'name',size:20},{col:'count',size:15},'merged_sum']"
|
||||
ng-if="!lookingGroup"></wz-table>
|
||||
|
||||
<wz-table ng-if="!lookingGroup" layout="row" flex func="loadGroup(index)" data="groups" keys="[{col:'name',size:20},{col:'count',size:15},'merged_sum']"></wz-table>
|
||||
|
||||
<div layout="row" class="wz-margin-top-10" ng-if="!lookingGroup">
|
||||
<span flex></span>
|
||||
<a class="small" id="btnDownload" ng-click="downloadCsv('groups')">Formatted <i aria-hidden="true" class="fa fa-download"></i></a>
|
||||
</div>
|
||||
|
||||
<wz-table-header layout="row" data="groupAgents" keys="[
|
||||
{name:'ID',sortValue:'id',size:10},
|
||||
@ -66,19 +69,28 @@
|
||||
]" ng-show="lookingGroup && groupsSelectedTab==='agents'">
|
||||
</wz-table-header>
|
||||
|
||||
<wz-table layout="column" flex full="'agent'" func="showAgent(agent)" data="groupAgents" keys="[{col:'id',size:10},'name','ip','os.platform','os.version','version']" ng-if="lookingGroup && groupsSelectedTab==='agents'">
|
||||
</wz-table>
|
||||
|
||||
<div layout="row" class="wz-margin-top-10" ng-if="lookingGroup && groupsSelectedTab==='agents'">
|
||||
<span flex></span>
|
||||
<a class="small" id="btnDownload" ng-click="downloadCsv('groupAgents')">Formatted <i aria-hidden="true" class="fa fa-download"></i></a>
|
||||
</div>
|
||||
|
||||
<wz-table-header layout="row" data="groupFiles" keys="[
|
||||
{name:'File',sortValue:'filename',size:30},
|
||||
{name:'MD5 checksum'}
|
||||
]" ng-show="lookingGroup && groupsSelectedTab==='files' && !file">
|
||||
</wz-table-header>
|
||||
|
||||
<wz-table layout="column" flex full="'agent'" func="showAgent(agent)" data="groupAgents" keys="[{col:'id',size:10},'name','ip','os.platform','os.version','version']"
|
||||
ng-if="lookingGroup && groupsSelectedTab==='agents'">
|
||||
</wz-table>
|
||||
|
||||
<wz-table layout="column" flex func="showFile(index)" data="groupFiles" keys="[{col:'filename',size:30},'hash']" ng-if="lookingGroup && groupsSelectedTab==='files' && !file">
|
||||
</wz-table>
|
||||
|
||||
<div layout="row" class="wz-margin-top-10" ng-if="lookingGroup && groupsSelectedTab==='files' && !file">
|
||||
<span flex></span>
|
||||
<a class="small" id="btnDownload" ng-click="downloadCsv('groupFiles')">Formatted <i aria-hidden="true" class="fa fa-download"></i></a>
|
||||
</div>
|
||||
|
||||
<div flex layout="column" class="md-padding" ng-if="lookingGroup && groupsSelectedTab==='files' && file">
|
||||
<div flex layout="column">
|
||||
<div layout="row" class="wz-padding-bottom-14">
|
||||
|
@ -18,6 +18,7 @@
|
||||
<option value="warning">Warning</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<span flex></span>
|
||||
<md-button ng-if="!realtime" class="wazuh-button md-raised md-primary manager-log-buttonplay" ng-click="playRealtime()" aria-label="Play realtime button">
|
||||
<i class="fa fa-play fa-fw" aria-hidden="true"></i>
|
||||
@ -83,5 +84,9 @@
|
||||
nopointer="true"
|
||||
noheight="true">
|
||||
</wz-table>
|
||||
<div layout="row" class="wz-margin-top-10">
|
||||
<span flex></span>
|
||||
<a class="small" id="btnDownload" ng-click="downloadCsv()">Formatted <i aria-hidden="true" class="fa fa-download"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -83,4 +83,8 @@
|
||||
isdecoders="true"
|
||||
class="no-lateral-padding">
|
||||
</wz-table>
|
||||
<div layout="row" class="wz-margin-top-10">
|
||||
<span flex></span>
|
||||
<a class="small" id="btnDownload" ng-click="downloadCsv()">Formatted <i aria-hidden="true" class="fa fa-download"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -103,4 +103,8 @@
|
||||
isdecoders="false"
|
||||
class="no-lateral-padding">
|
||||
</wz-table>
|
||||
<div layout="row" class="wz-margin-top-10">
|
||||
<span flex></span>
|
||||
<a class="small" id="btnDownload" ng-click="downloadCsv()">Formatted <i aria-hidden="true" class="fa fa-download"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -21,7 +21,7 @@ import ElasticWrapper from '../lib/elastic-wrapper'
|
||||
import getPath from '../../util/get-path'
|
||||
import packageInfo from '../../package.json'
|
||||
import monitoring from '../monitoring'
|
||||
|
||||
import { Parser } from 'json2csv';
|
||||
const blueWazuh = colors.blue('wazuh');
|
||||
|
||||
export default class WazuhApi {
|
||||
@ -497,4 +497,69 @@ export default class WazuhApi {
|
||||
return reply(this.genericErrorBuilder(500,6,error.message || error)).code(500)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get full data on CSV format from a list Wazuh API endpoint
|
||||
* @param {*} req
|
||||
* @param {*} res
|
||||
*/
|
||||
async csv(req,res) {
|
||||
try{
|
||||
|
||||
if(!req.payload || !req.payload.path) throw new Error('Field path is required')
|
||||
if(!req.payload.id) throw new Error('Field id is required')
|
||||
|
||||
const filters = req.payload && req.payload.filters && Array.isArray(req.payload.filters) ?
|
||||
req.payload.filters :
|
||||
[];
|
||||
|
||||
const config = await this.wzWrapper.getWazuhConfigurationById(req.payload.id)
|
||||
|
||||
let path = req.payload.path;
|
||||
|
||||
if(path && typeof path === 'string'){
|
||||
path = path[0] === '/' ? path.substr(1) : path
|
||||
}
|
||||
|
||||
if(!path) throw new Error('An error occurred parsing path field')
|
||||
|
||||
const params = { limit: 99999 };
|
||||
|
||||
if(filters.length) {
|
||||
for(const filter of filters) {
|
||||
if(!filter.name || !filter.value) continue;
|
||||
params[filter.name] = filter.value;
|
||||
}
|
||||
}
|
||||
|
||||
const output = await needle('get', `${config.url}:${config.port}/${path}`, params, {
|
||||
username : config.user,
|
||||
password : config.password,
|
||||
rejectUnauthorized: !config.insecure
|
||||
})
|
||||
|
||||
if(output && output.body && output.body.data && output.body.data.totalItems) {
|
||||
const fields = Object.keys(output.body.data.items[0]);
|
||||
const data = output.body.data.items;
|
||||
|
||||
const json2csvParser = new Parser({ fields });
|
||||
|
||||
const csv = json2csvParser.parse(data);
|
||||
|
||||
return res({ csv });
|
||||
|
||||
} else if (output && output.body && output.body.data && !output.body.data.totalItems) {
|
||||
|
||||
throw new Error('No results')
|
||||
|
||||
} else {
|
||||
|
||||
throw new Error('An error occurred fetching data from the Wazuh API')
|
||||
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
return res({ error: error.message || error }).code(500)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,12 +33,15 @@ export default (server, options) => {
|
||||
// Write in debug log
|
||||
server.route({ method: 'POST', path: '/api/wazuh/errlog', handler: (req,res) => ctrl.postErrorLog(req,res) });
|
||||
|
||||
// COMMENT HERE
|
||||
// Force fetch data to be inserted on wazuh-monitoring indices
|
||||
server.route({ method: 'GET', path: '/api/wazuh-api/fetchAgents', handler: (req,res) => ctrl.fetchAgents(req,res) });
|
||||
|
||||
// COMMENT HERE
|
||||
// Returns the config.yml file parsed
|
||||
server.route({ method: 'GET', path: '/api/wazuh-api/configuration', handler: (req,res) => ctrl.getConfigurationFile(req,res) });
|
||||
|
||||
// COMMENT HERE
|
||||
server.route({ method: 'POST',path: '/api/wazuh-api/wlogin', handler: (req,res) => ctrl.login(req,res) });
|
||||
// Experimental feature to simulate a login system
|
||||
server.route({ method: 'POST', path: '/api/wazuh-api/wlogin', handler: (req,res) => ctrl.login(req,res) });
|
||||
|
||||
// Returns data from the Wazuh API on CSV readable format
|
||||
server.route({ method: 'POST', path: '/api/wazuh-api/csv', handler: (req,res) => ctrl.csv(req,res)})
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user