2018-04-22 19:10:29 +00:00
|
|
|
/*
|
|
|
|
* Wazuh app - Class for Wazuh-API functions
|
2019-01-14 16:36:47 +00:00
|
|
|
* Copyright (C) 2015-2019 Wazuh, Inc.
|
2018-04-22 19:10:29 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
2017-10-27 07:22:10 +00:00
|
|
|
|
2017-10-27 07:34:38 +00:00
|
|
|
// Require some libraries
|
2018-09-10 08:32:49 +00:00
|
|
|
import needle from 'needle';
|
2018-09-11 09:52:54 +00:00
|
|
|
import { pciRequirementsFile } from '../integration-files/pci-requirements';
|
|
|
|
import { gdprRequirementsFile } from '../integration-files/gdpr-requirements';
|
2018-09-10 08:32:49 +00:00
|
|
|
import { ElasticWrapper } from '../lib/elastic-wrapper';
|
|
|
|
import { getPath } from '../../util/get-path';
|
|
|
|
import { Monitoring } from '../monitoring';
|
|
|
|
import { ErrorResponse } from './error-response';
|
|
|
|
import { Parser } from 'json2csv';
|
2018-09-03 09:46:55 +00:00
|
|
|
import { getConfiguration } from '../lib/get-configuration';
|
2018-09-10 08:32:49 +00:00
|
|
|
import { log } from '../logger';
|
2018-09-11 09:52:54 +00:00
|
|
|
import { KeyEquivalenece } from '../../util/csv-key-equivalence';
|
2019-01-21 15:34:27 +00:00
|
|
|
import { ApiErrorEquivalence } from '../../util/api-errors-equivalence';
|
2018-09-25 14:01:42 +00:00
|
|
|
import { cleanKeys } from '../../util/remove-key';
|
2018-12-11 12:28:14 +00:00
|
|
|
import { apiRequestList } from '../../util/api-request-list';
|
2018-12-13 11:30:28 +00:00
|
|
|
import * as ApiHelper from '../lib/api-helper';
|
2018-07-16 09:35:46 +00:00
|
|
|
|
2018-09-03 09:46:55 +00:00
|
|
|
export class WazuhApiCtrl {
|
2018-12-11 16:12:59 +00:00
|
|
|
/**
|
2018-12-13 10:02:53 +00:00
|
|
|
* Constructor
|
|
|
|
* @param {*} server
|
|
|
|
*/
|
2018-09-10 08:32:49 +00:00
|
|
|
constructor(server) {
|
|
|
|
this.wzWrapper = new ElasticWrapper(server);
|
2018-12-13 11:30:28 +00:00
|
|
|
this.monitoringInstance = new Monitoring(server, true);
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
|
|
|
|
2018-12-11 16:12:59 +00:00
|
|
|
/**
|
|
|
|
* Returns if the wazuh-api configuration is working
|
2018-12-13 10:02:53 +00:00
|
|
|
* @param {Object} req
|
|
|
|
* @param {Object} reply
|
2018-12-12 11:47:05 +00:00
|
|
|
* @returns {Object} status obj or ErrorResponse
|
2018-12-11 16:12:59 +00:00
|
|
|
*/
|
2018-09-10 08:32:49 +00:00
|
|
|
async checkStoredAPI(req, reply) {
|
|
|
|
try {
|
|
|
|
// Get config from elasticsearch
|
2018-12-13 11:30:28 +00:00
|
|
|
const api = await this.wzWrapper.getWazuhConfigurationById(req.payload);
|
|
|
|
if (api.error_code > 1) {
|
2018-09-10 08:32:49 +00:00
|
|
|
throw new Error(`Could not find Wazuh API entry on Elasticsearch.`);
|
2018-12-13 11:30:28 +00:00
|
|
|
} else if (api.error_code > 0) {
|
2018-09-10 08:32:49 +00:00
|
|
|
throw new Error(
|
|
|
|
'Valid credentials not found in Elasticsearch. It seems the credentials were not saved.'
|
|
|
|
);
|
|
|
|
}
|
2018-12-13 11:30:28 +00:00
|
|
|
|
|
|
|
const credInfo = ApiHelper.buildOptionsObject(api);
|
|
|
|
|
2018-09-10 08:32:49 +00:00
|
|
|
let response = await needle(
|
|
|
|
'get',
|
2018-12-13 11:30:28 +00:00
|
|
|
`${api.url}:${api.port}/version`,
|
2018-09-10 08:32:49 +00:00
|
|
|
{},
|
2018-10-15 10:34:33 +00:00
|
|
|
credInfo
|
2018-09-10 08:32:49 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
if (parseInt(response.body.error) === 0 && response.body.data) {
|
|
|
|
// Checking the cluster status
|
|
|
|
response = await needle(
|
|
|
|
'get',
|
2018-12-13 11:30:28 +00:00
|
|
|
`${api.url}:${api.port}/cluster/status`,
|
2018-09-10 08:32:49 +00:00
|
|
|
{},
|
2018-10-15 10:34:33 +00:00
|
|
|
credInfo
|
2018-09-10 08:32:49 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
if (!response.body.error) {
|
2018-10-15 10:34:33 +00:00
|
|
|
try {
|
|
|
|
const managerInfo = await needle(
|
|
|
|
'get',
|
2018-12-13 11:30:28 +00:00
|
|
|
`${api.url}:${api.port}/agents/000`,
|
2018-10-15 10:34:33 +00:00
|
|
|
{},
|
|
|
|
credInfo
|
|
|
|
);
|
|
|
|
const updatedManagerName = managerInfo.body.data.name;
|
2018-12-13 11:30:28 +00:00
|
|
|
api.cluster_info.manager = updatedManagerName;
|
2018-12-11 16:24:50 +00:00
|
|
|
await this.wzWrapper.updateWazuhIndexDocument(null, req.payload, {
|
2018-12-13 11:30:28 +00:00
|
|
|
doc: { cluster_info: api.cluster_info }
|
2018-10-15 10:34:33 +00:00
|
|
|
});
|
|
|
|
} catch (error) {
|
|
|
|
log(
|
|
|
|
'POST /api/check-stored-api :: Error updating Wazuh manager name.',
|
|
|
|
error.message || error
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-09-10 08:32:49 +00:00
|
|
|
// If cluster mode is active
|
|
|
|
if (response.body.data.enabled === 'yes') {
|
|
|
|
response = await needle(
|
|
|
|
'get',
|
2018-12-13 11:30:28 +00:00
|
|
|
`${api.url}:${api.port}/cluster/node`,
|
2018-09-10 08:32:49 +00:00
|
|
|
{},
|
2018-10-15 10:34:33 +00:00
|
|
|
credInfo
|
2018-09-10 08:32:49 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
if (!response.body.error) {
|
2018-12-13 11:30:28 +00:00
|
|
|
let managerName = api.cluster_info.manager;
|
|
|
|
delete api.cluster_info;
|
|
|
|
api.cluster_info = {};
|
|
|
|
api.cluster_info.status = 'enabled';
|
|
|
|
api.cluster_info.manager = managerName;
|
|
|
|
api.cluster_info.node = response.body.data.node;
|
|
|
|
api.cluster_info.cluster = response.body.data.cluster;
|
|
|
|
api.password = '****';
|
2019-01-31 14:41:51 +00:00
|
|
|
return {
|
2018-09-10 08:32:49 +00:00
|
|
|
statusCode: 200,
|
2018-12-13 11:30:28 +00:00
|
|
|
data: api,
|
2018-09-10 08:32:49 +00:00
|
|
|
idChanged: req.idChanged || null
|
2019-01-31 14:41:51 +00:00
|
|
|
};
|
2018-09-10 08:32:49 +00:00
|
|
|
} else if (response.body.error) {
|
|
|
|
const tmpMsg =
|
2018-12-18 15:19:05 +00:00
|
|
|
((response || {}).body || {}).message ||
|
|
|
|
'Unexpected error from /cluster/node';
|
2018-09-10 08:32:49 +00:00
|
|
|
throw new Error(tmpMsg);
|
2018-03-22 16:10:33 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
} else {
|
|
|
|
// Cluster mode is not active
|
2018-12-13 11:30:28 +00:00
|
|
|
let managerName = api.cluster_info.manager;
|
|
|
|
delete api.cluster_info;
|
|
|
|
api.cluster_info = {};
|
|
|
|
api.cluster_info.status = 'disabled';
|
|
|
|
api.cluster_info.cluster = 'Disabled';
|
|
|
|
api.cluster_info.manager = managerName;
|
|
|
|
api.password = '****';
|
2017-10-27 07:46:52 +00:00
|
|
|
|
2019-01-31 14:41:51 +00:00
|
|
|
return {
|
2018-09-10 08:32:49 +00:00
|
|
|
statusCode: 200,
|
2018-12-13 11:30:28 +00:00
|
|
|
data: api,
|
2018-09-10 08:32:49 +00:00
|
|
|
idChanged: req.idChanged || null
|
2019-01-31 14:41:51 +00:00
|
|
|
};
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const tmpMsg =
|
2018-12-18 15:19:05 +00:00
|
|
|
((response || {}).body || {}).message ||
|
|
|
|
'Unexpected error from /cluster/status';
|
2018-04-22 19:10:29 +00:00
|
|
|
|
2018-09-10 08:32:49 +00:00
|
|
|
throw new Error(tmpMsg);
|
2018-04-22 19:10:29 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
} else {
|
2018-12-18 15:19:05 +00:00
|
|
|
if (((response || {}).body || {}).message) {
|
2018-09-10 08:32:49 +00:00
|
|
|
throw new Error(response.body.message);
|
2018-04-22 19:10:29 +00:00
|
|
|
}
|
2017-10-27 08:05:27 +00:00
|
|
|
|
2018-12-13 11:30:28 +00:00
|
|
|
throw new Error(`${api.url}:${api.port}/version is unreachable`);
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
|
|
|
} catch (error) {
|
2019-02-08 11:31:17 +00:00
|
|
|
if (error.code === 'EPROTO') {
|
|
|
|
log('POST /api/check-stored-api', 'Wrong protocol being used to connect to the Wazuh API');
|
|
|
|
return {
|
|
|
|
statusCode: 200,
|
|
|
|
data: { password: '****', apiIsDown: true }
|
|
|
|
};
|
|
|
|
} else if (error.code === 'ECONNREFUSED') {
|
2018-10-01 07:56:50 +00:00
|
|
|
log('POST /api/check-stored-api', error.message || error);
|
2019-01-31 14:41:51 +00:00
|
|
|
return {
|
2018-09-10 08:32:49 +00:00
|
|
|
statusCode: 200,
|
|
|
|
data: { password: '****', apiIsDown: true }
|
2019-01-31 14:41:51 +00:00
|
|
|
};
|
2018-09-10 08:32:49 +00:00
|
|
|
} else {
|
|
|
|
// Check if we can connect to a different API
|
|
|
|
if (
|
|
|
|
error &&
|
|
|
|
error.body &&
|
|
|
|
typeof error.body.found !== 'undefined' &&
|
|
|
|
!error.body.found
|
|
|
|
) {
|
|
|
|
try {
|
|
|
|
const apis = await this.wzWrapper.getWazuhAPIEntries();
|
|
|
|
for (const api of apis.hits.hits) {
|
|
|
|
try {
|
2018-12-13 11:30:28 +00:00
|
|
|
const options = ApiHelper.buildOptionsObject(api);
|
|
|
|
|
|
|
|
options.password = Buffer.from(
|
|
|
|
api._source.api_password,
|
|
|
|
'base64'
|
|
|
|
).toString('ascii');
|
|
|
|
|
2018-09-10 08:32:49 +00:00
|
|
|
const response = await needle(
|
|
|
|
'get',
|
|
|
|
`${api._source.url}:${api._source.api_port}/version`,
|
|
|
|
{},
|
2018-12-13 11:30:28 +00:00
|
|
|
options
|
2018-09-10 08:32:49 +00:00
|
|
|
);
|
|
|
|
if (
|
2019-01-03 08:28:51 +00:00
|
|
|
((response || {}).body || {}).error === 0 &&
|
2018-12-28 15:34:38 +00:00
|
|
|
((response || {}).body || {}).data
|
2018-09-10 08:32:49 +00:00
|
|
|
) {
|
|
|
|
req.payload = api._id;
|
|
|
|
req.idChanged = api._id;
|
|
|
|
return this.checkStoredAPI(req, reply);
|
2018-03-22 14:50:42 +00:00
|
|
|
}
|
2019-02-08 11:31:17 +00:00
|
|
|
} catch (error) { } // eslint-disable-line
|
2018-04-22 19:10:29 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
} catch (error) {
|
2018-10-01 07:56:50 +00:00
|
|
|
log('POST /api/check-stored-api', error.message || error);
|
2018-09-10 08:32:49 +00:00
|
|
|
return ErrorResponse(error.message || error, 3020, 500, reply);
|
|
|
|
}
|
2018-03-22 14:50:42 +00:00
|
|
|
}
|
2018-10-01 07:56:50 +00:00
|
|
|
log('POST /api/check-stored-api', error.message || error);
|
2018-09-10 08:32:49 +00:00
|
|
|
return ErrorResponse(error.message || error, 3002, 500, reply);
|
|
|
|
}
|
2018-04-11 07:58:23 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
2016-10-25 19:54:05 +00:00
|
|
|
|
2018-12-11 16:12:59 +00:00
|
|
|
/**
|
|
|
|
* This perfoms a validation of API params
|
|
|
|
* @param {Object} payload API params
|
|
|
|
*/
|
2018-09-10 08:32:49 +00:00
|
|
|
validateCheckApiParams(payload) {
|
|
|
|
if (!('user' in payload)) {
|
|
|
|
return 'Missing param: API USER';
|
2018-05-14 10:56:27 +00:00
|
|
|
}
|
|
|
|
|
2018-09-10 08:32:49 +00:00
|
|
|
if (!('password' in payload) && !('id' in payload)) {
|
|
|
|
return 'Missing param: API PASSWORD';
|
|
|
|
}
|
2018-06-08 07:34:26 +00:00
|
|
|
|
2018-09-10 08:32:49 +00:00
|
|
|
if (!('url' in payload)) {
|
|
|
|
return 'Missing param: API URL';
|
|
|
|
}
|
2018-05-14 10:56:27 +00:00
|
|
|
|
2018-09-10 08:32:49 +00:00
|
|
|
if (!('port' in payload)) {
|
|
|
|
return 'Missing param: API PORT';
|
|
|
|
}
|
2018-05-14 10:56:27 +00:00
|
|
|
|
2018-09-10 08:32:49 +00:00
|
|
|
if (!payload.url.includes('https://') && !payload.url.includes('http://')) {
|
|
|
|
return 'protocol_error';
|
|
|
|
}
|
2018-05-14 10:56:27 +00:00
|
|
|
|
2018-09-10 08:32:49 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-12-11 16:12:59 +00:00
|
|
|
/**
|
|
|
|
* This check the wazuh-api configuration received in the POST body will work
|
2018-12-13 10:02:53 +00:00
|
|
|
* @param {Object} req
|
|
|
|
* @param {Object} reply
|
2018-12-12 11:47:05 +00:00
|
|
|
* @returns {Object} status obj or ErrorResponse
|
2018-12-11 16:12:59 +00:00
|
|
|
*/
|
2018-09-10 08:32:49 +00:00
|
|
|
async checkAPI(req, reply) {
|
|
|
|
try {
|
|
|
|
let apiAvailable = null;
|
|
|
|
|
|
|
|
const notValid = this.validateCheckApiParams(req.payload);
|
|
|
|
if (notValid) return ErrorResponse(notValid, 3003, 500, reply);
|
|
|
|
|
|
|
|
// Check if a Wazuh API id is given (already stored API)
|
|
|
|
if (req.payload && req.payload.id && !req.payload.password) {
|
|
|
|
const data = await this.wzWrapper.getWazuhConfigurationById(
|
|
|
|
req.payload.id
|
|
|
|
);
|
|
|
|
if (data) apiAvailable = data;
|
|
|
|
else
|
|
|
|
return ErrorResponse(
|
|
|
|
`The API ${req.payload.id} was not found`,
|
|
|
|
3029,
|
|
|
|
500,
|
|
|
|
reply
|
|
|
|
);
|
|
|
|
|
|
|
|
// Check if a password is given
|
|
|
|
} else if (req.payload && req.payload.password) {
|
|
|
|
apiAvailable = req.payload;
|
|
|
|
apiAvailable.password = Buffer.from(
|
|
|
|
req.payload.password,
|
|
|
|
'base64'
|
|
|
|
).toString('ascii');
|
|
|
|
}
|
|
|
|
|
|
|
|
let response = await needle(
|
|
|
|
'get',
|
|
|
|
`${apiAvailable.url}:${apiAvailable.port}/version`,
|
|
|
|
{},
|
2018-12-13 11:30:28 +00:00
|
|
|
ApiHelper.buildOptionsObject(apiAvailable)
|
2018-09-10 08:32:49 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
// Check wrong credentials
|
|
|
|
if (parseInt(response.statusCode) === 401) {
|
|
|
|
return ErrorResponse('Wrong credentials', 3004, 500, reply);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parseInt(response.body.error) === 0 && response.body.data) {
|
|
|
|
response = await needle(
|
|
|
|
'get',
|
|
|
|
`${apiAvailable.url}:${apiAvailable.port}/agents/000`,
|
|
|
|
{},
|
2018-12-13 11:30:28 +00:00
|
|
|
ApiHelper.buildOptionsObject(apiAvailable)
|
2018-09-10 08:32:49 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
if (!response.body.error) {
|
|
|
|
const managerName = response.body.data.name;
|
|
|
|
|
|
|
|
response = await needle(
|
|
|
|
'get',
|
|
|
|
`${apiAvailable.url}:${apiAvailable.port}/cluster/status`,
|
|
|
|
{},
|
2018-12-13 11:30:28 +00:00
|
|
|
ApiHelper.buildOptionsObject(apiAvailable)
|
2018-09-10 08:32:49 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
if (!response.body.error) {
|
|
|
|
if (response.body.data.enabled === 'yes') {
|
|
|
|
// If cluster mode is active
|
|
|
|
response = await needle(
|
|
|
|
'get',
|
|
|
|
`${apiAvailable.url}:${apiAvailable.port}/cluster/node`,
|
|
|
|
{},
|
2018-12-13 11:30:28 +00:00
|
|
|
ApiHelper.buildOptionsObject(apiAvailable)
|
2018-09-10 08:32:49 +00:00
|
|
|
);
|
2018-05-14 10:56:27 +00:00
|
|
|
|
2018-09-10 08:32:49 +00:00
|
|
|
if (!response.body.error) {
|
2019-01-31 14:41:51 +00:00
|
|
|
return {
|
2018-09-10 08:32:49 +00:00
|
|
|
manager: managerName,
|
|
|
|
node: response.body.data.node,
|
|
|
|
cluster: response.body.data.cluster,
|
|
|
|
status: 'enabled'
|
2019-01-31 14:41:51 +00:00
|
|
|
};
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Cluster mode is not active
|
2019-01-31 14:41:51 +00:00
|
|
|
return {
|
2018-09-10 08:32:49 +00:00
|
|
|
manager: managerName,
|
|
|
|
cluster: 'Disabled',
|
|
|
|
status: 'disabled'
|
2019-01-31 14:41:51 +00:00
|
|
|
};
|
2018-05-14 10:56:27 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
2017-10-27 08:05:27 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
2018-12-28 15:34:38 +00:00
|
|
|
|
2018-09-10 08:32:49 +00:00
|
|
|
const tmpMsg =
|
2019-01-03 08:28:51 +00:00
|
|
|
((response || {}).body || {}).message ||
|
|
|
|
'Unexpected error checking the Wazuh API';
|
2018-09-10 08:32:49 +00:00
|
|
|
|
|
|
|
throw new Error(tmpMsg);
|
|
|
|
} catch (error) {
|
2019-02-08 11:31:17 +00:00
|
|
|
if (error.code === 'EPROTO') {
|
|
|
|
log('POST /api/check-api', 'Wrong protocol being used to connect to the Wazuh API');
|
|
|
|
return ErrorResponse('Wrong protocol being used to connect to the Wazuh API', 3005, 500, reply);
|
|
|
|
}
|
2018-10-01 07:56:50 +00:00
|
|
|
log('POST /api/check-api', error.message || error);
|
2018-09-10 08:32:49 +00:00
|
|
|
return ErrorResponse(error.message || error, 3005, 500, reply);
|
2018-04-11 07:58:23 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
2017-08-13 19:54:56 +00:00
|
|
|
|
2018-12-11 16:12:59 +00:00
|
|
|
/**
|
|
|
|
* This get PCI requirements
|
2018-12-13 10:02:53 +00:00
|
|
|
* @param {Object} req
|
|
|
|
* @param {Object} reply
|
2018-12-12 11:47:05 +00:00
|
|
|
* @returns {Array<Object>} requirements or ErrorResponse
|
2018-12-11 16:12:59 +00:00
|
|
|
*/
|
2018-09-10 08:32:49 +00:00
|
|
|
async getPciRequirement(req, reply) {
|
|
|
|
try {
|
|
|
|
let pci_description = '';
|
2016-10-25 19:54:05 +00:00
|
|
|
|
2018-09-10 08:32:49 +00:00
|
|
|
if (req.params.requirement === 'all') {
|
|
|
|
if (!req.headers.id) {
|
2019-01-31 14:41:51 +00:00
|
|
|
return pciRequirementsFile;
|
2018-03-22 16:10:33 +00:00
|
|
|
}
|
2018-12-13 11:30:28 +00:00
|
|
|
let api = await this.wzWrapper.getWazuhConfigurationById(
|
2018-09-10 08:32:49 +00:00
|
|
|
req.headers.id
|
|
|
|
);
|
|
|
|
|
2018-12-13 11:30:28 +00:00
|
|
|
if (api.error_code > 1) {
|
2018-09-10 08:32:49 +00:00
|
|
|
// Can not connect to elasticsearch
|
|
|
|
return ErrorResponse(
|
|
|
|
'Elasticsearch unexpected error or cannot connect',
|
|
|
|
3007,
|
|
|
|
400,
|
|
|
|
reply
|
|
|
|
);
|
2018-12-13 11:30:28 +00:00
|
|
|
} else if (api.error_code > 0) {
|
2018-09-10 08:32:49 +00:00
|
|
|
// Credentials not found
|
|
|
|
return ErrorResponse('Credentials does not exists', 3008, 400, reply);
|
2018-07-10 14:25:15 +00:00
|
|
|
}
|
|
|
|
|
2018-09-10 08:32:49 +00:00
|
|
|
const response = await needle(
|
|
|
|
'get',
|
2018-12-13 11:30:28 +00:00
|
|
|
`${api.url}:${api.port}/rules/pci`,
|
2018-09-10 08:32:49 +00:00
|
|
|
{},
|
2018-12-13 11:30:28 +00:00
|
|
|
ApiHelper.buildOptionsObject(api)
|
2018-09-10 08:32:49 +00:00
|
|
|
);
|
|
|
|
|
2018-12-28 15:34:38 +00:00
|
|
|
if ((((response || {}).body || {}).data || {}).items) {
|
2018-09-10 08:32:49 +00:00
|
|
|
let PCIobject = {};
|
2018-12-28 15:34:38 +00:00
|
|
|
for (const item of response.body.data.items) {
|
2018-09-10 08:32:49 +00:00
|
|
|
if (typeof pciRequirementsFile[item] !== 'undefined')
|
|
|
|
PCIobject[item] = pciRequirementsFile[item];
|
|
|
|
}
|
2019-01-31 14:41:51 +00:00
|
|
|
return PCIobject;
|
2016-10-25 19:54:05 +00:00
|
|
|
} else {
|
2018-09-10 08:32:49 +00:00
|
|
|
return ErrorResponse(
|
|
|
|
'An error occurred trying to parse PCI DSS requirements',
|
|
|
|
3009,
|
|
|
|
400,
|
|
|
|
reply
|
|
|
|
);
|
2016-10-25 19:54:05 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
} else {
|
|
|
|
if (
|
|
|
|
typeof pciRequirementsFile[req.params.requirement] !== 'undefined'
|
|
|
|
) {
|
|
|
|
pci_description = pciRequirementsFile[req.params.requirement];
|
2018-04-21 11:31:47 +00:00
|
|
|
}
|
2017-06-01 15:08:10 +00:00
|
|
|
|
2019-01-31 14:41:51 +00:00
|
|
|
return {
|
2018-09-10 08:32:49 +00:00
|
|
|
pci: {
|
|
|
|
requirement: req.params.requirement,
|
|
|
|
description: pci_description
|
|
|
|
}
|
2019-01-31 14:41:51 +00:00
|
|
|
};
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
return ErrorResponse(error.message || error, 3010, 400, reply);
|
2018-01-24 20:33:45 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
2018-01-24 20:33:45 +00:00
|
|
|
|
2018-12-11 16:12:59 +00:00
|
|
|
/**
|
|
|
|
* This get GDPR Requirements
|
2018-12-13 10:02:53 +00:00
|
|
|
* @param {Object} req
|
|
|
|
* @param {Object} reply
|
2018-12-12 11:47:05 +00:00
|
|
|
* @returns {Array<Object>} requirements or ErrorResponse
|
2018-12-11 16:12:59 +00:00
|
|
|
*/
|
2018-09-10 08:32:49 +00:00
|
|
|
async getGdprRequirement(req, reply) {
|
|
|
|
try {
|
|
|
|
let gdpr_description = '';
|
2018-04-26 11:51:49 +00:00
|
|
|
|
2018-09-10 08:32:49 +00:00
|
|
|
if (req.params.requirement === 'all') {
|
|
|
|
if (!req.headers.id) {
|
2019-01-31 14:41:51 +00:00
|
|
|
return gdprRequirementsFile;
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
2018-12-13 11:30:28 +00:00
|
|
|
const api = await this.wzWrapper.getWazuhConfigurationById(
|
2018-09-10 08:32:49 +00:00
|
|
|
req.headers.id
|
|
|
|
);
|
|
|
|
|
|
|
|
// Checking for GDPR
|
|
|
|
const version = await needle(
|
|
|
|
'get',
|
2018-12-13 11:30:28 +00:00
|
|
|
`${api.url}:${api.port}/version`,
|
2018-09-10 08:32:49 +00:00
|
|
|
{},
|
2018-12-13 11:30:28 +00:00
|
|
|
ApiHelper.buildOptionsObject(api)
|
2018-09-10 08:32:49 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
const number = version.body.data;
|
|
|
|
|
|
|
|
const major = number.split('v')[1].split('.')[0];
|
|
|
|
const minor = number
|
|
|
|
.split('v')[1]
|
|
|
|
.split('.')[1]
|
|
|
|
.split('.')[0];
|
|
|
|
const patch = number
|
|
|
|
.split('v')[1]
|
|
|
|
.split('.')[1]
|
|
|
|
.split('.')[1];
|
|
|
|
|
|
|
|
if (
|
|
|
|
(major >= 3 && minor < 2) ||
|
|
|
|
(major >= 3 && minor >= 2 && patch < 3)
|
|
|
|
) {
|
2019-01-31 14:41:51 +00:00
|
|
|
return {};
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
2018-04-25 14:24:27 +00:00
|
|
|
|
2018-12-13 11:30:28 +00:00
|
|
|
if (api.error_code > 1) {
|
2018-09-10 08:32:49 +00:00
|
|
|
// Can not connect to elasticsearch
|
|
|
|
return ErrorResponse(
|
|
|
|
'Elasticsearch unexpected error or cannot connect',
|
|
|
|
3024,
|
|
|
|
400,
|
|
|
|
reply
|
|
|
|
);
|
2018-12-13 11:30:28 +00:00
|
|
|
} else if (api.error_code > 0) {
|
2018-09-10 08:32:49 +00:00
|
|
|
// Credentials not found
|
|
|
|
return ErrorResponse('Credentials does not exists', 3025, 400, reply);
|
2018-06-04 09:10:59 +00:00
|
|
|
}
|
|
|
|
|
2018-09-10 08:32:49 +00:00
|
|
|
const response = await needle(
|
|
|
|
'get',
|
2018-12-13 11:30:28 +00:00
|
|
|
`${api.url}:${api.port}/rules/gdpr`,
|
2018-09-10 08:32:49 +00:00
|
|
|
{},
|
2018-12-13 11:30:28 +00:00
|
|
|
ApiHelper.buildOptionsObject(api)
|
2018-09-10 08:32:49 +00:00
|
|
|
);
|
|
|
|
|
2018-12-28 15:34:38 +00:00
|
|
|
if ((((response || {}).body || {}).data || {}).items) {
|
2018-09-10 08:32:49 +00:00
|
|
|
let GDPRobject = {};
|
2018-12-28 15:34:38 +00:00
|
|
|
for (const item of response.body.data.items) {
|
2018-09-10 08:32:49 +00:00
|
|
|
if (typeof gdprRequirementsFile[item] !== 'undefined')
|
|
|
|
GDPRobject[item] = gdprRequirementsFile[item];
|
|
|
|
}
|
2019-01-31 14:41:51 +00:00
|
|
|
return GDPRobject;
|
2018-09-10 08:32:49 +00:00
|
|
|
} else {
|
|
|
|
return ErrorResponse(
|
|
|
|
'An error occurred trying to parse GDPR requirements',
|
|
|
|
3026,
|
|
|
|
400,
|
|
|
|
reply
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (
|
|
|
|
typeof gdprRequirementsFile[req.params.requirement] !== 'undefined'
|
|
|
|
) {
|
|
|
|
gdpr_description = gdprRequirementsFile[req.params.requirement];
|
|
|
|
}
|
2018-05-29 11:40:37 +00:00
|
|
|
|
2019-01-31 14:41:51 +00:00
|
|
|
return {
|
2018-09-10 08:32:49 +00:00
|
|
|
gdpr: {
|
|
|
|
requirement: req.params.requirement,
|
|
|
|
description: gdpr_description
|
|
|
|
}
|
2019-01-31 14:41:51 +00:00
|
|
|
};
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
return ErrorResponse(error.message || error, 3027, 400, reply);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-11 16:12:59 +00:00
|
|
|
/**
|
2019-01-14 10:32:36 +00:00
|
|
|
* This performs a request over Wazuh API and returns its response
|
2018-12-11 16:12:59 +00:00
|
|
|
* @param {String} method Method: GET, PUT, POST, DELETE
|
|
|
|
* @param {String} path API route
|
|
|
|
* @param {Object} data data and params to perform the request
|
|
|
|
* @param {String} id API id
|
2018-12-13 10:02:53 +00:00
|
|
|
* @param {Object} reply
|
2018-12-12 11:47:05 +00:00
|
|
|
* @returns {Object} API response or ErrorResponse
|
2018-12-11 16:12:59 +00:00
|
|
|
*/
|
2018-09-10 08:32:49 +00:00
|
|
|
async makeRequest(method, path, data, id, reply) {
|
2019-01-15 09:47:39 +00:00
|
|
|
const devTools = !!(data || {}).devTools;
|
2018-09-10 08:32:49 +00:00
|
|
|
try {
|
2018-12-13 11:30:28 +00:00
|
|
|
const api = await this.wzWrapper.getWazuhConfigurationById(id);
|
2018-09-10 08:32:49 +00:00
|
|
|
|
2019-01-14 11:12:24 +00:00
|
|
|
if (devTools) {
|
2019-01-14 10:45:25 +00:00
|
|
|
delete data.devTools;
|
|
|
|
}
|
2019-01-14 11:12:24 +00:00
|
|
|
|
2018-12-13 11:30:28 +00:00
|
|
|
if (api.error_code > 1) {
|
2018-09-10 08:32:49 +00:00
|
|
|
//Can not connect to elasticsearch
|
|
|
|
return ErrorResponse(
|
|
|
|
'Could not connect with elasticsearch',
|
|
|
|
3011,
|
|
|
|
404,
|
|
|
|
reply
|
|
|
|
);
|
2018-12-13 11:30:28 +00:00
|
|
|
} else if (api.error_code > 0) {
|
2018-09-10 08:32:49 +00:00
|
|
|
//Credentials not found
|
|
|
|
return ErrorResponse('Credentials does not exists', 3012, 404, reply);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!data) {
|
|
|
|
data = {};
|
|
|
|
}
|
|
|
|
|
2018-12-13 11:30:28 +00:00
|
|
|
const options = ApiHelper.buildOptionsObject(api);
|
2018-09-10 08:32:49 +00:00
|
|
|
|
2019-01-03 15:59:46 +00:00
|
|
|
// Set content type application/xml if needed
|
|
|
|
if (
|
|
|
|
typeof (data || {}).content === 'string' &&
|
|
|
|
(data || {}).origin === 'xmleditor'
|
|
|
|
) {
|
|
|
|
options.content_type = 'application/xml';
|
|
|
|
data = data.content.replace(new RegExp('\\n', 'g'), '');
|
|
|
|
}
|
|
|
|
|
2019-01-25 12:02:53 +00:00
|
|
|
if (
|
|
|
|
typeof (data || {}).content === 'string' &&
|
|
|
|
(data || {}).origin === 'json'
|
|
|
|
) {
|
|
|
|
options.content_type = 'application/json';
|
|
|
|
data = data.content.replace(new RegExp('\\n', 'g'), '');
|
|
|
|
}
|
|
|
|
|
2019-01-29 16:07:43 +00:00
|
|
|
if (
|
|
|
|
typeof (data || {}).content === 'string' &&
|
|
|
|
(data || {}).origin === 'raw'
|
|
|
|
) {
|
|
|
|
options.content_type = 'application/octet-stream';
|
|
|
|
data = data.content;
|
|
|
|
}
|
|
|
|
|
2018-12-13 11:30:28 +00:00
|
|
|
const fullUrl = getPath(api) + path;
|
2018-09-10 08:32:49 +00:00
|
|
|
const response = await needle(method, fullUrl, data, options);
|
|
|
|
|
|
|
|
if (
|
2019-01-14 11:12:24 +00:00
|
|
|
!((response || {}).body || {}).error &&
|
|
|
|
((response || {}).body || {}).data
|
2018-09-10 08:32:49 +00:00
|
|
|
) {
|
2018-09-25 14:01:42 +00:00
|
|
|
cleanKeys(response);
|
2019-01-31 14:41:51 +00:00
|
|
|
return response.body;
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
|
|
|
|
2019-01-14 11:12:24 +00:00
|
|
|
if (((response || {}).body || {}).error && devTools) {
|
2019-01-31 14:41:51 +00:00
|
|
|
return response.body;
|
2019-01-14 10:32:36 +00:00
|
|
|
}
|
2019-01-14 10:45:25 +00:00
|
|
|
|
2019-01-14 10:32:36 +00:00
|
|
|
throw ((response || {}).body || {}).error &&
|
2019-02-08 11:31:17 +00:00
|
|
|
((response || {}).body || {}).message
|
2019-01-14 10:32:36 +00:00
|
|
|
? { message: response.body.message, code: response.body.error }
|
|
|
|
: new Error('Unexpected error fetching data from the Wazuh API');
|
|
|
|
} catch (error) {
|
2019-01-21 15:25:58 +00:00
|
|
|
if (devTools) {
|
2019-01-31 14:41:51 +00:00
|
|
|
return { error: '3013', message: error.message || error };
|
2019-01-21 15:25:58 +00:00
|
|
|
} else {
|
2019-01-21 15:34:27 +00:00
|
|
|
if ((error || {}).code && ApiErrorEquivalence[error.code]) {
|
|
|
|
error.message = ApiErrorEquivalence[error.code];
|
2019-01-21 15:25:58 +00:00
|
|
|
}
|
|
|
|
return ErrorResponse(
|
|
|
|
error.message || error,
|
|
|
|
`Wazuh API error: ${error.code}` || 3013,
|
|
|
|
500,
|
|
|
|
reply
|
|
|
|
);
|
|
|
|
}
|
2018-06-01 07:51:24 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
|
|
|
|
2018-12-11 16:12:59 +00:00
|
|
|
/**
|
|
|
|
* This performs a generic request and returs its response
|
|
|
|
* @param {String} method Method: GET, PUT, POST, DELETE
|
|
|
|
* @param {String} path API route
|
|
|
|
* @param {Object} data data and params to perform the request
|
|
|
|
* @param {String} id API id
|
|
|
|
*/
|
2018-09-10 08:32:49 +00:00
|
|
|
async makeGenericRequest(method, path, data, id) {
|
|
|
|
try {
|
2018-12-13 11:30:28 +00:00
|
|
|
const api = await this.wzWrapper.getWazuhConfigurationById(id);
|
2018-09-10 08:32:49 +00:00
|
|
|
|
2018-12-13 11:30:28 +00:00
|
|
|
if (api.error_code > 1) {
|
2018-09-10 08:32:49 +00:00
|
|
|
//Can not connect to elasticsearch
|
|
|
|
throw new Error('Could not connect with elasticsearch');
|
2018-12-13 11:30:28 +00:00
|
|
|
} else if (api.error_code > 0) {
|
2018-09-10 08:32:49 +00:00
|
|
|
//Credentials not found
|
|
|
|
throw new Error('Credentials does not exists');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!data) {
|
|
|
|
data = {};
|
|
|
|
}
|
|
|
|
|
2018-12-13 11:30:28 +00:00
|
|
|
const options = ApiHelper.buildOptionsObject(api);
|
2018-09-10 08:32:49 +00:00
|
|
|
|
2018-12-13 11:30:28 +00:00
|
|
|
const fullUrl = getPath(api) + path;
|
2018-09-10 08:32:49 +00:00
|
|
|
const response = await needle(method, fullUrl, data, options);
|
|
|
|
|
|
|
|
if (
|
|
|
|
response &&
|
|
|
|
response.body &&
|
|
|
|
!response.body.error &&
|
|
|
|
response.body.data
|
|
|
|
) {
|
2018-09-25 14:01:42 +00:00
|
|
|
cleanKeys(response);
|
2018-09-10 08:32:49 +00:00
|
|
|
return response.body;
|
|
|
|
}
|
|
|
|
|
2019-01-03 08:28:51 +00:00
|
|
|
throw ((response || {}).body || {}).error &&
|
2019-02-08 11:31:17 +00:00
|
|
|
((response || {}).body || {}).message
|
2018-09-28 13:23:47 +00:00
|
|
|
? { message: response.body.message, code: response.body.error }
|
2018-09-10 08:32:49 +00:00
|
|
|
: new Error('Unexpected error fetching data from the Wazuh API');
|
|
|
|
} catch (error) {
|
|
|
|
return Promise.reject(error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-11 16:12:59 +00:00
|
|
|
/**
|
|
|
|
* This make a request to API
|
2018-12-13 10:02:53 +00:00
|
|
|
* @param {Object} req
|
|
|
|
* @param {Object} reply
|
2018-12-12 11:47:05 +00:00
|
|
|
* @returns {Object} api response or ErrorResponse
|
2018-12-11 16:12:59 +00:00
|
|
|
*/
|
2018-09-10 08:32:49 +00:00
|
|
|
requestApi(req, reply) {
|
2018-10-30 13:21:00 +00:00
|
|
|
const configuration = getConfiguration();
|
2018-12-11 12:28:14 +00:00
|
|
|
const adminMode = !(
|
|
|
|
configuration &&
|
|
|
|
typeof configuration.admin !== 'undefined' &&
|
|
|
|
!configuration.admin
|
|
|
|
);
|
2018-11-15 08:39:12 +00:00
|
|
|
|
2018-09-10 08:32:49 +00:00
|
|
|
if (!req.payload.method) {
|
|
|
|
return ErrorResponse('Missing param: method', 3015, 400, reply);
|
|
|
|
} else if (!req.payload.path) {
|
|
|
|
return ErrorResponse('Missing param: path', 3016, 400, reply);
|
|
|
|
} else {
|
2018-12-11 12:28:14 +00:00
|
|
|
if (req.payload.method !== 'GET' && !adminMode) {
|
|
|
|
return ErrorResponse(
|
|
|
|
req.payload.body && req.payload.body.devTools
|
|
|
|
? 'Allowed method: [GET]'
|
|
|
|
: `Forbidden (${req.payload.method} ${req.payload.path}`,
|
|
|
|
3029,
|
|
|
|
400,
|
|
|
|
reply
|
|
|
|
);
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
|
|
|
if (req.payload.body.devTools) {
|
2019-01-14 10:45:25 +00:00
|
|
|
//delete req.payload.body.devTools;
|
2018-09-10 08:32:49 +00:00
|
|
|
const keyRegex = new RegExp(/.*agents\/\d*\/key.*/);
|
|
|
|
if (
|
|
|
|
typeof req.payload.path === 'string' &&
|
2018-10-30 13:21:00 +00:00
|
|
|
keyRegex.test(req.payload.path) &&
|
|
|
|
!adminMode
|
2018-09-10 08:32:49 +00:00
|
|
|
) {
|
|
|
|
return ErrorResponse(
|
|
|
|
'Forbidden route /agents/<id>/key',
|
|
|
|
3028,
|
|
|
|
400,
|
|
|
|
reply
|
|
|
|
);
|
2018-06-19 14:20:00 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
|
|
|
return this.makeRequest(
|
|
|
|
req.payload.method,
|
|
|
|
req.payload.path,
|
|
|
|
req.payload.body,
|
|
|
|
req.payload.id,
|
|
|
|
reply
|
|
|
|
);
|
2018-06-19 14:20:00 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
|
|
|
|
2018-12-11 16:12:59 +00:00
|
|
|
/**
|
2018-12-13 10:48:23 +00:00
|
|
|
* Fetch agent status and insert it directly on demand
|
2018-12-13 10:02:53 +00:00
|
|
|
* @param {Object} req
|
|
|
|
* @param {Object} reply
|
2018-12-12 11:47:05 +00:00
|
|
|
* @returns {Object} status obj or ErrorResponse
|
2018-12-11 16:12:59 +00:00
|
|
|
*/
|
2018-09-10 08:32:49 +00:00
|
|
|
async fetchAgents(req, reply) {
|
|
|
|
try {
|
2018-12-13 10:48:23 +00:00
|
|
|
const output = await this.monitoringInstance.fetchAgentsExternal();
|
2019-01-31 14:41:51 +00:00
|
|
|
return {
|
2018-09-10 08:32:49 +00:00
|
|
|
statusCode: 200,
|
|
|
|
error: '0',
|
|
|
|
data: '',
|
|
|
|
output
|
2019-01-31 14:41:51 +00:00
|
|
|
};
|
2018-09-10 08:32:49 +00:00
|
|
|
} catch (error) {
|
|
|
|
return ErrorResponse(error.message || error, 3018, 500, reply);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get full data on CSV format from a list Wazuh API endpoint
|
2018-12-11 16:12:59 +00:00
|
|
|
* @param {Object} req
|
|
|
|
* @param {Object} res
|
2018-12-12 11:47:05 +00:00
|
|
|
* @returns {Object} csv or ErrorResponse
|
2018-09-10 08:32:49 +00:00
|
|
|
*/
|
|
|
|
async csv(req, reply) {
|
|
|
|
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');
|
|
|
|
|
2018-12-18 15:19:05 +00:00
|
|
|
const filters = Array.isArray(((req || {}).payload || {}).filters)
|
|
|
|
? req.payload.filters
|
|
|
|
: [];
|
2018-09-10 08:32:49 +00:00
|
|
|
|
|
|
|
const config = await this.wzWrapper.getWazuhConfigurationById(
|
|
|
|
req.payload.id
|
|
|
|
);
|
|
|
|
|
|
|
|
let path_tmp = req.payload.path;
|
|
|
|
|
|
|
|
if (path_tmp && typeof path_tmp === 'string') {
|
|
|
|
path_tmp = path_tmp[0] === '/' ? path_tmp.substr(1) : path_tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!path_tmp) throw new Error('An error occurred parsing path field');
|
|
|
|
|
|
|
|
// Real limit, regardless the user query
|
2019-01-15 12:05:06 +00:00
|
|
|
const params = { limit: 500 };
|
2018-09-10 08:32:49 +00:00
|
|
|
|
|
|
|
if (filters.length) {
|
|
|
|
for (const filter of filters) {
|
|
|
|
if (!filter.name || !filter.value) continue;
|
|
|
|
params[filter.name] = filter.value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-13 11:30:28 +00:00
|
|
|
const cred = ApiHelper.buildOptionsObject(config);
|
2018-09-10 08:32:49 +00:00
|
|
|
|
|
|
|
const itemsArray = [];
|
|
|
|
const output = await needle(
|
|
|
|
'get',
|
|
|
|
`${config.url}:${config.port}/${path_tmp}`,
|
|
|
|
params,
|
|
|
|
cred
|
|
|
|
);
|
2018-12-28 15:34:38 +00:00
|
|
|
if ((((output || {}).body || {}).data || {}).totalItems) {
|
2018-09-10 08:32:49 +00:00
|
|
|
params.offset = 0;
|
|
|
|
const { totalItems } = output.body.data;
|
|
|
|
itemsArray.push(...output.body.data.items);
|
2019-01-15 12:02:26 +00:00
|
|
|
while (itemsArray.length < totalItems && params.offset < totalItems) {
|
2018-09-10 08:32:49 +00:00
|
|
|
params.offset += params.limit;
|
|
|
|
const tmpData = await needle(
|
|
|
|
'get',
|
|
|
|
`${config.url}:${config.port}/${path_tmp}`,
|
|
|
|
params,
|
|
|
|
cred
|
|
|
|
);
|
|
|
|
itemsArray.push(...tmpData.body.data.items);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-28 15:34:38 +00:00
|
|
|
if ((((output || {}).body || {}).data || {}).totalItems) {
|
2018-11-27 11:03:29 +00:00
|
|
|
const fields = req.payload.path.includes('/agents')
|
|
|
|
? [
|
2019-02-08 11:31:17 +00:00
|
|
|
'id',
|
|
|
|
'status',
|
|
|
|
'name',
|
|
|
|
'ip',
|
|
|
|
'group',
|
|
|
|
'manager',
|
|
|
|
'node_name',
|
|
|
|
'dateAdd',
|
|
|
|
'version',
|
|
|
|
'lastKeepAlive',
|
|
|
|
'os'
|
|
|
|
]
|
2018-11-27 11:03:29 +00:00
|
|
|
: Object.keys(output.body.data.items[0]);
|
2018-09-10 08:32:49 +00:00
|
|
|
|
|
|
|
const json2csvParser = new Parser({ fields });
|
|
|
|
let csv = json2csvParser.parse(itemsArray);
|
|
|
|
|
|
|
|
for (const field of fields) {
|
|
|
|
if (csv.includes(field)) {
|
2018-09-11 09:52:54 +00:00
|
|
|
csv = csv.replace(field, KeyEquivalenece[field] || field);
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
|
|
|
}
|
2018-06-19 14:20:00 +00:00
|
|
|
|
2019-01-30 17:28:08 +00:00
|
|
|
return reply.response(csv).type('text/csv');
|
2018-09-10 08:32:49 +00:00
|
|
|
} 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 ErrorResponse(error.message || error, 3034, 500, reply);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-11 16:12:59 +00:00
|
|
|
/**
|
|
|
|
* Get the each filed unique values of agents
|
2018-12-13 10:02:53 +00:00
|
|
|
* @param {Object} req
|
|
|
|
* @param {Object} reply
|
2018-12-12 11:47:05 +00:00
|
|
|
* @returns {Array<Object>} unique fileds or ErrorResponse
|
2018-12-11 16:12:59 +00:00
|
|
|
*/
|
2018-09-10 08:32:49 +00:00
|
|
|
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
|
|
|
|
);
|
|
|
|
|
2018-12-13 11:30:28 +00:00
|
|
|
const headers = ApiHelper.buildOptionsObject(config);
|
2018-09-10 08:32:49 +00:00
|
|
|
|
2018-10-22 14:51:20 +00:00
|
|
|
const distinctUrl = `${config.url}:${config.port}/agents/stats/distinct`;
|
2018-09-10 08:32:49 +00:00
|
|
|
|
|
|
|
const data = await Promise.all([
|
|
|
|
needle(
|
|
|
|
'get',
|
2018-10-22 14:51:20 +00:00
|
|
|
distinctUrl,
|
2018-09-10 08:32:49 +00:00
|
|
|
{ fields: 'node_name', select: 'node_name' },
|
|
|
|
headers
|
|
|
|
),
|
|
|
|
needle(
|
|
|
|
'get',
|
2018-10-26 15:35:53 +00:00
|
|
|
`${config.url}:${config.port}/agents/groups`,
|
|
|
|
{},
|
2018-09-10 08:32:49 +00:00
|
|
|
headers
|
|
|
|
),
|
|
|
|
needle(
|
|
|
|
'get',
|
2018-10-22 14:51:20 +00:00
|
|
|
distinctUrl,
|
2018-09-10 08:32:49 +00:00
|
|
|
{
|
|
|
|
fields: 'os.name,os.platform,os.version',
|
|
|
|
select: 'os.name,os.platform,os.version'
|
|
|
|
},
|
|
|
|
headers
|
|
|
|
),
|
|
|
|
needle(
|
|
|
|
'get',
|
2018-10-22 14:51:20 +00:00
|
|
|
distinctUrl,
|
2018-09-10 08:32:49 +00:00
|
|
|
{ fields: 'version', select: 'version' },
|
|
|
|
headers
|
|
|
|
),
|
|
|
|
needle(
|
|
|
|
'get',
|
|
|
|
`${config.url}:${config.port}/agents/summary`,
|
|
|
|
{},
|
|
|
|
headers
|
|
|
|
),
|
|
|
|
needle(
|
|
|
|
'get',
|
|
|
|
`${config.url}:${config.port}/agents`,
|
|
|
|
{ limit: 1, sort: '-dateAdd' },
|
|
|
|
headers
|
|
|
|
)
|
|
|
|
]);
|
|
|
|
|
2018-12-11 12:28:14 +00:00
|
|
|
const parsedResponses = data.map(item =>
|
2018-11-27 11:03:29 +00:00
|
|
|
item && item.body && item.body.data && !item.body.error
|
|
|
|
? item.body.data
|
|
|
|
: false
|
2018-10-22 14:51:20 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
const [
|
|
|
|
nodes,
|
|
|
|
groups,
|
|
|
|
osPlatforms,
|
|
|
|
versions,
|
|
|
|
summary,
|
|
|
|
lastAgent
|
|
|
|
] = parsedResponses;
|
|
|
|
|
2018-09-10 08:32:49 +00:00
|
|
|
const result = {
|
|
|
|
groups: [],
|
|
|
|
nodes: [],
|
|
|
|
versions: [],
|
|
|
|
osPlatforms: [],
|
|
|
|
lastAgent: {},
|
|
|
|
summary: {
|
|
|
|
agentsCountActive: 0,
|
|
|
|
agentsCountDisconnected: 0,
|
|
|
|
agentsCountNeverConnected: 0,
|
|
|
|
agentsCountTotal: 0,
|
|
|
|
agentsCoverity: 0
|
2018-07-17 07:41:53 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
};
|
|
|
|
|
2018-10-22 14:51:20 +00:00
|
|
|
if (nodes && nodes.items) {
|
|
|
|
result.nodes = nodes.items
|
2019-01-16 11:51:57 +00:00
|
|
|
.filter(item => !!item.node_name && item.node_name !== 'unknown')
|
2018-09-10 08:32:49 +00:00
|
|
|
.map(item => item.node_name);
|
|
|
|
}
|
|
|
|
|
2018-10-22 14:51:20 +00:00
|
|
|
if (groups && groups.items) {
|
2018-10-26 15:35:53 +00:00
|
|
|
result.groups = groups.items.map(item => item.name);
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
|
|
|
|
2018-10-22 14:51:20 +00:00
|
|
|
if (osPlatforms && osPlatforms.items) {
|
|
|
|
result.osPlatforms = osPlatforms.items
|
2018-09-10 08:32:49 +00:00
|
|
|
.filter(
|
|
|
|
item =>
|
|
|
|
!!item.os && item.os.platform && item.os.name && item.os.version
|
|
|
|
)
|
|
|
|
.map(item => item.os);
|
|
|
|
}
|
|
|
|
|
2018-10-22 14:51:20 +00:00
|
|
|
if (versions && versions.items) {
|
|
|
|
result.versions = versions.items
|
2018-09-10 08:32:49 +00:00
|
|
|
.filter(item => !!item.version)
|
|
|
|
.map(item => item.version);
|
|
|
|
}
|
|
|
|
|
2018-10-22 14:51:20 +00:00
|
|
|
if (summary) {
|
2018-09-10 08:32:49 +00:00
|
|
|
Object.assign(result.summary, {
|
2018-10-22 14:51:20 +00:00
|
|
|
agentsCountActive: summary.Active - 1,
|
|
|
|
agentsCountDisconnected: summary.Disconnected,
|
|
|
|
agentsCountNeverConnected: summary['Never connected'],
|
|
|
|
agentsCountTotal: summary.Total - 1,
|
2018-09-10 08:32:49 +00:00
|
|
|
agentsCoverity:
|
2018-10-22 14:51:20 +00:00
|
|
|
summary.Total - 1
|
|
|
|
? ((summary.Active - 1) / (summary.Total - 1)) * 100
|
2018-09-10 08:32:49 +00:00
|
|
|
: 0
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-10-22 14:51:20 +00:00
|
|
|
if (lastAgent && lastAgent.items && lastAgent.items.length) {
|
|
|
|
Object.assign(result.lastAgent, lastAgent.items[0]);
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
|
|
|
|
2019-01-31 14:41:51 +00:00
|
|
|
return { error: 0, result };
|
2018-09-10 08:32:49 +00:00
|
|
|
} catch (error) {
|
|
|
|
return ErrorResponse(error.message || error, 3035, 500, reply);
|
2018-07-17 07:41:53 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
2018-11-16 12:59:47 +00:00
|
|
|
|
|
|
|
// Get de list of available requests in the API
|
2019-01-30 11:36:25 +00:00
|
|
|
getRequestList() {
|
2018-11-16 12:59:47 +00:00
|
|
|
//Read a static JSON until the api call has implemented
|
2019-01-30 11:36:25 +00:00
|
|
|
return apiRequestList;
|
2018-11-16 12:59:47 +00:00
|
|
|
}
|
2018-04-11 07:58:23 +00:00
|
|
|
}
|