Merge branch '3.3' into 3.3-ciscat-extension

This commit is contained in:
Juanjo Jiménez 2018-06-20 16:53:42 +02:00 committed by GitHub
commit 182ef33196
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 264 additions and 98 deletions

View File

@ -14,7 +14,7 @@ import * as FileSaver from '../services/file-saver'
const app = uiModules.get('app/wazuh', []);
app.controller('agentsPreviewController', function ($scope, $routeParams, genericReq, apiReq, appState, $location, errorHandler, csvReq, shareAgent) {
app.controller('agentsPreviewController', function ($scope, $routeParams, genericReq, appState, $location, errorHandler, csvReq, shareAgent) {
$scope.search = term => {
$scope.$broadcast('wazuhSearch',{term})
@ -50,36 +50,6 @@ app.controller('agentsPreviewController', function ($scope, $routeParams, generi
$location.search('tab', $scope.submenuNavItem);
});
let tmpUrl, tmpUrl2;
if (appState.getClusterInfo().status === 'enabled') {
tmpUrl = `/api/wazuh-elastic/top/cluster/${appState.getClusterInfo().cluster}/agent.name`;
tmpUrl2 = `/api/wazuh-elastic/top/cluster/${appState.getClusterInfo().cluster}/agent.id`;
} else {
tmpUrl = `/api/wazuh-elastic/top/manager/${appState.getClusterInfo().manager}/agent.name`;
tmpUrl2 = `/api/wazuh-elastic/top/manager/${appState.getClusterInfo().manager}/agent.id`;
}
// Retrieve os list
const retrieveList = agents => {
for(const agent of agents){
if(agent.id === '000') continue;
if(agent.group && !$scope.groups.includes(agent.group)) $scope.groups.push(agent.group);
if(agent.node_name && !$scope.nodes.includes(agent.node_name)) $scope.nodes.push(agent.node_name);
if(agent.version && !$scope.versions.includes(agent.version)) $scope.versions.push(agent.version);
if(agent.os && agent.os.name){
const exists = $scope.osPlatforms.filter((e) => e.name === agent.os.name && e.platform === agent.os.platform && e.version === agent.os.version);
if(!exists.length){
$scope.osPlatforms.push({
name: agent.os.name,
platform: agent.os.platform,
version: agent.os.version
});
}
}
}
}
$scope.downloadCsv = async () => {
try {
errorHandler.info('Your download should begin automatically...', 'CSV')
@ -99,29 +69,35 @@ app.controller('agentsPreviewController', function ($scope, $routeParams, generi
const load = async () => {
try{
const api = JSON.parse(appState.getCurrentAPI()).id
const clusterInfo = appState.getClusterInfo();
const firstUrlParam = clusterInfo.status === 'enabled' ? 'cluster' : 'manager';
const secondUrlParam = clusterInfo[firstUrlParam];
const data = await Promise.all([
apiReq.request('GET', '/agents/summary', { }),
genericReq.request('GET', tmpUrl)
genericReq.request('GET', '/api/wazuh-api/agents-unique/' + api, {}),
genericReq.request('GET', `/api/wazuh-elastic/top/${firstUrlParam}/${secondUrlParam}/agent.name`)
]);
const unique = data[0].data.result;
// Once Wazuh core fixes agent 000 issues, this should be adjusted
const active = data[0].data.data.Active - 1;
const total = data[0].data.data.Total - 1;
$scope.groups = unique.groups;
$scope.nodes = unique.nodes;
$scope.versions = unique.versions;
$scope.osPlatforms = unique.osPlatforms;
$scope.lastAgent = unique.lastAgent;
$scope.agentsCountActive = unique.summary.agentsCountActive;
$scope.agentsCountDisconnected = unique.summary.agentsCountDisconnected;
$scope.agentsCountNeverConnected = unique.summary.agentsCountNeverConnected;
$scope.agentsCountTotal = unique.summary.agentsCountTotal;
$scope.agentsCoverity = unique.summary.agentsCoverity;
$scope.agentsCountActive = active;
$scope.agentsCountDisconnected = data[0].data.data.Disconnected;
$scope.agentsCountNeverConnected = data[0].data.data['Never connected'];
$scope.agentsCountTotal = total;
$scope.agentsCoverity = (active / total) * 100;
// tmpUrl y tmpUrl2
if (data[1].data.data === '') {
$scope.mostActiveAgent.name = appState.getClusterInfo().manager;
$scope.mostActiveAgent.id = '000';
} else {
$scope.mostActiveAgent.name = data[1].data.data;
const info = await genericReq.request('GET', tmpUrl2);
const info = await genericReq.request('GET', `/api/wazuh-elastic/top/${firstUrlParam}/${secondUrlParam}/agent.id`);
if (info.data.data === '' && $scope.mostActiveAgent.name !== '') {
$scope.mostActiveAgent.id = '000';
} else {
@ -129,22 +105,6 @@ app.controller('agentsPreviewController', function ($scope, $routeParams, generi
}
}
// Fetch agents sorting by -dateAdd and using pagination
const agents = [];
const total_items = data[0].data.data.Total;
let offset = 0;
const limit = 2000;
while(agents.length < total_items){
const page = await apiReq.request('GET', '/agents', { sort:'-dateAdd', limit, offset });
agents.push(...page.data.data.items)
offset += limit;
}
// Last agent
$scope.lastAgent = agents[0];
retrieveList(agents);
$scope.loading = false;
if(!$scope.$$phase) $scope.$digest();
return;

View File

@ -15,10 +15,11 @@ import FilterHandler from '../utils/filter-handler'
import generateMetric from '../utils/generate-metric'
import TabNames from '../utils/tab-names'
import { metricsAudit, metricsVulnerability, metricsScap, metricsCiscat, metricsVirustotal } from '../utils/agents-metrics'
import * as FileSaver from '../services/file-saver'
const app = uiModules.get('app/wazuh', []);
app.controller('agentsController', function ($timeout, $scope, $location, $rootScope, appState, apiReq, AgentsAutoComplete, errorHandler, tabVisualizations, vis2png, shareAgent, commonData, reportingService, visFactoryService) {
app.controller('agentsController', function ($timeout, $scope, $location, $rootScope, appState, apiReq, AgentsAutoComplete, errorHandler, tabVisualizations, vis2png, shareAgent, commonData, reportingService, visFactoryService, csvReq) {
$rootScope.reportStatus = false;
@ -212,6 +213,23 @@ app.controller('agentsController', function ($timeout, $scope, $location, $rootS
return;
}
$scope.downloadCsv = async data_path => {
try {
errorHandler.info('Your download should begin automatically...', 'CSV')
const currentApi = JSON.parse(appState.getCurrentAPI()).id;
const output = await csvReq.fetch(data_path, currentApi, null);
const blob = new Blob([output], {type: 'text/csv'});
FileSaver.saveAs(blob, 'packages.csv');
return;
} catch (error) {
errorHandler.handle(error,'Download CSV');
}
return;
}
//Destroy
$scope.$on("$destroy", () => {

View File

@ -381,13 +381,17 @@ app.controller('settingsController', function ($scope, $rootScope, $http, $route
};
// Toggle extension
$scope.toggleExtension = async (extension, state) => {
getCurrentAPIIndex()
if ($scope.apiEntries && $scope.apiEntries.length && ['oscap','audit','pci','gdpr','ciscat','aws','virustotal'].includes(extension)) {
$scope.apiEntries[currentApiEntryIndex]._source.extensions[extension] = state;
appState.setExtensions($scope.apiEntries[currentApiEntryIndex]._id,$scope.apiEntries[currentApiEntryIndex]._source.extensions);
$scope.toggleExtension = (extension, state) => {
try{
const api = JSON.parse(appState.getCurrentAPI()).id
const currentExtensions = appState.getExtensions(api);
currentExtensions[extension] = state;
appState.setExtensions(api,currentExtensions)
getCurrentAPIIndex()
$scope.apiEntries[currentApiEntryIndex]._source.extensions = currentExtensions;
if(!$scope.$$phase) $scope.$digest();
} catch (error) {
errorHandler.handle(error, 'Settings')
}
};

View File

@ -24,7 +24,8 @@ app.directive('wazuhTable', function() {
path: '=path',
keys: '=keys',
allowClick: '=allowClick',
implicitFilter: '=implicitFilter'
implicitFilter: '=implicitFilter',
rowsPerPage: '=rowsPerPage'
},
controller: function($scope, apiReq, $timeout, shareAgent, $location, errorHandler) {
$scope.keyEquivalence = KeyEquivalenece;
@ -52,7 +53,7 @@ app.directive('wazuhTable', function() {
$scope.wazuh_table_loading = true;
//// PAGINATION ////////////////////
$scope.itemsPerPage = 10;
$scope.itemsPerPage = $scope.rowsPerPage || 10;
$scope.pagedItems = [];
$scope.currentPage = 0;
let items = [];
@ -117,7 +118,9 @@ app.directive('wazuhTable', function() {
instance.addSorting(field.value || field);
$scope.sortValue = instance.sortValue;
$scope.sortDir = instance.sortDir;
items = await instance.fetch();
const result = await instance.fetch();
items = result.items;
$scope.time = result.time;
$scope.totalItems = items.length;
$scope.items = items;
checkGap();
@ -135,7 +138,9 @@ app.directive('wazuhTable', function() {
$scope.wazuh_table_loading = true;
if(removeFilters) instance.removeFilters();
instance.addFilter('search',term);
items = await instance.fetch();
const result = await instance.fetch();
items = result.items;
$scope.time = result.time;
$scope.totalItems = items.length;
$scope.items = items;
checkGap();
@ -160,7 +165,9 @@ app.directive('wazuhTable', function() {
instance.addFilter(filter.name,filter.value);
}
items = await instance.fetch();
const result = await instance.fetch();
items = result.items;
$scope.time = result.time;
$scope.totalItems = items.length;
$scope.items = items;
checkGap();
@ -194,13 +201,17 @@ app.directive('wazuhTable', function() {
const realTimeFunction = async () => {
try {
while(realTime) {
items = await instance.fetch({limit:10});
await $timeout(1000);
const result = await instance.fetch({limit:10});
items = result.items;
$scope.time = result.time;
$scope.totalItems = items.length;
$scope.items = items;
$scope.totalItems = items.length;
$scope.items = items;
checkGap();
$scope.searchTable();
if(!$scope.$$phase) $scope.$digest()
await $timeout(1000);
}
} catch(error) {
realTime = false;
@ -229,7 +240,9 @@ app.directive('wazuhTable', function() {
const init = async () => {
try {
$scope.wazuh_table_loading = true;
items = await instance.fetch();
const result = await instance.fetch();
items = result.items;
$scope.time = result.time;
$scope.totalItems = items.length;
$scope.items = items;
checkGap();
@ -244,6 +257,23 @@ app.directive('wazuhTable', function() {
init()
const splitArray = array => {
if(Array.isArray(array)){
if(!array.length) return false;
let str = '';
for(const item of array) str += `${item}, `;
str = str.substring(0, str.length - 2);
return str;
}
return array;
}
$scope.checkIfArray = item => {
return typeof item === 'object' ?
splitArray(item) :
item == 0 ? '0' : item;
}
$scope.$on('$destroy',() => {
realTime = null;
})

View File

@ -1,6 +1,6 @@
<div layout="row" class="md-padding" ng-show="wazuh_table_loading" >
<md-progress-linear class="md-accent" md-mode="indeterminate"></md-progress-linear>
<div class='uil-ring-css'><div></div></div>
</div>
<div layout="row" ng-show="!wazuh_table_loading && items.length">
<table class="table table-striped table-condensed" style="table-layout: fixed !important" id="wz_table">
@ -15,22 +15,31 @@
</thead>
<tbody>
<tr ng-class="allowClick ? 'cursor-pointer' : ''" class="wz-word-wrap" ng-repeat="item in pagedItems[currentPage]" ng-click="clickAction(item)">
<td ng-repeat="key in keys" ng-if="path !== '/decoders'">{{ key === 'os.name' ? item.os.name || '---' : key === 'os.version' ? item.os.version || '---' : item[key.value || key] || '---' }}</td>
<td ng-repeat="key in keys" ng-if="path !== '/decoders'">
{{
key === 'os.name' ?
item.os.name || '---' :
key === 'os.version' ?
item.os.version || '---' :
checkIfArray(item[key.value || key]) || '---'
}}
</td>
<td ng-repeat="key in keys" ng-if="path === '/decoders'">
{{
key === 'details.program_name' || key.value === 'details.program_name' ?
item.details.program_name || '---' :
item.details.program_name || '---' :
key === 'details.order' || key.value === 'details.order' ?
item.details.order || '---' :
item[key.value || key] || '---'
item.details.order || '---' :
checkIfArray(item[key.value || key]) || '---'
}}
</td>
</tr>
</tbody>
<tfoot ng-show="items.length >= itemsPerPage">
<tfoot >
<td colspan="{{keys.length}}">
<div class="pagination pull-right" style="margin:0 !important">
<span ng-show="!wazuh_table_loading" class="color-grey">{{ totalItems }} items ({{time | number: 2}} seconds)</span>
<div ng-show="items.length >= itemsPerPage" class="pagination pull-right" style="margin:0 !important">
<ul layout="row">
<li ng-class="{disabled: currentPage == 0}" class="md-padding">
<a href ng-click="prevPage()">« Prev</a>
@ -50,6 +59,16 @@
</tfoot>
</table>
</div>
<div layout="row" ng-show="!wazuh_table_loading" class="md-padding">
Result: {{ totalItems }} items.
<div layout="row" ng-if="!wazuh_table_loading && !totalItems">
<div flex class="euiCallOut euiCallOut--warning" data-test-subj="discoverNoResults">
<div class="euiCallOutHeader">
<svg class="euiIcon euiIcon--medium euiCallOutHeader__icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16"><defs><path id="help-a" d="M13.6 12.186l-1.357-1.358c-.025-.025-.058-.034-.084-.056.53-.794.84-1.746.84-2.773a4.977 4.977 0 0 0-.84-2.772c.026-.02.059-.03.084-.056L13.6 3.813a6.96 6.96 0 0 1 0 8.373zM8 15A6.956 6.956 0 0 1 3.814 13.6l1.358-1.358c.025-.025.034-.057.055-.084C6.02 12.688 6.974 13 8 13a4.978 4.978 0 0 0 2.773-.84c.02.026.03.058.056.083l1.357 1.358A6.956 6.956 0 0 1 8 15zm-5.601-2.813a6.963 6.963 0 0 1 0-8.373l1.359 1.358c.024.025.057.035.084.056A4.97 4.97 0 0 0 3 8c0 1.027.31 1.98.842 2.773-.027.022-.06.031-.084.056l-1.36 1.358zm5.6-.187A4 4 0 1 1 8 4a4 4 0 0 1 0 8zM8 1c1.573 0 3.019.525 4.187 1.4l-1.357 1.358c-.025.025-.035.057-.056.084A4.979 4.979 0 0 0 8 3a4.979 4.979 0 0 0-2.773.842c-.021-.027-.03-.059-.055-.084L3.814 2.4A6.957 6.957 0 0 1 8 1zm0-1a8.001 8.001 0 1 0 .003 16.002A8.001 8.001 0 0 0 8 0z"></path></defs><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#help-a" fill-rule="evenodd"></use></svg>
<span class="euiCallOutHeader__title">No results match your search criteria</span>
</div>
</div>
</div>
<div layout="row" ng-if="!wazuh_table_loading && !totalItems" class="wz-margin-top-10">
<span class="color-grey">0 items ({{time | number: 2}} seconds)</span>
</div>

View File

@ -56,6 +56,7 @@ export default class DataFactory {
async fetch(options = {}) {
try {
const start = new Date();
this.items = [];
let offset = 0;
const limit = options.limit || 2000;
@ -69,7 +70,9 @@ export default class DataFactory {
this.items.push(...firstPage.data.data.items)
if(options.limit) {
if(this.path === '/agents') this.items = this.items.filter(item => item.id !== '000')
return this.items;
const end = new Date();
const elapsed = (end - start) / 1000
return {items:this.items, time:elapsed};
}
const totalItems = firstPage.data.data.totalItems;
@ -89,7 +92,11 @@ export default class DataFactory {
const remainingItems = await Promise.all(ops);
remainingItems.map(page => this.items.push(...page.data.data.items))
if(this.path === '/agents') this.items = this.items.filter(item => item.id !== '000')
return this.items;
const end = new Date();
const elapsed = (end - start) / 1000;
return {items:this.items, time:elapsed};
} catch (error) {
return Promise.reject(error);

View File

@ -101,7 +101,12 @@
</div>
<div layout="row" class="margin-top-30">
<wazuh-table flex path="'/agents'" keys="['id','name','ip','status','group','os.name','os.version','version']" allow-click="true">
<wazuh-table
flex
path="'/agents'"
keys="['id',{value:'name',size:2},'ip','status','group','os.name','os.version','version']"
allow-click="true"
rows-per-page="17">
</wazuh-table>
</div>

View File

@ -70,4 +70,10 @@
keys="['name','architecture','version',{value:'vendor',size:3},{value:'description',size:3}]">
</wazuh-table>
</div>
<div layout="row" class="md-padding" ng-if="agent">
<span flex></span>
<a class="small" id="btnDownload" ng-click="downloadCsv('/syscollector/' + agent.id + '/packages')">Formatted
<i aria-hidden="true" class="fa fa-fw fa-download"></i>
</a>
</div>
</md-content>

View File

@ -63,7 +63,12 @@
<!-- Groups table -->
<div layout="row" ng-if="!lookingGroup" class="md-padding">
<wazuh-table flex path="'/agents/groups'" keys="['name','count','merged_sum']" allow-click="true">
<wazuh-table
flex
path="'/agents/groups'"
keys="['name','count','merged_sum']"
allow-click="true"
rows-per-page="19">
</wazuh-table>
</div>
<!-- End groups table -->
@ -79,8 +84,12 @@
<!-- Group agents table -->
<div layout="row" ng-if="lookingGroup && groupsSelectedTab==='agents' && currentGroup" class="md-padding">
<wazuh-table flex path="'/agents/groups/' + currentGroup.name" keys="['id','name','ip','status','os.name','os.version','version']"
allow-click="true">
<wazuh-table
flex
path="'/agents/groups/' + currentGroup.name"
keys="['id','name','ip','status','os.name','os.version','version']"
allow-click="true"
rows-per-page="14">
</wazuh-table>
</div>
<!-- End Group agents table -->
@ -96,8 +105,12 @@
<!-- Group files table -->
<div layout="row" ng-if="lookingGroup && groupsSelectedTab==='files' && !fileViewer && currentGroup" class="md-padding">
<wazuh-table flex path="'/agents/groups/' + currentGroup.name + '/files'" keys="[{value:'filename',size:2},{value:'hash',size:6}]"
allow-click="true">
<wazuh-table
flex
path="'/agents/groups/' + currentGroup.name + '/files'"
keys="[{value:'filename',size:2},{value:'hash',size:6}]"
allow-click="true"
rows-per-page="14">
</wazuh-table>
</div>
<!-- End Group files table -->

View File

@ -48,7 +48,12 @@
<!-- Logs table section -->
<div layout="row">
<wazuh-table flex path="'/manager/logs'" keys="['timestamp',{value:'tag',size:2},'level',{value:'description',size:4,nosortable:true}]"></wazuh-table>
<wazuh-table
flex
path="'/manager/logs'"
keys="['timestamp',{value:'tag',size:2},'level',{value:'description',size:4,nosortable:true}]"
rows-per-page="19">
</wazuh-table>
</div>
<!-- End Logs table section -->

View File

@ -47,13 +47,15 @@
flex
path="'/decoders'"
keys="['name',{value:'details.program_name',size:2},{value:'details.order',size:2},'file']"
allow-click="true">
allow-click="true"
rows-per-page="18">
</wazuh-table>
<wazuh-table ng-if="!implicitFilterFromDetail"
flex
path="'/decoders'"
keys="['name',{value:'details.program_name',size:2,nosortable:true},{value:'details.order',size:2,nosortable:true},'file']"
allow-click="true">
allow-click="true"
rows-per-page="18">
</wazuh-table>
</div>

View File

@ -55,12 +55,16 @@
implicit-filter="implicitFilterFromDetail"
flex
path="'/rules'"
keys="['id',{value:'file',size:2},{value:'description',size:2},{value:'groups',nosortable:true,size:2},{value:'pci',nosortable:true,size:2},{value:'gdpr',nosortable:true},'level']" allow-click="true">
keys="['id',{value:'file',size:2},{value:'description',size:2},{value:'groups',nosortable:true,size:2},{value:'pci',nosortable:true,size:2},{value:'gdpr',nosortable:true},'level']"
allow-click="true"
rows-per-page="18">
</wazuh-table>
<wazuh-table ng-if="!implicitFilterFromDetail"
flex
path="'/rules'"
keys="['id',{value:'file',size:2},{value:'description',size:2},{value:'groups',nosortable:true,size:2},{value:'pci',nosortable:true,size:2},{value:'gdpr',nosortable:true},'level']" allow-click="true">
keys="['id',{value:'file',size:2},{value:'description',size:2},{value:'groups',nosortable:true,size:2},{value:'pci',nosortable:true,size:2},{value:'gdpr',nosortable:true},'level']"
allow-click="true"
rows-per-page="18">
</wazuh-table>
</div>

View File

@ -6,6 +6,7 @@
height: 300px;
color: black;
direction: ltr;
border-top: 1px solid #ddd
}
/* PADDING */
@ -281,6 +282,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
.CodeMirror-code {
outline: none;
line-height: 18px;
}
/* Force content-box sizing for the elements where we expect it */

View File

@ -757,4 +757,93 @@ export default class WazuhApi {
}
}
async getAgentsFieldsUniqueCount(req, reply) {
try {
if(!req.params || !req.params.api) throw new Error('Field api is required')
const config = await this.wzWrapper.getWazuhConfigurationById(req.params.api);
const headers = {
headers: {
'wazuh-app-version': packageInfo.version
},
username : config.user,
password : config.password,
rejectUnauthorized: !config.insecure
};
const url = `${config.url}:${config.port}/agents`;
const params = {
limit : 2000,
offset: 0,
sort :'-dateAdd'
}
const items = [];
const output = await needle('get', url, params, headers)
items.push(...output.body.data.items)
const totalItems = output.body.data.totalItems;
while(items.length < totalItems){
params.offset += params.limit;
const tmp = await needle('get', url, params, headers)
items.push(...tmp.body.data.items)
}
const result = {
groups : [],
nodes : [],
versions : [],
osPlatforms: [],
lastAgent : items[0],
summary: {
agentsCountActive :0,
agentsCountDisconnected :0,
agentsCountNeverConnected:0,
agentsCountTotal :0,
agentsCoverity :0
}
}
for(const agent of items){
if(agent.id === '000') continue;
if(agent.group && !result.groups.includes(agent.group)) result.groups.push(agent.group);
if(agent.node_name && !result.nodes.includes(agent.node_name)) result.nodes.push(agent.node_name);
if(agent.version && !result.versions.includes(agent.version)) result.versions.push(agent.version);
if(agent.os && agent.os.name){
const exists = result.osPlatforms.filter((e) => e.name === agent.os.name && e.platform === agent.os.platform && e.version === agent.os.version);
if(!exists.length){
result.osPlatforms.push({
name: agent.os.name,
platform: agent.os.platform,
version: agent.os.version
});
}
}
}
const summary = await needle('get', url + '/summary', {}, headers)
// Once Wazuh core fixes agent 000 issues, this should be adjusted
const active = summary.body.data.Active - 1;
const total = summary.body.data.Total - 1;
result.summary.agentsCountActive = active;
result.summary.agentsCountDisconnected = summary.body.data.Disconnected;
result.summary.agentsCountNeverConnected = summary.body.data['Never connected'];
result.summary.agentsCountTotal = total;
result.summary.agentsCoverity = (active / total) * 100;
return reply({error:0, result})
} catch (error) {
return ErrorResponse(error.message || error, 3035, 500, reply)
}
}
}

View File

@ -57,4 +57,6 @@ export default (server, options) => {
// Returns total RAM available from the current machine where Kibana is being executed
server.route({ method: 'GET', path: '/api/wazuh-api/ram', handler: (req,res) => ctrl.totalRam(req,res)})
// Returns unique fields from the agents such OS, agent version ...
server.route({ method: 'GET', path: '/api/wazuh-api/agents-unique/{api}', handler: (req,res) => ctrl.getAgentsFieldsUniqueCount(req,res)});
};