diff --git a/plugins/main/common/config-equivalences.js b/plugins/main/common/config-equivalences.js index 83aebea711..529a0346b2 100644 --- a/plugins/main/common/config-equivalences.js +++ b/plugins/main/common/config-equivalences.js @@ -37,7 +37,6 @@ export const configEquivalences = { 'wazuh.monitoring.pattern': 'Default index pattern to use for Wazuh monitoring.', hideManagerAlerts: 'Hide the alerts of the manager in every dashboard.', - 'logs.level': 'Logging level of the App.', 'enrollment.dns': 'Specifies the Wazuh registration server, used for the agent enrollment.', 'enrollment.password': @@ -85,7 +84,6 @@ export const nameEquivalence = { 'wazuh.monitoring.creation': 'Index creation', 'wazuh.monitoring.pattern': 'Index pattern', hideManagerAlerts: 'Hide manager alerts', - 'logs.level': 'Log level', 'enrollment.dns': 'Enrollment DNS', 'cron.prefix': 'Cron prefix', 'cron.statistics.status': 'Status', @@ -142,7 +140,6 @@ export const categoriesEquivalence = { 'wazuh.monitoring.creation': MONITORING, 'wazuh.monitoring.pattern': MONITORING, hideManagerAlerts: GENERAL, - 'logs.level': GENERAL, 'enrollment.dns': GENERAL, 'cron.prefix': GENERAL, 'cron.statistics.status': STATISTICS, @@ -197,15 +194,6 @@ export const formEquivalence = { }, 'wazuh.monitoring.pattern': { type: TEXT }, hideManagerAlerts: { type: BOOLEAN }, - 'logs.level': { - type: LIST, - params: { - options: [ - { text: 'Info', value: 'info' }, - { text: 'Debug', value: 'debug' }, - ], - }, - }, 'enrollment.dns': { type: TEXT }, 'cron.prefix': { type: TEXT }, 'cron.statistics.status': { type: BOOLEAN }, diff --git a/plugins/main/common/constants.ts b/plugins/main/common/constants.ts index 463a67395a..8fcce9ea44 100644 --- a/plugins/main/common/constants.ts +++ b/plugins/main/common/constants.ts @@ -140,35 +140,6 @@ export const WAZUH_DATA_CONFIG_REGISTRY_PATH = path.join( 'wazuh-registry.json', ); -// Wazuh data path - logs -export const MAX_MB_LOG_FILES = 100; -export const WAZUH_DATA_LOGS_DIRECTORY_PATH = path.join( - WAZUH_DATA_ABSOLUTE_PATH, - 'logs', -); -export const WAZUH_DATA_LOGS_PLAIN_FILENAME = 'wazuhapp-plain.log'; -export const WAZUH_DATA_LOGS_PLAIN_PATH = path.join( - WAZUH_DATA_LOGS_DIRECTORY_PATH, - WAZUH_DATA_LOGS_PLAIN_FILENAME, -); -export const WAZUH_DATA_LOGS_RAW_FILENAME = 'wazuhapp.log'; -export const WAZUH_DATA_LOGS_RAW_PATH = path.join( - WAZUH_DATA_LOGS_DIRECTORY_PATH, - WAZUH_DATA_LOGS_RAW_FILENAME, -); - -// Wazuh data path - UI logs -export const WAZUH_UI_LOGS_PLAIN_FILENAME = 'wazuh-ui-plain.log'; -export const WAZUH_UI_LOGS_RAW_FILENAME = 'wazuh-ui.log'; -export const WAZUH_UI_LOGS_PLAIN_PATH = path.join( - WAZUH_DATA_LOGS_DIRECTORY_PATH, - WAZUH_UI_LOGS_PLAIN_FILENAME, -); -export const WAZUH_UI_LOGS_RAW_PATH = path.join( - WAZUH_DATA_LOGS_DIRECTORY_PATH, - WAZUH_UI_LOGS_RAW_FILENAME, -); - // Wazuh data path - downloads export const WAZUH_DATA_DOWNLOADS_DIRECTORY_PATH = path.join( WAZUH_DATA_ABSOLUTE_PATH, @@ -1506,38 +1477,6 @@ export const PLUGIN_SETTINGS: { [key: string]: TPluginSetting } = { return schema.boolean(); }, }, - 'logs.level': { - title: 'Log level', - description: 'Logging level of the App.', - category: SettingCategory.GENERAL, - type: EpluginSettingType.select, - options: { - select: [ - { - text: 'Info', - value: 'info', - }, - { - text: 'Debug', - value: 'debug', - }, - ], - }, - defaultValue: 'info', - isConfigurableFromFile: true, - isConfigurableFromUI: true, - requiresRestartingPluginPlatform: true, - validate: function (value) { - return SettingsValidator.literal( - this.options.select.map(({ value }) => value), - )(value); - }, - validateBackend: function (schema) { - return schema.oneOf( - this.options.select.map(({ value }) => schema.literal(value)), - ); - }, - }, pattern: { title: 'Index pattern', description: diff --git a/plugins/main/common/plugin-settings.test.ts b/plugins/main/common/plugin-settings.test.ts index 2ede6f322f..959f5f9d7d 100644 --- a/plugins/main/common/plugin-settings.test.ts +++ b/plugins/main/common/plugin-settings.test.ts @@ -151,9 +151,6 @@ describe('[settings] Input validation', () => { ${'ip.ignore'} | ${['test', 'test#']} | ${'It can\'t contain invalid characters: \\, /, ?, ", <, >, |, ,, #.'} ${'ip.selector'} | ${true} | ${undefined} ${'ip.selector'} | ${''} | ${'It should be a boolean. Allowed values: true or false.'} - ${'logs.level'} | ${'info'} | ${undefined} - ${'logs.level'} | ${'debug'} | ${undefined} - ${'logs.level'} | ${''} | ${'Invalid value. Allowed values: info, debug.'} ${'pattern'} | ${'test'} | ${undefined} ${'pattern'} | ${'test*'} | ${undefined} ${'pattern'} | ${''} | ${'Value can not be empty.'} diff --git a/plugins/main/common/wazu-menu/wz-menu-settings.cy.ts b/plugins/main/common/wazu-menu/wz-menu-settings.cy.ts index 4d1007a72f..9b8fc0dc76 100644 --- a/plugins/main/common/wazu-menu/wz-menu-settings.cy.ts +++ b/plugins/main/common/wazu-menu/wz-menu-settings.cy.ts @@ -16,7 +16,6 @@ export enum WAZUH_MENU_SETTINGS_SECTIONS_CY_TEST_ID { MODULES = 'menuSettingsModulesLink', SAMPLE_DATA = 'menuSettingsSampleDataLink', CONFIGURATION = 'menuSettingsConfigurationLink', - LOGS = 'menuSettingsLogsLink', MISCELLANEOUS = 'menuSettingsMiscellaneousLink', ABOUT = 'menuSettingsAboutLink', } diff --git a/plugins/main/opensearch_dashboards.json b/plugins/main/opensearch_dashboards.json index c22c97b0b7..d2beb8d5a5 100644 --- a/plugins/main/opensearch_dashboards.json +++ b/plugins/main/opensearch_dashboards.json @@ -18,6 +18,7 @@ "opensearchDashboardsUtils", "opensearchDashboardsLegacy", "wazuhCheckUpdates", + "wazuhCore", "wazuhEndpoints" ], "optionalPlugins": [ diff --git a/plugins/main/package.json b/plugins/main/package.json index 0641b43211..9f1cc623d6 100644 --- a/plugins/main/package.json +++ b/plugins/main/package.json @@ -64,8 +64,7 @@ "react-cookie": "^4.0.3", "read-last-lines": "^1.7.2", "timsort": "^0.3.0", - "typescript": "^5.0.4", - "winston": "3.9.0" + "typescript": "^5.0.4" }, "devDependencies": { "@types/node-cron": "^2.0.3", @@ -89,4 +88,4 @@ "redux-mock-store": "^1.5.4", "swagger-client": "^3.19.11" } -} \ No newline at end of file +} diff --git a/plugins/main/public/components/security/main.tsx b/plugins/main/public/components/security/main.tsx index ed17f3667e..9d54666232 100644 --- a/plugins/main/public/components/security/main.tsx +++ b/plugins/main/public/components/security/main.tsx @@ -12,7 +12,6 @@ import { Users } from './users/users'; import { Roles } from './roles/roles'; import { Policies } from './policies/policies'; import { GenericRequest } from '../../react-services/generic-request'; -import { API_USER_STATUS_RUN_AS } from '../../../server/lib/cache-api-user-has-run-as'; import { AppState } from '../../react-services/app-state'; import { RolesMapping } from './roles-mapping/roles-mapping'; import { @@ -30,6 +29,7 @@ import { UI_ERROR_SEVERITIES } from '../../react-services/error-orchestrator/typ import { getErrorOrchestrator } from '../../react-services/common-services'; import { getPluginDataPath } from '../../../common/plugin'; import { security } from '../../utils/applications'; +import { getWazuhCorePlugin } from '../../kibana-services'; const tabs = [ { @@ -128,16 +128,16 @@ export const WzSecurity = compose( const isNotRunAs = allowRunAs => { let runAsWarningTxt = ''; switch (allowRunAs) { - case API_USER_STATUS_RUN_AS.HOST_DISABLED: + case getWazuhCorePlugin().API_USER_STATUS_RUN_AS.HOST_DISABLED: runAsWarningTxt = `For the role mapping to take effect, enable run_as in ${getPluginDataPath( 'config/wazuh.yml', )} configuration file, restart the ${PLUGIN_PLATFORM_NAME} service and clear your browser cache and cookies.`; break; - case API_USER_STATUS_RUN_AS.USER_NOT_ALLOWED: + case getWazuhCorePlugin().API_USER_STATUS_RUN_AS.USER_NOT_ALLOWED: runAsWarningTxt = 'The role mapping has no effect because the current Wazuh API user has allow_run_as disabled.'; break; - case API_USER_STATUS_RUN_AS.ALL_DISABLED: + case getWazuhCorePlugin().API_USER_STATUS_RUN_AS.ALL_DISABLED: runAsWarningTxt = `For the role mapping to take effect, enable run_as in ${getPluginDataPath( 'config/wazuh.yml', )} configuration file and set the current Wazuh API user allow_run_as to true. Restart the ${PLUGIN_PLATFORM_NAME} service and clear your browser cache and cookies.`; @@ -174,7 +174,8 @@ export const WzSecurity = compose( {selectedTabId === 'roleMapping' && ( <> {allowRunAs !== undefined && - allowRunAs !== API_USER_STATUS_RUN_AS.ENABLED && + allowRunAs !== + getWazuhCorePlugin().API_USER_STATUS_RUN_AS.ENABLED && isNotRunAs(allowRunAs)} diff --git a/plugins/main/public/components/settings/api/api-table.js b/plugins/main/public/components/settings/api/api-table.js index 53e6056e33..607fbc416b 100644 --- a/plugins/main/public/components/settings/api/api-table.js +++ b/plugins/main/public/components/settings/api/api-table.js @@ -29,19 +29,21 @@ import { } from '@elastic/eui'; import { WzButtonPermissions } from '../../common/permissions/button'; import { AppState } from '../../../react-services/app-state'; -import { API_USER_STATUS_RUN_AS } from '../../../../server/lib/cache-api-user-has-run-as'; import { withErrorBoundary, withReduxProvider } from '../../common/hocs'; import { compose } from 'redux'; import { UI_ERROR_SEVERITIES } from '../../../react-services/error-orchestrator/types'; import { UI_LOGGER_LEVELS } from '../../../../common/constants'; import { getErrorOrchestrator } from '../../../react-services/common-services'; -import { getWazuhCheckUpdatesPlugin } from '../../../kibana-services'; +import { + getWazuhCheckUpdatesPlugin, + getWazuhCorePlugin, +} from '../../../kibana-services'; import { AvailableUpdatesFlyout } from './available-updates-flyout'; import { formatUIDate } from '../../../react-services/time-service'; export const ApiTable = compose( withErrorBoundary, - withReduxProvider + withReduxProvider, )( class ApiTable extends Component { constructor(props) { @@ -62,7 +64,9 @@ export const ApiTable = compose( async getApisAvailableUpdates(forceUpdate = false) { try { this.setState({ refreshingAvailableUpdates: true }); - const availableUpdates = await this.state.getAvailableUpdates(forceUpdate); + const availableUpdates = await this.state.getAvailableUpdates( + forceUpdate, + ); this.setState({ availableUpdates }); } catch (error) { const options = { @@ -73,7 +77,9 @@ export const ApiTable = compose( error: { error: error, message: error.message || error, - title: `Error checking available updates: ${error.message || error}`, + title: `Error checking available updates: ${ + error.message || error + }`, }, }; @@ -141,7 +147,12 @@ export const ApiTable = compose( refreshingEntries: false, }); } catch (error) { - if (error && error.data && error.data.message && error.data.code === 2001) { + if ( + error && + error.data && + error.data.message && + error.data.code === 2001 + ) { this.props.showAddApiWithInitialError(error); } } @@ -154,7 +165,7 @@ export const ApiTable = compose( async checkApi(api) { try { const entries = this.state.apiEntries; - const idx = entries.map((e) => e.id).indexOf(api.id); + const idx = entries.map(e => e.id).indexOf(api.id); try { await this.props.checkManager(api); entries[idx].status = 'online'; @@ -183,7 +194,9 @@ export const ApiTable = compose( error: { error: error, message: error.message || error, - title: `Error checking manager connection: ${error.message || error}`, + title: `Error checking manager connection: ${ + error.message || error + }`, }, }; @@ -213,13 +226,14 @@ export const ApiTable = compose( }, }; - const isLoading = this.state.refreshingEntries || this.state.refreshingAvailableUpdates; + const isLoading = + this.state.refreshingEntries || this.state.refreshingAvailableUpdates; const items = [ - ...this.state.apiEntries?.map((apiEntry) => { + ...this.state.apiEntries?.map(apiEntry => { const versionData = this.state.availableUpdates?.apis_available_updates?.find( - (apiAvailableUpdates) => apiAvailableUpdates.api_id === apiEntry.id + apiAvailableUpdates => apiAvailableUpdates.api_id === apiEntry.id, ) || {}; return { @@ -272,44 +286,56 @@ export const ApiTable = compose( name: 'Status', align: 'left', sortable: true, - render: (item) => { + render: item => { if (item) { return item === 'online' ? ( - + Online ) : item.status === 'down' ? ( - + - + Warning - + this.props.copyToClipBoard(item.downReason)} + color='primary' + iconType='questionInCircle' + aria-label='Info about the error' + onClick={() => + this.props.copyToClipBoard(item.downReason) + } /> ) : ( - + - + Offline - + this.props.copyToClipBoard(item.downReason)} + color='primary' + iconType='questionInCircle' + aria-label='Info about the error' + onClick={() => + this.props.copyToClipBoard(item.downReason) + } /> @@ -318,7 +344,7 @@ export const ApiTable = compose( } else { return ( - +   Checking ); @@ -346,26 +372,42 @@ export const ApiTable = compose( if (!this.state.refreshingAvailableUpdates) { return ( - + - + {getContent()} {item === 'availableUpdates' ? ( - View available updates

}> + View available updates

} + > this.setState({ apiAvailableUpdateDetails: api })} + aria-label='Availabe updates' + iconType='eye' + onClick={() => + this.setState({ apiAvailableUpdateDetails: api }) + } />
) : null} {item === 'error' && api.error?.detail ? ( - + - +   Checking ); @@ -393,20 +435,22 @@ export const ApiTable = compose( align: 'center', sortable: true, width: '80px', - render: (value) => { - return value === API_USER_STATUS_RUN_AS.ENABLED ? ( + render: value => { + return value === + getWazuhCorePlugin().API_USER_STATUS_RUN_AS.ENABLED ? ( - + - ) : value === API_USER_STATUS_RUN_AS.USER_NOT_ALLOWED ? ( + ) : value === + getWazuhCorePlugin().API_USER_STATUS_RUN_AS.USER_NOT_ALLOWED ? ( - + ) : ( '' @@ -415,15 +459,19 @@ export const ApiTable = compose( }, { name: 'Actions', - render: (item) => ( + render: item => ( Set as default

}} - iconType={item.id === this.props.currentDefault ? 'starFilled' : 'starEmpty'} - aria-label="Set as default" + iconType={ + item.id === this.props.currentDefault + ? 'starFilled' + : 'starEmpty' + } + aria-label='Set as default' onClick={async () => { const currentDefault = await this.props.setDefault(item); this.setState({ @@ -433,12 +481,12 @@ export const ApiTable = compose( />
- Check connection

}> + Check connection

}> await this.checkApi(item)} - color="success" + color='success' />
@@ -456,8 +504,8 @@ export const ApiTable = compose( return ( - - + + @@ -469,8 +517,8 @@ export const ApiTable = compose( this.props.showAddApi()} > @@ -478,26 +526,33 @@ export const ApiTable = compose( - await this.refresh()}> + await this.refresh()} + > Refresh await this.getApisAvailableUpdates(true)} > Check updates{' '} - + @@ -508,32 +563,34 @@ export const ApiTable = compose( - - From here you can manage and configure the API entries. You can also check their - connection and status. + + From here you can manage and configure the API entries. You + can also check their connection and status. this.setState({ apiAvailableUpdateDetails: undefined })} + onClose={() => + this.setState({ apiAvailableUpdateDetails: undefined }) + } /> ); } - } + }, ); ApiTable.propTypes = { diff --git a/plugins/main/public/components/settings/settings-logs/logs.js b/plugins/main/public/components/settings/settings-logs/logs.js deleted file mode 100644 index b173905e3d..0000000000 --- a/plugins/main/public/components/settings/settings-logs/logs.js +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Wazuh app - React component building the API entries table. - * - * Copyright (C) 2015-2022 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 React, { Component } from 'react'; -import { - EuiFlexGroup, - EuiFlexItem, - EuiCodeBlock, - EuiPanel, - EuiPage, - EuiButtonEmpty, - EuiTitle, - EuiText, - EuiProgress, -} from '@elastic/eui'; - -import { formatUIDate } from '../../../react-services/time-service'; -import { withErrorBoundary } from '../../common/hocs'; -import { getPluginDataPath } from '../../../../common/plugin'; - -class SettingsLogs extends Component { - constructor(props) { - super(props); - this.state = { - logs: [], - refreshingEntries: false, - }; - this.HEIGHT_WITHOUT_CODE_EDITOR = 325; - } - - componentDidMount() { - this._isMounted = true; - this.refresh(); - } - - componentWillUnmount() { - this._isMounted = false; - } - - async refresh() { - this.setState({ - refreshingEntries: true, - }); - const logs = await this.props.getLogs(); - this._isMounted && - this.setState({ - refreshingEntries: false, - logs, - }); - } - - formatDate(date) { - return formatUIDate(date) - .replace('-', '/') - .replace('T', ' ') - .replace('Z', '') - .split('.')[0]; - } - - getMessage(log) { - const data = log.data || log.message; - return typeof data === 'object' - ? data.message || JSON.stringify(data) - : data.toString(); - } - - render() { - let text = ''; - (this.state.logs || []).forEach(x => { - text = - text + - (this.formatDate(x.date) + - ' ' + - x.level.toUpperCase() + - ' ' + - this.getMessage(x) + - '\n'); - }); - return ( - - - - - - - -

App log messages

-
-
-
-
- - await this.refresh()} - > - Refresh - - -
- - Log file located at {getPluginDataPath('logs/wazuhapp.log')} - - {this.state.refreshingEntries && ( - - )} - {!this.state.refreshingEntries && ( -
- - {text} - -
- )} -
-
- ); - } -} - -export default withErrorBoundary(SettingsLogs); diff --git a/plugins/main/public/controllers/settings/index.js b/plugins/main/public/controllers/settings/index.js index 6908d4af39..34300f298a 100644 --- a/plugins/main/public/controllers/settings/index.js +++ b/plugins/main/public/controllers/settings/index.js @@ -14,7 +14,6 @@ import { ApiTable } from '../../components/settings/api/api-table'; import { AddApi } from '../../components/settings/api/add-api'; import { ApiIsDown } from '../../components/settings/api/api-is-down'; import { WzConfigurationSettings } from '../../components/settings/configuration/configuration'; -import SettingsLogs from '../../components/settings/settings-logs/logs'; import { SettingsMiscellaneous } from '../../components/settings/miscellaneous/miscellaneous'; import { WzSampleDataWrapper } from '../../components/add-modules-data/WzSampleDataWrapper'; import { getAngularModule } from '../../kibana-services'; @@ -24,7 +23,6 @@ const app = getAngularModule(); WzSampleDataWrapper.displayName = 'WzSampleDataWrapper'; WzConfigurationSettings.displayName = 'WzConfigurationSettings'; -SettingsLogs.displayName = 'SettingsLogs'; SettingsMiscellaneous.displayName = 'SettingsMiscellaneous'; ApiTable.displayName = 'ApiTable'; AddApi.displayName = 'AddApi'; @@ -35,7 +33,6 @@ app .controller('settingsController', SettingsController) .value('WzSampleDataWrapper', WzSampleDataWrapper) .value('WzConfigurationSettings', WzConfigurationSettings) - .value('SettingsLogs', SettingsLogs) .value('SettingsMiscelaneous', SettingsMiscellaneous) .value('ApiTable', ApiTable) .value('AddApi', AddApi) diff --git a/plugins/main/public/controllers/settings/settings.js b/plugins/main/public/controllers/settings/settings.js index b405243fb0..c5897f5733 100644 --- a/plugins/main/public/controllers/settings/settings.js +++ b/plugins/main/public/controllers/settings/settings.js @@ -156,9 +156,6 @@ export class SettingsController { this.settingsTabsProps = { clickAction: tab => { this.switchTab(tab, true); - if (tab === 'logs') { - this.refreshLogs(); - } }, selectedTab: this.tab || 'api', // Define tabs for Wazuh plugin settings application @@ -166,12 +163,6 @@ export class SettingsController { getWzCurrentAppID() === appSettings.id ? this.tabsConfiguration : null, wazuhConfig: this.wazuhConfig, }; - - this.settingsLogsProps = { - getLogs: async () => { - return await this.getAppLogs(); - }, - }; } /** @@ -432,37 +423,6 @@ export class SettingsController { else this.messageErrorUpdate = text; } - /** - * Returns Wazuh app logs - */ - async getAppLogs() { - try { - const logs = await this.genericReq.request('GET', '/utils/logs', {}); - this.$scope.$applyAsync(); - return logs.data.lastLogs.map(item => JSON.parse(item)); - } catch (error) { - const options = { - context: `${SettingsController.name}.getAppLogs`, - level: UI_LOGGER_LEVELS.ERROR, - severity: UI_ERROR_SEVERITIES.BUSINESS, - error: { - error: error, - message: error.message || error, - title: error.name || error, - }, - }; - getErrorOrchestrator().handleError(options); - - return [ - { - date: new Date(), - level: 'error', - message: 'Error when loading logs', - }, - ]; - } - } - /** * Returns Wazuh app info */ @@ -482,10 +442,6 @@ export class SettingsController { const pattern = AppState.getCurrentPattern(); this.selectedIndexPattern = pattern || config['pattern']; - if (this.tab === 'logs') { - this.getAppLogs(); - } - this.getCurrentAPIIndex(); if ( (this.currentApiEntryIndex || this.currentApiEntryIndex === 0) && @@ -510,13 +466,6 @@ export class SettingsController { } } - /** - * This ask again for wazuh logs - */ - refreshLogs() { - return this.getAppLogs(); - } - /** * Checks if there are new APIs entries in the wazuh.yml */ diff --git a/plugins/main/public/controllers/settings/settings.test.ts b/plugins/main/public/controllers/settings/settings.test.ts index 88f1300e20..40b423cde9 100644 --- a/plugins/main/public/controllers/settings/settings.test.ts +++ b/plugins/main/public/controllers/settings/settings.test.ts @@ -1,10 +1,9 @@ -import { ApiCheck, AppState, formatUIDate } from '../../react-services'; +import { ApiCheck, formatUIDate } from '../../react-services'; import { SettingsController } from './settings'; import { ErrorHandler } from '../../react-services/error-management'; import { UI_LOGGER_LEVELS } from '../../../common/constants'; import { UI_ERROR_SEVERITIES } from '../../react-services/error-orchestrator/types'; -import { ManageHosts } from '../../../server/lib/manage-hosts'; import axios, { AxiosResponse } from 'axios'; jest.mock('../../react-services/time-service'); jest.mock('../../react-services/app-state'); @@ -155,8 +154,18 @@ describe('Settings Controller', () => { ); controller.getSettings = jest.fn().mockResolvedValue([]); // mocking manager hosts - apiEntries from wazuh.yml - const manageHosts = new ManageHosts(); - controller.apiEntries = await manageHosts.getHosts(); + + controller.apiEntries = [ + { + manager: { + url: 'https://wazuh.manager', + port: 55000, + username: 'wazuh-wui', + password: 'mypassword1-', + run_as: false, + }, + }, + ]; await controller.$onInit(); expect(mockedGetErrorOrchestrator.handleError).toBeCalledTimes(1); expect(mockedGetErrorOrchestrator.handleError).toBeCalledWith( diff --git a/plugins/main/public/factories/wazuh-config.js b/plugins/main/public/factories/wazuh-config.js index 97940ff949..5a08473755 100644 --- a/plugins/main/public/factories/wazuh-config.js +++ b/plugins/main/public/factories/wazuh-config.js @@ -31,11 +31,4 @@ export class WazuhConfig { getConfig() { return this.config; } - - /** - * Returns true if debug level is enabled, otherwise it returns false. - */ - isDebug() { - return ((this.config || {})['logs.level'] || false) === 'debug'; - } } diff --git a/plugins/main/public/kibana-services.ts b/plugins/main/public/kibana-services.ts index 1257f238ed..1c536dc5e1 100644 --- a/plugins/main/public/kibana-services.ts +++ b/plugins/main/public/kibana-services.ts @@ -45,6 +45,8 @@ export const [getWzCurrentAppID, setWzCurrentAppID] = createGetterSetter('WzCurrentAppID'); export const [getWazuhCheckUpdatesPlugin, setWazuhCheckUpdatesPlugin] = createGetterSetter('WazuhCheckUpdatesPlugin'); +export const [getWazuhCorePlugin, setWazuhCorePlugin] = + createGetterSetter('WazuhCorePlugin'); export const [getHeaderActionMenuMounter, setHeaderActionMenuMounter] = createGetterSetter( 'headerActionMenuMounter', diff --git a/plugins/main/public/plugin.ts b/plugins/main/public/plugin.ts index ab5ea70129..313a2edbc3 100644 --- a/plugins/main/public/plugin.ts +++ b/plugins/main/public/plugin.ts @@ -24,6 +24,7 @@ import { setWzCurrentAppID, setWazuhCheckUpdatesPlugin, setHeaderActionMenuMounter, + setWazuhCorePlugin, } from './kibana-services'; import { AppPluginStartDependencies, @@ -56,7 +57,10 @@ export class WazuhPlugin public initializeInnerAngular?: () => void; private innerAngularInitialized: boolean = false; private hideTelemetryBanner?: () => void; - public async setup(core: CoreSetup, plugins: WazuhSetupPlugins): WazuhSetup { + public async setup( + core: CoreSetup, + plugins: WazuhSetupPlugins, + ): Promise { // Get custom logos configuration to start up the app with the correct logos let logosInitialState = {}; try { @@ -221,6 +225,7 @@ export class WazuhPlugin setOverlays(core.overlays); setErrorOrchestrator(ErrorOrchestratorService); setWazuhCheckUpdatesPlugin(plugins.wazuhCheckUpdates); + setWazuhCorePlugin(plugins.wazuhCore); return {}; } } diff --git a/plugins/main/public/react-services/wazuh-config.js b/plugins/main/public/react-services/wazuh-config.js index 689ca382af..21f6dfe686 100644 --- a/plugins/main/public/react-services/wazuh-config.js +++ b/plugins/main/public/react-services/wazuh-config.js @@ -10,8 +10,8 @@ * Find more information about this on the LICENSE file. */ -import store from "../redux/store"; -import { updateAppConfig } from "../redux/actions/appConfigActions"; +import store from '../redux/store'; +import { updateAppConfig } from '../redux/actions/appConfigActions'; export class WazuhConfig { constructor() { @@ -20,7 +20,6 @@ export class WazuhConfig { } WazuhConfig.instance = this; - return this; } @@ -29,7 +28,7 @@ export class WazuhConfig { * @param {Object} cfg */ setConfig(cfg) { - store.dispatch(updateAppConfig({...cfg})); + store.dispatch(updateAppConfig({ ...cfg })); } /** @@ -38,11 +37,4 @@ export class WazuhConfig { getConfig() { return store.getState().appConfig.data; } - - /** - * Returns true if debug level is enabled, otherwise it returns false. - */ - isDebug() { - return ((this.getConfig() || {})['logs.level'] || false) === 'debug'; - } } diff --git a/plugins/main/public/templates/settings/settings.html b/plugins/main/public/templates/settings/settings.html index 04460ef309..3493e99bb0 100644 --- a/plugins/main/public/templates/settings/settings.html +++ b/plugins/main/public/templates/settings/settings.html @@ -1,7 +1,10 @@
- +
@@ -9,7 +12,10 @@ ng-if="!ctrl.load && ctrl.settingsTabsProps && !ctrl.apiIsDown && ctrl.apiTableProps.apiEntries.length && ctrl.settingsTabsProps.tabs" class="wz-margin-top-16 md-margin-h" > - +
@@ -17,17 +23,35 @@
- +
-
- +
+
-
- +
+
@@ -40,11 +64,6 @@ >
- -
- -
-
diff --git a/plugins/main/public/types.ts b/plugins/main/public/types.ts index d4edcbb66a..647791058f 100644 --- a/plugins/main/public/types.ts +++ b/plugins/main/public/types.ts @@ -5,13 +5,20 @@ import { VisualizationsSetup, VisualizationsStart, } from '../../../src/plugins/visualizations/public'; -import { DataPublicPluginSetup, DataPublicPluginStart } from '../../../src/plugins/data/public'; +import { + DataPublicPluginSetup, + DataPublicPluginStart, +} from '../../../src/plugins/data/public'; import { NavigationPublicPluginStart } from '../../../src/plugins/navigation/public'; import { UiActionsSetup } from '../../../src/plugins/ui_actions/public'; import { SecurityOssPluginStart } from '../../../src/plugins/security_oss/public/'; import { SavedObjectsStart } from '../../../src/plugins/saved_objects/public'; -import { TelemetryPluginStart, TelemetryPluginSetup } from '../../../src/plugins/telemetry/public'; +import { + TelemetryPluginStart, + TelemetryPluginSetup, +} from '../../../src/plugins/telemetry/public'; import { WazuhCheckUpdatesPluginStart } from '../../wazuh-check-updates/public'; +import { WazuhCorePluginStart } from '../../wazuh-core/public'; import { WazuhEndpointsPluginStart } from '../../wazuh-endpoints/public'; import { DashboardStart } from '../../../src/plugins/dashboard/public'; @@ -25,6 +32,7 @@ export interface AppPluginStartDependencies { savedObjects: SavedObjectsStart; telemetry: TelemetryPluginStart; wazuhCheckUpdates: WazuhCheckUpdatesPluginStart; + wazuhCore: WazuhCorePluginStart; wazuhEndpoints: WazuhEndpointsPluginStart; dashboard: DashboardStart; } diff --git a/plugins/main/public/utils/applications.ts b/plugins/main/public/utils/applications.ts index aa675cd06b..bbf791d96b 100644 --- a/plugins/main/public/utils/applications.ts +++ b/plugins/main/public/utils/applications.ts @@ -713,22 +713,6 @@ export const appSettings = { redirectTo: () => '/settings?tab=configuration', }; -const appLogs = { - category: 'management', - id: 'app-logs', - title: i18n.translate('wz-app-app-logs-title', { - defaultMessage: 'App Logs', - }), - description: i18n.translate('wz-app-app-logs-description', { - defaultMessage: 'Explore the logs related to the applications.', - }), - euiIconType: 'indexRollupApp', - order: 8904, - showInOverviewApp: false, - showInAgentMenu: false, - redirectTo: () => '/settings?tab=logs', -}; - const about = { category: 'management', id: 'about', @@ -782,7 +766,6 @@ export const Applications = [ serverApis, sampleData, appSettings, - appLogs, about, ].sort((a, b) => { // Sort applications by order diff --git a/plugins/main/server/controllers/wazuh-api.ts b/plugins/main/server/controllers/wazuh-api.ts index cc7bf6cf69..0f366841ed 100644 --- a/plugins/main/server/controllers/wazuh-api.ts +++ b/plugins/main/server/controllers/wazuh-api.ts @@ -13,7 +13,6 @@ // Require some libraries import { ErrorResponse } from '../lib/error-response'; import { Parser } from 'json2csv'; -import { log } from '../lib/logger'; import { KeyEquivalence } from '../../common/csv-key-equivalence'; import { ApiErrorEquivalence } from '../lib/api-errors-equivalence'; import apiRequestList from '../../common/api-info/endpoints'; @@ -21,31 +20,17 @@ import { HTTP_STATUS_CODES } from '../../common/constants'; import { getCustomizationSetting } from '../../common/services/settings'; import { addJobToQueue } from '../start/queue'; import fs from 'fs'; -import { ManageHosts } from '../lib/manage-hosts'; -import { UpdateRegistry } from '../lib/update-registry'; import jwtDecode from 'jwt-decode'; import { OpenSearchDashboardsRequest, RequestHandlerContext, OpenSearchDashboardsResponseFactory, } from 'src/core/server'; -import { - APIUserAllowRunAs, - CacheInMemoryAPIUserAllowRunAs, - API_USER_STATUS_RUN_AS, -} from '../lib/cache-api-user-has-run-as'; import { getCookieValueByName } from '../lib/cookie'; -import { SecurityObj } from '../lib/security-factory'; import { getConfiguration } from '../lib/get-configuration'; export class WazuhApiCtrl { - manageHosts: ManageHosts; - updateRegistry: UpdateRegistry; - - constructor() { - this.manageHosts = new ManageHosts(); - this.updateRegistry = new UpdateRegistry(); - } + constructor() {} async getToken( context: RequestHandlerContext, @@ -82,14 +67,16 @@ export class WazuhApiCtrl { }); } } catch (error) { - log('wazuh-api:getToken', error.message || error); + context.wazuh.logger.error( + `Error decoding the API host entry token: ${error.message}`, + ); } } } let token; if ( - (await APIUserAllowRunAs.canUse(idHost)) == - API_USER_STATUS_RUN_AS.ENABLED + (await context.wazuh_core.cacheAPIUserAllowRunAs.canUse(idHost)) === + context.wazuh_core.cacheAPIUserAllowRunAs.API_USER_STATUS_RUN_AS.ENABLED ) { token = await context.wazuh.api.client.asCurrentUser.authenticate( idHost, @@ -116,11 +103,12 @@ export class WazuhApiCtrl { body: { token }, }); } catch (error) { - const errorMessage = - ((error.response || {}).data || {}).detail || error.message || error; - log('wazuh-api:getToken', errorMessage); + const errorMessage = `Error getting the authorization token: ${ + ((error.response || {}).data || {}).detail || error.message || error + }`; + context.wazuh.logger.error(errorMessage); return ErrorResponse( - `Error getting the authorization token: ${errorMessage}`, + errorMessage, 3000, error?.response?.status || HTTP_STATUS_CODES.INTERNAL_SERVER_ERROR, response, @@ -143,13 +131,17 @@ export class WazuhApiCtrl { try { // Get config from wazuh.yml const id = request.body.id; - const api = await this.manageHosts.getHostById(id); + context.wazuh.logger.debug(`Getting server API host by ID: ${id}`); + const api = await context.wazuh_core.manageHosts.getHostById(id); + context.wazuh.logger.debug( + `Server API host data: ${JSON.stringify(api)}`, + ); // Check Manage Hosts if (!Object.keys(api).length) { - throw new Error('Could not find Wazuh API entry on wazuh.yml'); + throw new Error('Could not find server API entry in the configuration'); } - log('wazuh-api:checkStoredAPI', `${id} exists`, 'debug'); + context.wazuh.logger.debug(`${id} exists`); // Fetch needed information about the cluster and the manager itself const responseManagerInfo = @@ -161,7 +153,7 @@ export class WazuhApiCtrl { ); // Look for socket-related errors - if (this.checkResponseIsDown(responseManagerInfo)) { + if (this.checkResponseIsDown(context, responseManagerInfo)) { return ErrorResponse( `ERROR3099 - ${ responseManagerInfo.data.detail || 'Wazuh not ready yet' @@ -240,7 +232,10 @@ export class WazuhApiCtrl { if (api.cluster_info) { // Update cluster information in the wazuh-registry.json - await this.updateRegistry.updateClusterInfo(id, api.cluster_info); + await context.wazuh_core.updateRegistry.updateClusterInfo( + id, + api.cluster_info, + ); // Hide Wazuh API secret, username, password const copied = { ...api }; @@ -280,7 +275,7 @@ export class WazuhApiCtrl { }); } else { try { - const apis = await this.manageHosts.getHosts(); + const apis = await context.wazuh_core.manageHosts.getHosts(); for (const api of apis) { try { const id = Object.keys(api)[0]; @@ -293,7 +288,7 @@ export class WazuhApiCtrl { { apiHostID: id }, ); - if (this.checkResponseIsDown(responseManagerInfo)) { + if (this.checkResponseIsDown(context, responseManagerInfo)) { return ErrorResponse( `ERROR3099 - ${ response.data.detail || 'Wazuh not ready yet' @@ -311,7 +306,7 @@ export class WazuhApiCtrl { } catch (error) {} // eslint-disable-line } } catch (error) { - log('wazuh-api:checkStoredAPI', error.message || error); + context.wazuh.logger.error(error.message || error); return ErrorResponse( error.message || error, 3020, @@ -319,7 +314,7 @@ export class WazuhApiCtrl { response, ); } - log('wazuh-api:checkStoredAPI', error.message || error); + context.wazuh.logger.error(error.message || error); return ErrorResponse( error.message || error, 3002, @@ -374,15 +369,18 @@ export class WazuhApiCtrl { let apiAvailable = null; // const notValid = this.validateCheckApiParams(request.body); // if (notValid) return ErrorResponse(notValid, 3003, HTTP_STATUS_CODES.INTERNAL_SERVER_ERROR, response); - log('wazuh-api:checkAPI', `${request.body.id} is valid`, 'debug'); + context.wazuh.logger.debug(`${request.body.id} is valid`); // Check if a Wazuh API id is given (already stored API) - const data = await this.manageHosts.getHostById(request.body.id); + const data = await context.wazuh_core.manageHosts.getHostById( + request.body.id, + ); if (data) { apiAvailable = data; } else { - log('wazuh-api:checkAPI', `API ${request.body.id} not found`); + const errorMessage = `The server API host entry with ID ${request.body.id} was not found`; + context.wazuh.logger.debug(errorMessage); return ErrorResponse( - `The API ${request.body.id} was not found`, + errorMessage, 3029, HTTP_STATUS_CODES.INTERNAL_SERVER_ERROR, response, @@ -411,12 +409,7 @@ export class WazuhApiCtrl { response, ); } - - log( - 'wazuh-api:checkAPI', - `${request.body.id} credentials are valid`, - 'debug', - ); + context.wazuh.logger.debug(`${request.body.id} credentials are valid`); if ( responseManagerInfo.status === HTTP_STATUS_CODES.OK && responseManagerInfo.data @@ -442,7 +435,9 @@ export class WazuhApiCtrl { ); // Check the run_as for the API user and update it - let apiUserAllowRunAs = API_USER_STATUS_RUN_AS.ALL_DISABLED; + let apiUserAllowRunAs = + context.wazuh_core.cacheAPIUserAllowRunAs.API_USER_STATUS_RUN_AS + .ALL_DISABLED; const responseApiUserAllowRunAs = await context.wazuh.api.client.asInternalUser.request( 'GET', @@ -457,29 +452,33 @@ export class WazuhApiCtrl { if (allow_run_as && apiAvailable && apiAvailable.run_as) // HOST AND USER ENABLED - apiUserAllowRunAs = API_USER_STATUS_RUN_AS.ENABLED; + apiUserAllowRunAs = + context.wazuh_core.cacheAPIUserAllowRunAs.API_USER_STATUS_RUN_AS + .ENABLED; else if (!allow_run_as && apiAvailable && apiAvailable.run_as) // HOST ENABLED AND USER DISABLED - apiUserAllowRunAs = API_USER_STATUS_RUN_AS.USER_NOT_ALLOWED; + apiUserAllowRunAs = + context.wazuh_core.cacheAPIUserAllowRunAs.API_USER_STATUS_RUN_AS + .USER_NOT_ALLOWED; else if (allow_run_as && (!apiAvailable || !apiAvailable.run_as)) // USER ENABLED AND HOST DISABLED - apiUserAllowRunAs = API_USER_STATUS_RUN_AS.HOST_DISABLED; + apiUserAllowRunAs = + context.wazuh_core.cacheAPIUserAllowRunAs.API_USER_STATUS_RUN_AS + .HOST_DISABLED; else if (!allow_run_as && (!apiAvailable || !apiAvailable.run_as)) // HOST AND USER DISABLED - apiUserAllowRunAs = API_USER_STATUS_RUN_AS.ALL_DISABLED; + apiUserAllowRunAs = + context.wazuh_core.cacheAPIUserAllowRunAs.API_USER_STATUS_RUN_AS + .ALL_DISABLED; } - CacheInMemoryAPIUserAllowRunAs.set( + context.wazuh_core.cacheAPIUserAllowRunAs.set( request.body.id, apiAvailable.username, apiUserAllowRunAs, ); if (responseCluster.status === HTTP_STATUS_CODES.OK) { - log( - 'wazuh-api:checkStoredAPI', - `Wazuh API response is valid`, - 'debug', - ); + context.wazuh.logger.debug('Wazuh API response is valid'); if (responseCluster.data.data.enabled === 'yes') { // If cluster mode is active let responseClusterLocal = @@ -517,7 +516,7 @@ export class WazuhApiCtrl { } } } catch (error) { - log('wazuh-api:checkAPI', error.message || error); + context.wazuh.logger.warn(error.message || error); if ( error && @@ -561,7 +560,7 @@ export class WazuhApiCtrl { } } - checkResponseIsDown(response) { + checkResponseIsDown(context, response) { if (response.status !== HTTP_STATUS_CODES.OK) { // Avoid "Error communicating with socket" like errors const socketErrorCodes = [1013, 1014, 1017, 1018, 1019]; @@ -569,8 +568,7 @@ export class WazuhApiCtrl { const isDown = socketErrorCodes.includes(status); isDown && - log( - 'wazuh-api:makeRequest', + context.wazuh.logger.error( 'Wazuh API is online but Wazuh is not ready yet', ); @@ -612,7 +610,7 @@ export class WazuhApiCtrl { const isValid = execd && modulesd && wazuhdb && clusterd; - isValid && log('wazuh-api:checkDaemons', `Wazuh is ready`, 'debug'); + isValid && context.wazuh.logger.debug('Wazuh is ready'); if (path === '/ping') { return { isValid }; @@ -622,7 +620,7 @@ export class WazuhApiCtrl { throw new Error('Wazuh not ready yet'); } } catch (error) { - log('wazuh-api:checkDaemons', error.message || error); + context.wazuh.logger.error(error.message || error); return Promise.reject(error); } } @@ -667,13 +665,13 @@ export class WazuhApiCtrl { async makeRequest(context, method, path, data, id, response) { const devTools = !!(data || {}).devTools; try { - const api = await this.manageHosts.getHostById(id); + const api = await context.wazuh_core.manageHosts.getHostById(id); if (devTools) { delete data.devTools; } if (!Object.keys(api).length) { - log('wazuh-api:makeRequest', 'Could not get host credentials'); + context.wazuh.logger.error('Could not get host credentials'); //Can not get credentials from wazuh-hosts return ErrorResponse( 'Could not get host credentials', @@ -723,7 +721,7 @@ export class WazuhApiCtrl { if (delay) { addJobToQueue({ startAt: new Date(Date.now() + delay), - run: async () => { + run: async contextJob => { try { await context.wazuh.api.client.asCurrentUser.request( method, @@ -732,8 +730,7 @@ export class WazuhApiCtrl { options, ); } catch (error) { - log( - 'queue:delayApiRequest', + contextJob.wazuh.logger.error( `An error ocurred in the delayed request: "${method} ${path}": ${ error.message || error }`, @@ -753,8 +750,7 @@ export class WazuhApiCtrl { } catch (error) { const isDown = (error || {}).code === 'ECONNREFUSED'; if (!isDown) { - log( - 'wazuh-api:makeRequest', + context.wazuh.logger.error( 'Wazuh API is online but Wazuh is not ready yet', ); return ErrorResponse( @@ -767,7 +763,7 @@ export class WazuhApiCtrl { } } - log('wazuh-api:makeRequest', `${method} ${path}`, 'debug'); + context.wazuh.logger.debug(`${method} ${path}`); // Extract keys from parameters const dataProperties = Object.keys(data); @@ -790,7 +786,7 @@ export class WazuhApiCtrl { data, options, ); - const responseIsDown = this.checkResponseIsDown(responseToken); + const responseIsDown = this.checkResponseIsDown(context, responseToken); if (responseIsDown) { return ErrorResponse( `ERROR3099 - ${response.body.message || 'Wazuh not ready yet'}`, @@ -841,7 +837,7 @@ export class WazuhApiCtrl { ); } const errorMsg = (error.response || {}).data || error.message; - log('wazuh-api:makeRequest', errorMsg || error); + context.wazuh.logger.error(errorMsg || error); if (devTools) { return response.ok({ body: { error: '3013', message: errorMsg || error }, @@ -890,7 +886,7 @@ export class WazuhApiCtrl { response, ); } else if (!request.body.method.match(/^(?:GET|PUT|POST|DELETE)$/)) { - log('wazuh-api:makeRequest', 'Request method is not valid.'); + context.wazuh.logger.error('Request method is not valid.'); //Method is not a valid HTTP request method return ErrorResponse( 'Request method is not valid.', @@ -906,7 +902,7 @@ export class WazuhApiCtrl { response, ); } else if (!request.body.path.startsWith('/')) { - log('wazuh-api:makeRequest', 'Request path is not valid.'); + context.wazuh.logger.error('Request path is not valid.'); //Path doesn't start with '/' return ErrorResponse( 'Request path is not valid.', @@ -955,7 +951,7 @@ export class WazuhApiCtrl { if (!tmpPath) throw new Error('An error occurred parsing path field'); - log('wazuh-api:csv', `Report ${tmpPath}`, 'debug'); + context.wazuh.logger.debug(`Report ${tmpPath}`); // Real limit, regardless the user query const params = { limit: 500 }; @@ -1088,7 +1084,7 @@ export class WazuhApiCtrl { ); } } catch (error) { - log('wazuh-api:csv', error.message || error); + context.wazuh.logger.error(error.message || error); return ErrorResponse( error.message || error, 3034, @@ -1124,13 +1120,11 @@ export class WazuhApiCtrl { ) { try { const source = JSON.parse( - fs.readFileSync(this.updateRegistry.file, 'utf8'), + fs.readFileSync(context.wazuh_core.updateRegistry.file, 'utf8'), ); if (source.installationDate && source.lastRestart) { - log( - 'wazuh-api:getTimeStamp', + context.wazuh.logger.debug( `Installation date: ${source.installationDate}. Last restart: ${source.lastRestart}`, - 'debug', ); return response.ok({ body: { @@ -1142,7 +1136,7 @@ export class WazuhApiCtrl { throw new Error('Could not fetch wazuh-version registry'); } } catch (error) { - log('wazuh-api:getTimeStamp', error.message || error); + context.wazuh.logger.error(error.message || error); return ErrorResponse( error.message || 'Could not fetch wazuh-version registry', 4001, @@ -1166,7 +1160,7 @@ export class WazuhApiCtrl { ) { try { const source = JSON.parse( - fs.readFileSync(this.updateRegistry.file, 'utf8'), + fs.readFileSync(context.wazuh_core.updateRegistry.file, 'utf8'), ); return response.ok({ body: { @@ -1175,7 +1169,7 @@ export class WazuhApiCtrl { }, }); } catch (error) { - log('wazuh-api:getSetupInfo', error.message || error); + context.wazuh.logger.error(error.message || error); return ErrorResponse( `Could not get data from wazuh-version registry due to ${ error.message || error @@ -1242,7 +1236,7 @@ export class WazuhApiCtrl { body: syscollector, }); } catch (error) { - log('wazuh-api:getSyscollector', error.message || error); + context.wazuh.logger.error(error.message || error); return ErrorResponse( error.message || error, 3035, @@ -1280,7 +1274,7 @@ export class WazuhApiCtrl { body: { logos }, }); } catch (error) { - log('wazuh-api:getAppLogos', error.message || error); + context.wazuh.logger.error(error.message || error); return ErrorResponse( error.message || error, 3035, diff --git a/plugins/main/server/controllers/wazuh-elastic.ts b/plugins/main/server/controllers/wazuh-elastic.ts index cdcfb23d41..63550e4900 100644 --- a/plugins/main/server/controllers/wazuh-elastic.ts +++ b/plugins/main/server/controllers/wazuh-elastic.ts @@ -10,7 +10,6 @@ * Find more information about this on the LICENSE file. */ import { ErrorResponse } from '../lib/error-response'; -import { log } from '../lib/logger'; import { getConfiguration } from '../lib/get-configuration'; import { AgentsVisualizations, @@ -25,13 +24,10 @@ import { WAZUH_SAMPLE_ALERTS_INDEX_REPLICAS, } from '../../common/constants'; import jwtDecode from 'jwt-decode'; -import { ManageHosts } from '../lib/manage-hosts'; import { OpenSearchDashboardsRequest, RequestHandlerContext, OpenSearchDashboardsResponseFactory, - SavedObject, - SavedObjectsFindResponse, } from 'src/core/server'; import { getCookieValueByName } from '../lib/cookie'; import { @@ -43,10 +39,8 @@ import { WAZUH_INDEXER_NAME } from '../../common/constants'; export class WazuhElasticCtrl { wzSampleAlertsIndexPrefix: string; - manageHosts: ManageHosts; constructor() { this.wzSampleAlertsIndexPrefix = this.getSampleAlertPrefix(); - this.manageHosts = new ManageHosts(); } /** @@ -125,14 +119,12 @@ export class WazuhElasticCtrl { item = lastChar === '*' ? item.slice(0, -1) : item; return item.includes(pattern) || pattern.includes(item); }); - log( - 'wazuh-elastic:getTemplate', + context.wazuh.logger.debug( `Template is valid: ${ isIncluded && Array.isArray(isIncluded) && isIncluded.length ? 'yes' : 'no' }`, - 'debug', ); return isIncluded && Array.isArray(isIncluded) && isIncluded.length ? response.ok({ @@ -150,7 +142,7 @@ export class WazuhElasticCtrl { }, }); } catch (error) { - log('wazuh-elastic:getTemplate', error.message || error); + context.wazuh.logger.error(error.message || error); return ErrorResponse( `Could not retrieve templates from ${WAZUH_INDEXER_NAME} due to ${ error.message || error @@ -254,7 +246,7 @@ export class WazuhElasticCtrl { }, }); } catch (error) { - log('wazuh-elastic:getFieldTop', error.message || error); + context.wazuh.logger.error(error.message || error); return ErrorResponse(error.message || error, 4004, 500, response); } } @@ -333,7 +325,7 @@ export class WazuhElasticCtrl { }, }); } catch (error) { - log('wazuh-elastic:getCurrentPlatform', error.message || error); + context.wazuh.logger.error(error.message || error); return ErrorResponse(error.message || error, 4011, 500, response); } } @@ -343,85 +335,68 @@ export class WazuhElasticCtrl { * @param {Array} app_objects Object containing raw visualizations. * @param {String} id Index-pattern id to use in the visualizations. Eg: 'wazuh-alerts' */ - async buildVisualizationsRaw(app_objects, id, namespace = false) { - try { - const config = getConfiguration(); - let monitoringPattern = - (config || {})['wazuh.monitoring.pattern'] || - getSettingDefaultValue('wazuh.monitoring.pattern'); - log( - 'wazuh-elastic:buildVisualizationsRaw', - `Building ${app_objects.length} visualizations`, - 'debug', - ); - log( - 'wazuh-elastic:buildVisualizationsRaw', - `Index pattern ID: ${id}`, - 'debug', - ); - const visArray = []; - let aux_source, bulk_content; - for (let element of app_objects) { - aux_source = JSON.parse(JSON.stringify(element._source)); - - // Replace index-pattern for visualizations - if ( - aux_source && - aux_source.kibanaSavedObjectMeta && - aux_source.kibanaSavedObjectMeta.searchSourceJSON && - typeof aux_source.kibanaSavedObjectMeta.searchSourceJSON === 'string' - ) { - const defaultStr = aux_source.kibanaSavedObjectMeta.searchSourceJSON; - - const isMonitoring = defaultStr.includes('wazuh-monitoring'); - if (isMonitoring) { - if (namespace && namespace !== 'default') { - if ( - monitoringPattern.includes(namespace) && - monitoringPattern.includes('index-pattern:') - ) { - monitoringPattern = - monitoringPattern.split('index-pattern:')[1]; - } + buildVisualizationsRaw(context, app_objects, id, namespace = false) { + const config = getConfiguration(); + let monitoringPattern = + (config || {})['wazuh.monitoring.pattern'] || + getSettingDefaultValue('wazuh.monitoring.pattern'); + context.wazuh.logger.debug(`Building ${app_objects.length} visualizations`); + context.wazuh.logger.debug(`Index pattern ID: ${id}`); + const visArray = []; + let aux_source, bulk_content; + for (let element of app_objects) { + aux_source = JSON.parse(JSON.stringify(element._source)); + + // Replace index-pattern for visualizations + if ( + aux_source && + aux_source.kibanaSavedObjectMeta && + aux_source.kibanaSavedObjectMeta.searchSourceJSON && + typeof aux_source.kibanaSavedObjectMeta.searchSourceJSON === 'string' + ) { + const defaultStr = aux_source.kibanaSavedObjectMeta.searchSourceJSON; + + const isMonitoring = defaultStr.includes('wazuh-monitoring'); + if (isMonitoring) { + if (namespace && namespace !== 'default') { + if ( + monitoringPattern.includes(namespace) && + monitoringPattern.includes('index-pattern:') + ) { + monitoringPattern = monitoringPattern.split('index-pattern:')[1]; } - aux_source.kibanaSavedObjectMeta.searchSourceJSON = - defaultStr.replace( - /wazuh-monitoring/g, - monitoringPattern[monitoringPattern.length - 1] === '*' || - (namespace && namespace !== 'default') - ? monitoringPattern - : monitoringPattern + '*', - ); - } else { - aux_source.kibanaSavedObjectMeta.searchSourceJSON = - defaultStr.replace(/wazuh-alerts/g, id); } + aux_source.kibanaSavedObjectMeta.searchSourceJSON = + defaultStr.replace( + /wazuh-monitoring/g, + monitoringPattern[monitoringPattern.length - 1] === '*' || + (namespace && namespace !== 'default') + ? monitoringPattern + : monitoringPattern + '*', + ); + } else { + aux_source.kibanaSavedObjectMeta.searchSourceJSON = + defaultStr.replace(/wazuh-alerts/g, id); } + } - // Replace index-pattern for selector visualizations - if (typeof (aux_source || {}).visState === 'string') { - aux_source.visState = aux_source.visState.replace( - /wazuh-alerts/g, - id, - ); - } + // Replace index-pattern for selector visualizations + if (typeof (aux_source || {}).visState === 'string') { + aux_source.visState = aux_source.visState.replace(/wazuh-alerts/g, id); + } - // Bulk source - bulk_content = {}; - bulk_content[element._type] = aux_source; + // Bulk source + bulk_content = {}; + bulk_content[element._type] = aux_source; - visArray.push({ - attributes: bulk_content.visualization, - type: element._type, - id: element._id, - _version: bulk_content.visualization.version, - }); - } - return visArray; - } catch (error) { - log('wazuh-elastic:buildVisualizationsRaw', error.message || error); - return Promise.reject(error); + visArray.push({ + attributes: bulk_content.visualization, + type: element._type, + id: element._id, + _version: bulk_content.visualization.version, + }); } + return visArray; } /** @@ -433,6 +408,7 @@ export class WazuhElasticCtrl { * @param {String} master_node Master node name. Eg: 'node01' */ buildClusterVisualizationsRaw( + context, app_objects, id, nodes = [], @@ -493,10 +469,7 @@ export class WazuhElasticCtrl { return visArray; } catch (error) { - log( - 'wazuh-elastic:buildClusterVisualizationsRaw', - error.message || error, - ); + context.wazuh.logger.error(error.message || error); return Promise.reject(error); } } @@ -539,12 +512,11 @@ export class WazuhElasticCtrl { }, }); } - log( - 'wazuh-elastic:createVis', + context.wazuh.logger.debug( `${tabPrefix}[${tabSufix}] with index pattern ${request.params.pattern}`, - 'debug', ); const raw = await this.buildVisualizationsRaw( + context, file, request.params.pattern, ); @@ -552,7 +524,7 @@ export class WazuhElasticCtrl { body: { acknowledge: true, raw: raw }, }); } catch (error) { - log('wazuh-elastic:createVis', error.message || error); + context.wazuh.logger.error(error.message || error); return ErrorResponse(error.message || error, 4007, 500, response); } } @@ -596,6 +568,7 @@ export class WazuhElasticCtrl { const { id: patternID, title: patternName } = request.body.pattern; const raw = await this.buildClusterVisualizationsRaw( + context, file, patternID, nodes, @@ -608,7 +581,7 @@ export class WazuhElasticCtrl { body: { acknowledge: true, raw: raw }, }); } catch (error) { - log('wazuh-elastic:createClusterVis', error.message || error); + context.wazuh.logger.error(error.message || error); return ErrorResponse(error.message || error, 4009, 500, response); } } @@ -673,8 +646,7 @@ export class WazuhElasticCtrl { body: { index: sampleAlertsIndex, exists: existsSampleIndex.body }, }); } catch (error) { - log( - 'wazuh-elastic:haveSampleAlertsOfCategory', + context.wazuh.logger.error( `Error checking if there are sample alerts indices: ${ error.message || error }`, @@ -795,28 +767,21 @@ export class WazuhElasticCtrl { index: sampleAlertsIndex, body: configuration, }); - log( - 'wazuh-elastic:createSampleAlerts', - `Created ${sampleAlertsIndex} index`, - 'debug', - ); + context.wazuh.logger.info(`Index ${sampleAlertsIndex} created`); } await context.core.opensearch.client.asCurrentUser.bulk({ index: sampleAlertsIndex, body: bulk, }); - log( - 'wazuh-elastic:createSampleAlerts', + context.wazuh.logger.info( `Added sample alerts to ${sampleAlertsIndex} index`, - 'debug', ); return response.ok({ body: { index: sampleAlertsIndex, alertCount: sampleAlerts.length }, }); } catch (error) { - log( - 'wazuh-elastic:createSampleAlerts', + context.wazuh.logger.error( `Error adding sample alerts to ${sampleAlertsIndex} index: ${ error.message || error }`, @@ -887,11 +852,7 @@ export class WazuhElasticCtrl { await context.core.opensearch.client.asCurrentUser.indices.delete({ index: sampleAlertsIndex, }); - log( - 'wazuh-elastic:deleteSampleAlerts', - `Deleted ${sampleAlertsIndex} index`, - 'debug', - ); + context.wazuh.logger.info(`Deleted ${sampleAlertsIndex} index`); return response.ok({ body: { result: 'deleted', index: sampleAlertsIndex }, }); @@ -904,8 +865,7 @@ export class WazuhElasticCtrl { ); } } catch (error) { - log( - 'wazuh-elastic:deleteSampleAlerts', + context.wazuh.logger.error( `Error deleting sample alerts of ${sampleAlertsIndex} index: ${ error.message || error }`, @@ -929,7 +889,7 @@ export class WazuhElasticCtrl { body: data.body, }); } catch (error) { - log('wazuh-elastic:alerts', error.message || error); + context.wazuh.logger.error(error.message || error); return ErrorResponse(error.message || error, 4010, 500, response); } } @@ -954,7 +914,7 @@ export class WazuhElasticCtrl { body: existIndex.body, }); } catch (error) { - log('wazuh-elastic:existsStatisticsIndices', error.message || error); + context.wazuh.logger.error(error.message || error); return ErrorResponse(error.message || error, 1000, 500, response); } } diff --git a/plugins/main/server/controllers/wazuh-hosts.ts b/plugins/main/server/controllers/wazuh-hosts.ts index 9d6a1d1779..52cc3cfcce 100644 --- a/plugins/main/server/controllers/wazuh-hosts.ts +++ b/plugins/main/server/controllers/wazuh-hosts.ts @@ -21,19 +21,10 @@ import { PLUGIN_PLATFORM_NAME, WAZUH_DATA_PLUGIN_PLATFORM_BASE_ABSOLUTE_PATH, } from '../../common/constants'; -import { APIUserAllowRunAs } from '../lib/cache-api-user-has-run-as'; import { ErrorResponse } from '../lib/error-response'; -import { log } from '../lib/logger'; -import { ManageHosts } from '../lib/manage-hosts'; -import { UpdateRegistry } from '../lib/update-registry'; export class WazuhHostsCtrl { - manageHosts: ManageHosts; - updateRegistry: UpdateRegistry; - constructor() { - this.manageHosts = new ManageHosts(); - this.updateRegistry = new UpdateRegistry(); - } + constructor() {} /** * This get all hosts entries in the wazuh.yml and the related info in the wazuh-registry.json @@ -42,57 +33,23 @@ export class WazuhHostsCtrl { * @param {Object} response * API entries or ErrorResponse */ - async getHostsEntries(context: RequestHandlerContext, request: OpenSearchDashboardsRequest, response: OpenSearchDashboardsResponseFactory) { + async getHostsEntries( + context: RequestHandlerContext, + request: OpenSearchDashboardsRequest, + response: OpenSearchDashboardsResponseFactory, + ) { try { - const removePassword = true; - const hosts = await this.manageHosts.getHosts(); - const registry = await this.updateRegistry.getHosts(); - const result = await this.joinHostRegistry(hosts, registry, removePassword); + const result = + await context.wazuh_core.serverAPIHostEntries.getHostsEntries(); return response.ok({ - body: result + body: result, }); } catch (error) { - if(error && error.message && ['ENOENT: no such file or directory', WAZUH_DATA_PLUGIN_PLATFORM_BASE_ABSOLUTE_PATH].every(text => error.message.includes(text))){ - return response.badRequest({ - body: { - message: `Error getting the hosts entries: The \'${WAZUH_DATA_PLUGIN_PLATFORM_BASE_ABSOLUTE_PATH}\' directory could not exist in your ${PLUGIN_PLATFORM_NAME} installation. - If this doesn't exist, create it and give the permissions 'sudo mkdir ${WAZUH_DATA_PLUGIN_PLATFORM_BASE_ABSOLUTE_PATH};sudo chown -R ${PLUGIN_PLATFORM_INSTALLATION_USER}:${PLUGIN_PLATFORM_INSTALLATION_USER_GROUP} ${WAZUH_DATA_PLUGIN_PLATFORM_BASE_ABSOLUTE_PATH}'. After, restart the ${PLUGIN_PLATFORM_NAME} service.` - } - }) - } - log('wazuh-hosts:getHostsEntries', error.message || error); + context.wazuh.logger.error(error.message || error); return ErrorResponse(error.message || error, 2001, 500, response); } } - /** - * Joins the hosts with the related information in the registry - * @param {Object} hosts - * @param {Object} registry - * @param {Boolean} removePassword - */ - async joinHostRegistry(hosts: any, registry: any, removePassword: boolean = true) { - try { - if (!Array.isArray(hosts)) { - throw new Error('Hosts configuration error in wazuh.yml'); - } - - return await Promise.all(hosts.map(async h => { - const id = Object.keys(h)[0]; - const api = Object.assign(h[id], { id: id }); - const host = Object.assign(api, registry[id]); - // Add to run_as from API user. Use the cached value or get it doing a request - host.allow_run_as = await APIUserAllowRunAs.check(id); - if (removePassword) { - delete host.password; - delete host.token; - }; - return host; - })); - } catch (error) { - throw new Error(error); - } - } /** * This update an API hostname * @param {Object} context @@ -100,26 +57,31 @@ export class WazuhHostsCtrl { * @param {Object} response * Status response or ErrorResponse */ - async updateClusterInfo(context: RequestHandlerContext, request: OpenSearchDashboardsRequest, response: OpenSearchDashboardsResponseFactory) { + async updateClusterInfo( + context: RequestHandlerContext, + request: OpenSearchDashboardsRequest, + response: OpenSearchDashboardsResponseFactory, + ) { try { const { id } = request.params; const { cluster_info } = request.body; - await this.updateRegistry.updateClusterInfo(id, cluster_info); - log( - 'wazuh-hosts:updateClusterInfo', - `API entry ${id} hostname updated`, - 'debug' + await context.wazuh_core.updateRegistry.updateClusterInfo( + id, + cluster_info, ); + context.wazuh.logger.info(`Server API host entry ${id} updated`); return response.ok({ - body: { statusCode: 200, message: 'ok' } + body: { statusCode: 200, message: 'ok' }, }); } catch (error) { - log('wazuh-hosts:updateClusterInfo', error.message || error); + context.wazuh.logger.error(error.message || error); return ErrorResponse( - `Could not update data in wazuh-registry.json due to ${error.message || error}`, + `Could not update data in wazuh-registry.json due to ${ + error.message || error + }`, 2012, 500, - response + response, ); } } @@ -130,21 +92,27 @@ export class WazuhHostsCtrl { * @param {Object} request * @param {Object} response */ - async removeOrphanEntries(context: RequestHandlerContext, request: OpenSearchDashboardsRequest, response: OpenSearchDashboardsResponseFactory) { + async removeOrphanEntries( + context: RequestHandlerContext, + request: OpenSearchDashboardsRequest, + response: OpenSearchDashboardsResponseFactory, + ) { try { const { entries } = request.body; - log('wazuh-hosts:cleanRegistry', 'Cleaning registry', 'debug'); - await this.updateRegistry.removeOrphanEntries(entries); + context.wazuh.logger.debug('Cleaning registry file'); + await context.wazuh_core.updateRegistry.removeOrphanEntries(entries); return response.ok({ - body: { statusCode: 200, message: 'ok' } + body: { statusCode: 200, message: 'ok' }, }); } catch (error) { - log('wazuh-hosts:cleanRegistry', error.message || error); + context.wazuh.logger.error(error.message || error); return ErrorResponse( - `Could not clean entries in the wazuh-registry.json due to ${error.message || error}`, + `Could not clean entries in the wazuh-registry.json due to ${ + error.message || error + }`, 2013, 500, - response + response, ); } } diff --git a/plugins/main/server/controllers/wazuh-reporting-security-endpoint-handler.test.ts b/plugins/main/server/controllers/wazuh-reporting-security-endpoint-handler.test.ts index 5e0c8c4e15..1a0d771df1 100644 --- a/plugins/main/server/controllers/wazuh-reporting-security-endpoint-handler.test.ts +++ b/plugins/main/server/controllers/wazuh-reporting-security-endpoint-handler.test.ts @@ -2,51 +2,62 @@ import md5 from 'md5'; import fs from 'fs'; import { WazuhReportingCtrl } from './wazuh-reporting'; -jest.mock('../lib/logger', () => ({ - log: jest.fn() -})); - jest.mock('../lib/reporting/extended-information', () => ({ extendedInformation: () => {}, - buildAgentsTable: () => {} + buildAgentsTable: () => {}, })); jest.mock('../lib/reporting/printer', () => { class ReportPrinterMock { - constructor() { } - addContent() { } - addConfigTables() { } - addTables() { } - addTimeRangeAndFilters() { } - addVisualizations() { } - formatDate() { } - checkTitle() { } - addSimpleTable() { } - addList() { } - addNewLine() { } - addContentWithNewLine() { } - addAgentsFilters() { } - print() { } + constructor() {} + addContent() {} + addConfigTables() {} + addTables() {} + addTimeRangeAndFilters() {} + addVisualizations() {} + formatDate() {} + checkTitle() {} + addSimpleTable() {} + addList() {} + addNewLine() {} + addContentWithNewLine() {} + addAgentsFilters() {} + print() {} } return { - ReportPrinter: ReportPrinterMock - } + ReportPrinter: ReportPrinterMock, + }; }); -const getMockerUserContext = (username: string) => ({ username, hashUsername: md5(username) }); +const getMockerUserContext = (username: string) => ({ + username, + hashUsername: md5(username), +}); const mockContext = (username: string) => ({ wazuh: { security: { - getCurrentUser: () => getMockerUserContext(username) - } - } + getCurrentUser: () => getMockerUserContext(username), + }, + logger: { + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + debug: jest.fn(), + get: jest.fn(() => ({ + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + debug: jest.fn(), + })), + }, + }, }); const mockResponse = () => ({ - ok: (body) => body, - custom: (body) => body, - badRequest: (body) => body + ok: body => body, + custom: body => body, + badRequest: body => body, }); const endpointController = new WazuhReportingCtrl(); @@ -71,124 +82,155 @@ describe('[security] Report endpoints guard related to a file. Parameter defines }); it.each` - testTitle | username | filename | endpointProtected - ${'Execute endpoint handler'} | ${'admin'} | ${'wazuh-module-overview-general-1234.pdf'} | ${false} - ${'Endpoint protected'} | ${'admin'} | ${'../wazuh-module-overview-general-1234.pdf'} | ${true} - ${'Endpoint protected'} | ${'admin'} | ${'wazuh-module-overview-../general-1234.pdf'} | ${true} - ${'Endpoint protected'} | ${'admin'} | ${'custom../wazuh-module-overview-general-1234.pdf'} | ${true} - ${'Execute endpoint handler'} | ${'../../etc'} | ${'wazuh-module-agents-001-general-1234.pdf'} | ${false} - ${'Endpoint protected'} | ${'../../etc'} | ${'../wazuh-module-agents-001-general-1234.pdf'} | ${true} - ${'Endpoint protected'} | ${'../../etc'} | ${'wazuh-module-overview-../general-1234.pdf'} | ${true} - ${'Endpoint protected'} | ${'../../etc'} | ${'custom../wazuh-module-overview-general-1234.pdf'} | ${true} - `(`$testTitle + testTitle | username | filename | endpointProtected + ${'Execute endpoint handler'} | ${'admin'} | ${'wazuh-module-overview-general-1234.pdf'} | ${false} + ${'Endpoint protected'} | ${'admin'} | ${'../wazuh-module-overview-general-1234.pdf'} | ${true} + ${'Endpoint protected'} | ${'admin'} | ${'wazuh-module-overview-../general-1234.pdf'} | ${true} + ${'Endpoint protected'} | ${'admin'} | ${'custom../wazuh-module-overview-general-1234.pdf'} | ${true} + ${'Execute endpoint handler'} | ${'../../etc'} | ${'wazuh-module-agents-001-general-1234.pdf'} | ${false} + ${'Endpoint protected'} | ${'../../etc'} | ${'../wazuh-module-agents-001-general-1234.pdf'} | ${true} + ${'Endpoint protected'} | ${'../../etc'} | ${'wazuh-module-overview-../general-1234.pdf'} | ${true} + ${'Endpoint protected'} | ${'../../etc'} | ${'custom../wazuh-module-overview-general-1234.pdf'} | ${true} + `( + `$testTitle username: $username filename: $filename - endpointProtected: $endpointProtected`, async ({ username, filename, endpointProtected }) => { - const response = await endpointController.checkReportsUserDirectoryIsValidRouteDecorator( - routeHandler, - function getFilename(request) { - return request.params.name + endpointProtected: $endpointProtected`, + async ({ username, filename, endpointProtected }) => { + const response = + await endpointController.checkReportsUserDirectoryIsValidRouteDecorator( + routeHandler, + function getFilename(request) { + return request.params.name; + }, + )( + mockContext(username), + { params: { name: filename } }, + mockResponse(), + ); + if (endpointProtected) { + expect(response.body.message).toBe('5040 - You shall not pass!'); + expect(routeHandler.mock.calls).toHaveLength(0); + } else { + expect(routeHandler.mock.calls).toHaveLength(1); + expect(response).toBe(routeHandlerResponse); } - )(mockContext(username), { params: { name: filename } }, mockResponse()); - if (endpointProtected) { - - expect(response.body.message).toBe('5040 - You shall not pass!'); - expect(routeHandler.mock.calls).toHaveLength(0); - } else { - expect(routeHandler.mock.calls).toHaveLength(1); - expect(response).toBe(routeHandlerResponse); - } - }); - + }, + ); }); describe('[security] GET /reports', () => { - it.each` - username - ${'admin'} - ${'../../etc'} - `(`Get user reports: GET /reports - username: $username`, async ({ username }) => { - jest.spyOn(fs, 'readdirSync').mockImplementation(() => []); - - const response = await endpointController.getReports(mockContext(username), {}, mockResponse()); - expect(response.body.reports).toHaveLength(0); - }); + username + ${'admin'} + ${'../../etc'} + `( + `Get user reports: GET /reports + username: $username`, + async ({ username }) => { + jest.spyOn(fs, 'readdirSync').mockImplementation(() => []); + + const response = await endpointController.getReports( + mockContext(username), + {}, + mockResponse(), + ); + expect(response.body.reports).toHaveLength(0); + }, + ); }); describe('[security] GET /reports/{name}', () => { - it.each` - titleTest | username | filename | valid - ${'Get report'} | ${'admin'} | ${'wazuh-module-overview-1234.pdf'} | ${true} - ${'Endpoint protected'} | ${'admin'} | ${'../wazuh-module-overview-1234.pdf'} | ${false} - ${'Get report'} | ${'../../etc'} | ${'wazuh-module-overview-1234.pdf'} | ${true} - ${'Endpoint protected'} | ${'../../etc'} | ${'../wazuh-module-overview-1234.pdf'} | ${false} - `(`$titleTest: GET /reports/$filename + titleTest | username | filename | valid + ${'Get report'} | ${'admin'} | ${'wazuh-module-overview-1234.pdf'} | ${true} + ${'Endpoint protected'} | ${'admin'} | ${'../wazuh-module-overview-1234.pdf'} | ${false} + ${'Get report'} | ${'../../etc'} | ${'wazuh-module-overview-1234.pdf'} | ${true} + ${'Endpoint protected'} | ${'../../etc'} | ${'../wazuh-module-overview-1234.pdf'} | ${false} + `( + `$titleTest: GET /reports/$filename username: $username - valid: $valid`, async ({ username, filename, valid }) => { - const fileContent = 'content file'; - jest.spyOn(fs, 'readFileSync').mockImplementation(() => fileContent); - - const response = await endpointController.getReportByName(mockContext(username), { params: { name: filename } }, mockResponse()); - if (valid) { - expect(response.headers['Content-Type']).toBe('application/pdf'); - expect(response.body).toBe('content file'); - } else { - expect(response.body.message).toBe('5040 - You shall not pass!'); - } - }); + valid: $valid`, + async ({ username, filename, valid }) => { + const fileContent = 'content file'; + jest.spyOn(fs, 'readFileSync').mockImplementation(() => fileContent); + + const response = await endpointController.getReportByName( + mockContext(username), + { params: { name: filename } }, + mockResponse(), + ); + if (valid) { + expect(response.headers['Content-Type']).toBe('application/pdf'); + expect(response.body).toBe('content file'); + } else { + expect(response.body.message).toBe('5040 - You shall not pass!'); + } + }, + ); }); describe('[security] POST /reports', () => { jest.mock('../lib/filesystem', () => ({ - createDataDirectoryIfNotExists: jest.fn() + createDataDirectoryIfNotExists: jest.fn(), })); it.each` - titleTest | username | moduleID | valid - ${'Create report'} | ${'admin'} | ${'general'} | ${true} - ${'Endpoint protected'} | ${'admin'} | ${'../general'} | ${false} - ${'Create report'} | ${'../../etc'} | ${'general'} | ${true} - ${'Endpoint protected'} | ${'../../etc'} | ${'../general'} | ${false} - `(`$titleTest: POST /reports/modules/$moduleID + titleTest | username | moduleID | valid + ${'Create report'} | ${'admin'} | ${'general'} | ${true} + ${'Endpoint protected'} | ${'admin'} | ${'../general'} | ${false} + ${'Create report'} | ${'../../etc'} | ${'general'} | ${true} + ${'Endpoint protected'} | ${'../../etc'} | ${'../general'} | ${false} + `( + `$titleTest: POST /reports/modules/$moduleID username: $username - valid: $valid`, async ({ username, moduleID, valid }) => { - jest.spyOn(endpointController, 'renderHeader').mockImplementation(() => true); - jest.spyOn(endpointController, 'sanitizeKibanaFilters').mockImplementation(() => [false, false]); - - const mockRequest = { - body: { - array: [], - agents: false, - browserTimezone: '', - searchBar: '', - filters: [], - time: { - from: '', - to: '' + valid: $valid`, + async ({ username, moduleID, valid }) => { + jest + .spyOn(endpointController, 'renderHeader') + .mockImplementation(() => true); + jest + .spyOn(endpointController, 'sanitizeKibanaFilters') + .mockImplementation(() => [false, false]); + + const mockRequest = { + body: { + array: [], + agents: false, + browserTimezone: '', + searchBar: '', + filters: [], + time: { + from: '', + to: '', + }, + tables: [], + section: 'overview', + indexPatternTitle: 'wazuh-alerts-*', + apiId: 'default', + tab: moduleID, }, - tables: [], - section: 'overview', - indexPatternTitle: 'wazuh-alerts-*', - apiId: 'default', - tab: moduleID - }, - params: { - moduleID: moduleID - } - }; + params: { + moduleID: moduleID, + }, + }; - const response = await endpointController.createReportsModules(mockContext(username), mockRequest, mockResponse()); + const response = await endpointController.createReportsModules( + mockContext(username), + mockRequest, + mockResponse(), + ); - if (valid) { - expect(response.body.success).toBe(true); - expect(response.body.message).toMatch(new RegExp(`Report wazuh-module-overview-${moduleID}`)); - } else { - expect(response.body.message).toBe('5040 - You shall not pass!'); - }; - }); + if (valid) { + expect(response.body.success).toBe(true); + expect(response.body.message).toMatch( + new RegExp(`Report wazuh-module-overview-${moduleID}`), + ); + } else { + expect(response.body.message).toBe('5040 - You shall not pass!'); + } + }, + ); }); describe('[security] DELETE /reports/', () => { @@ -199,26 +241,35 @@ describe('[security] DELETE /reports/', () => { }); it.each` - titleTest | username | filename | valid - ${'Delete report'} | ${'admin'} | ${'wazuh-module-overview-1234.pdf'} | ${true} - ${'Endpoint protected'} | ${'admin'} | ${'../wazuh-module-overview-1234.pdf'} | ${false} - ${'Endpoint protected'} | ${'admin'} | ${'custom../wazuh-module-overview-1234.pdf'}| ${false} - ${'Delete report'} | ${'../../etc'} | ${'wazuh-module-overview-1234.pdf'} | ${true} - ${'Endpoint protected'} | ${'../../etc'} | ${'../wazuh-module-overview-1234.pdf'} | ${false} - ${'Endpoint protected'} | ${'../../etc'} | ${'custom../wazuh-module-overview-1234.pdf'}| ${false} - `(`[security] DELETE /reports/$filename + titleTest | username | filename | valid + ${'Delete report'} | ${'admin'} | ${'wazuh-module-overview-1234.pdf'} | ${true} + ${'Endpoint protected'} | ${'admin'} | ${'../wazuh-module-overview-1234.pdf'} | ${false} + ${'Endpoint protected'} | ${'admin'} | ${'custom../wazuh-module-overview-1234.pdf'} | ${false} + ${'Delete report'} | ${'../../etc'} | ${'wazuh-module-overview-1234.pdf'} | ${true} + ${'Endpoint protected'} | ${'../../etc'} | ${'../wazuh-module-overview-1234.pdf'} | ${false} + ${'Endpoint protected'} | ${'../../etc'} | ${'custom../wazuh-module-overview-1234.pdf'} | ${false} + `( + `[security] DELETE /reports/$filename username: $username - valid: $valid`, async ({ filename, username, valid }) => { - mockFsUnlinkSync = jest.spyOn(fs, 'unlinkSync').mockImplementation(() => { }); - - const response = await endpointController.deleteReportByName(mockContext(username), { params: { name: filename } }, mockResponse()); - - if (valid) { - expect(response.body.error).toBe(0); - expect(mockFsUnlinkSync.mock.calls).toHaveLength(1); - } else { - expect(response.body.message).toBe('5040 - You shall not pass!'); - expect(mockFsUnlinkSync.mock.calls).toHaveLength(0); - }; - }); -}); \ No newline at end of file + valid: $valid`, + async ({ filename, username, valid }) => { + mockFsUnlinkSync = jest + .spyOn(fs, 'unlinkSync') + .mockImplementation(() => {}); + + const response = await endpointController.deleteReportByName( + mockContext(username), + { params: { name: filename } }, + mockResponse(), + ); + + if (valid) { + expect(response.body.error).toBe(0); + expect(mockFsUnlinkSync.mock.calls).toHaveLength(1); + } else { + expect(response.body.message).toBe('5040 - You shall not pass!'); + expect(mockFsUnlinkSync.mock.calls).toHaveLength(0); + } + }, + ); +}); diff --git a/plugins/main/server/controllers/wazuh-reporting-security-endpoint-parameters-validation.test.ts b/plugins/main/server/controllers/wazuh-reporting-security-endpoint-parameters-validation.test.ts index edd829d9fa..4aa002af30 100644 --- a/plugins/main/server/controllers/wazuh-reporting-security-endpoint-parameters-validation.test.ts +++ b/plugins/main/server/controllers/wazuh-reporting-security-endpoint-parameters-validation.test.ts @@ -5,8 +5,15 @@ import { ByteSizeValue } from '@osd/config-schema'; import supertest from 'supertest'; import { WazuhReportingRoutes } from '../routes/wazuh-reporting'; import md5 from 'md5'; -import { createDataDirectoryIfNotExists, createDirectoryIfNotExists } from '../lib/filesystem'; -import { WAZUH_DATA_ABSOLUTE_PATH, WAZUH_DATA_DOWNLOADS_DIRECTORY_PATH, WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH } from '../../common/constants'; +import { + createDataDirectoryIfNotExists, + createDirectoryIfNotExists, +} from '../lib/filesystem'; +import { + WAZUH_DATA_ABSOLUTE_PATH, + WAZUH_DATA_DOWNLOADS_DIRECTORY_PATH, + WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH, +} from '../../common/constants'; import { execSync } from 'child_process'; import path from 'path'; import fs from 'fs'; @@ -16,18 +23,25 @@ const logger = loggingService.get(); const context = { wazuh: { security: { - getCurrentUser: (request) => { + getCurrentUser: request => { // x-test-username header doesn't exist when the platform or plugin are running. // It is used to generate the output of this method so we can simulate the user // that does the request to the endpoint and is expected by the endpoint handlers // of the plugin. const username = request.headers['x-test-username']; - return { username, hashUsername: md5(username) } - } - } - } + return { username, hashUsername: md5(username) }; + }, + }, + logger: { + debug: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + }, + }, }; -const enhanceWithContext = (fn: (...args: any[]) => any) => fn.bind(null, context); +const enhanceWithContext = (fn: (...args: any[]) => any) => + fn.bind(null, context); let server, innerServer; beforeAll(async () => { @@ -40,13 +54,29 @@ beforeAll(async () => { // Create report files [ { name: md5('admin'), files: ['wazuh-module-overview-general-1234.pdf'] }, - { name: md5('../../etc'), files: ['wazuh-module-overview-general-1234.pdf'] } + { + name: md5('../../etc'), + files: ['wazuh-module-overview-general-1234.pdf'], + }, ].forEach(({ name, files }) => { - createDirectoryIfNotExists(path.join(WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH, name)); + createDirectoryIfNotExists( + path.join(WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH, name), + ); if (files) { - files.forEach(filename => fs.closeSync(fs.openSync(path.join(WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH, name, filename), 'w'))); - }; + files.forEach(filename => + fs.closeSync( + fs.openSync( + path.join( + WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH, + name, + filename, + ), + 'w', + ), + ), + ); + } }); // Create server @@ -64,7 +94,11 @@ beforeAll(async () => { } as any; server = new HttpServer(loggingService, 'tests'); const router = new Router('', logger, enhanceWithContext); - const { registerRouter, server: innerServerTest, ...rest } = await server.setup(config); + const { + registerRouter, + server: innerServerTest, + ...rest + } = await server.setup(config); innerServer = innerServerTest; // Register routes @@ -87,202 +121,229 @@ afterAll(async () => { describe('[endpoint] GET /reports', () => { it.each` - username - ${'admin'} - ${'../../etc'} - `(`Get reports of user GET /reports - 200 - username: $username`, async ({ username }) => { - const response = await supertest(innerServer.listener) - .get('/reports') - .set('x-test-username', username) - .expect(200); + username + ${'admin'} + ${'../../etc'} + `( + `Get reports of user GET /reports - 200 + username: $username`, + async ({ username }) => { + const response = await supertest(innerServer.listener) + .get('/reports') + .set('x-test-username', username) + .expect(200); - expect(response.body.reports).toBeDefined(); - }); + expect(response.body.reports).toBeDefined(); + }, + ); }); describe('[endpoint][security] GET /reports/{name} - Parameters validation', () => { it.each` - testTitle | username | filename | responseStatusCode | responseBodyMessage - ${'Get report by filename'} | ${'admin'} | ${'wazuh-module-overview-general-1234.pdf'} | ${200} | ${null} - ${'Invalid parameters'} | ${'admin'} | ${'..%2fwazuh-module-overview-general-1234.pdf'} | ${400} | ${'[request params.name]: must be A-z, 0-9, _, ., and - are allowed. It must end with .pdf.'} - ${'Invalid parameters'} | ${'admin'} | ${'custom..%2fwazuh-module-overview-general-1234.pdf'} | ${400} | ${'[request params.name]: must be A-z, 0-9, _, ., and - are allowed. It must end with .pdf.'} - ${'Route not found'} | ${'admin'} | ${'../custom..%2fwazuh-module-overview-general-1234.pdf'} | ${404} | ${/Not Found/} - ${'Get report by filename'} | ${'../../etc'} | ${'wazuh-module-overview-general-1234.pdf'} | ${200} | ${null} - ${'Invalid parameters'} | ${'../../etc'} | ${'..%2fwazuh-module-overview-general-1234.pdf'} | ${400} | ${'[request params.name]: must be A-z, 0-9, _, ., and - are allowed. It must end with .pdf.'} - ${'Invalid parameters'} | ${'../../etc'} | ${'custom..%2fwazuh-module-overview-general-1234.pdf'} | ${400} | ${'[request params.name]: must be A-z, 0-9, _, ., and - are allowed. It must end with .pdf.'} - ${'Route not found'} | ${'../../etc'} | ${'../custom..%2fwazuh-module-overview-general-1234.pdf'} | ${404} | ${/Not Found/} - `(`$testTitle: GET /reports/$filename - responseStatusCode: $responseStatusCode + testTitle | username | filename | responseStatusCode | responseBodyMessage + ${'Get report by filename'} | ${'admin'} | ${'wazuh-module-overview-general-1234.pdf'} | ${200} | ${null} + ${'Invalid parameters'} | ${'admin'} | ${'..%2fwazuh-module-overview-general-1234.pdf'} | ${400} | ${'[request params.name]: must be A-z, 0-9, _, ., and - are allowed. It must end with .pdf.'} + ${'Invalid parameters'} | ${'admin'} | ${'custom..%2fwazuh-module-overview-general-1234.pdf'} | ${400} | ${'[request params.name]: must be A-z, 0-9, _, ., and - are allowed. It must end with .pdf.'} + ${'Route not found'} | ${'admin'} | ${'../custom..%2fwazuh-module-overview-general-1234.pdf'} | ${404} | ${/Not Found/} + ${'Get report by filename'} | ${'../../etc'} | ${'wazuh-module-overview-general-1234.pdf'} | ${200} | ${null} + ${'Invalid parameters'} | ${'../../etc'} | ${'..%2fwazuh-module-overview-general-1234.pdf'} | ${400} | ${'[request params.name]: must be A-z, 0-9, _, ., and - are allowed. It must end with .pdf.'} + ${'Invalid parameters'} | ${'../../etc'} | ${'custom..%2fwazuh-module-overview-general-1234.pdf'} | ${400} | ${'[request params.name]: must be A-z, 0-9, _, ., and - are allowed. It must end with .pdf.'} + ${'Route not found'} | ${'../../etc'} | ${'../custom..%2fwazuh-module-overview-general-1234.pdf'} | ${404} | ${/Not Found/} + `( + `$testTitle: GET /reports/$filename - responseStatusCode: $responseStatusCode username: $username - responseBodyMessage: $responseBodyMessage`, async ({ username, filename, responseStatusCode, responseBodyMessage }) => { - const response = await supertest(innerServer.listener) - .get(`/reports/${filename}`) - .set('x-test-username', username) - .expect(responseStatusCode); - if (responseStatusCode === 200) { - expect(response.header['content-type']).toMatch(/application\/pdf/); - expect(response.body instanceof Buffer).toBe(true); - }; - if (responseBodyMessage) { - expect(response.body.message).toMatch(responseBodyMessage); - }; - }); + responseBodyMessage: $responseBodyMessage`, + async ({ username, filename, responseStatusCode, responseBodyMessage }) => { + const response = await supertest(innerServer.listener) + .get(`/reports/${filename}`) + .set('x-test-username', username) + .expect(responseStatusCode); + if (responseStatusCode === 200) { + expect(response.header['content-type']).toMatch(/application\/pdf/); + expect(response.body instanceof Buffer).toBe(true); + } + if (responseBodyMessage) { + expect(response.body.message).toMatch(responseBodyMessage); + } + }, + ); }); describe('[endpoint][security] POST /reports/modules/{moduleID} - Parameters validation', () => { it.each` - testTitle | username | moduleID | agents | responseStatusCode | responseBodyMessage - ${'Invalid paramenters'} | ${'admin'} | ${'..general'} | ${false} | ${400} | ${/\[request params.moduleID\]: types that failed validation:/} - ${'Route not found'} | ${'admin'} | ${'../general'} | ${false} | ${404} | ${/Not Found/} - ${'Route not found'} | ${'admin'} | ${'../general'} | ${'001'} | ${404} | ${/Not Found/} - ${'Invalid paramenters'} | ${'admin'} | ${'..%2fgeneral'} | ${'../001'} | ${400} | ${/\[request params.moduleID\]: types that failed validation:/} - ${'Invalid paramenters'} | ${'admin'} | ${'..%2fgeneral'} | ${'001'} | ${400} | ${/\[request params.moduleID\]: types that failed validation:/} - ${'Invalid paramenters'} | ${'admin'} | ${'general'} | ${'..001'} | ${400} | ${/\[request body.agents\]: types that failed validation:/} - ${'Invalid paramenters'} | ${'admin'} | ${'general'} | ${'../001'} | ${400} | ${/\[request body.agents\]: types that failed validation:/} - `(`$testTitle: GET /reports/modules/$moduleID - responseStatusCode: $responseStatusCode + testTitle | username | moduleID | agents | responseStatusCode | responseBodyMessage + ${'Invalid paramenters'} | ${'admin'} | ${'..general'} | ${false} | ${400} | ${/\[request params.moduleID\]: types that failed validation:/} + ${'Route not found'} | ${'admin'} | ${'../general'} | ${false} | ${404} | ${/Not Found/} + ${'Route not found'} | ${'admin'} | ${'../general'} | ${'001'} | ${404} | ${/Not Found/} + ${'Invalid paramenters'} | ${'admin'} | ${'..%2fgeneral'} | ${'../001'} | ${400} | ${/\[request params.moduleID\]: types that failed validation:/} + ${'Invalid paramenters'} | ${'admin'} | ${'..%2fgeneral'} | ${'001'} | ${400} | ${/\[request params.moduleID\]: types that failed validation:/} + ${'Invalid paramenters'} | ${'admin'} | ${'general'} | ${'..001'} | ${400} | ${/\[request body.agents\]: types that failed validation:/} + ${'Invalid paramenters'} | ${'admin'} | ${'general'} | ${'../001'} | ${400} | ${/\[request body.agents\]: types that failed validation:/} + `( + `$testTitle: GET /reports/modules/$moduleID - responseStatusCode: $responseStatusCode username: $username agents: $agents - responseBodyMessage: $responseBodyMessage`, async ({ username, moduleID, agents, responseStatusCode, responseBodyMessage }) => { - const response = await supertest(innerServer.listener) - .post(`/reports/modules/${moduleID}`) - .set('x-test-username', username) - .send({ - array: [], - agents: agents, - browserTimezone: '', - searchBar: '', - filters: [], - time: { - from: '', - to: '' - }, - tables: [], - section: 'overview', - indexPatternTitle: 'wazuh-alerts-*', - apiId: 'default' - }) - .expect(responseStatusCode); - if (responseBodyMessage) { - expect(response.body.message).toMatch(responseBodyMessage); - }; - }); + responseBodyMessage: $responseBodyMessage`, + async ({ + username, + moduleID, + agents, + responseStatusCode, + responseBodyMessage, + }) => { + const response = await supertest(innerServer.listener) + .post(`/reports/modules/${moduleID}`) + .set('x-test-username', username) + .send({ + array: [], + agents: agents, + browserTimezone: '', + searchBar: '', + filters: [], + time: { + from: '', + to: '', + }, + tables: [], + section: 'overview', + indexPatternTitle: 'wazuh-alerts-*', + apiId: 'default', + }) + .expect(responseStatusCode); + if (responseBodyMessage) { + expect(response.body.message).toMatch(responseBodyMessage); + } + }, + ); }); describe('[endpoint][security] POST /reports/groups/{groupID} - Parameters validation', () => { it.each` - testTitle | username | groupID | responseStatusCode | responseBodyMessage - ${'Invalid parameters'} | ${'admin'} | ${'..%2fdefault'} | ${400} | ${'[request params.groupID]: must be A-z, 0-9, _, . are allowed. It must not be ., .. or all.'} - ${'Route not found'} | ${'admin'} | ${'../default'} | ${404} | ${/Not Found/} - ${'Invalid parameters'} | ${'../../etc'} | ${'..%2fdefault'} | ${400} | ${'[request params.groupID]: must be A-z, 0-9, _, . are allowed. It must not be ., .. or all.'} - ${'Route not found'} | ${'../../etc'} | ${'../default'} | ${404} | ${/Not Found/} - `(`$testTitle: GET /reports/groups/$groupID - $responseStatusCode + testTitle | username | groupID | responseStatusCode | responseBodyMessage + ${'Invalid parameters'} | ${'admin'} | ${'..%2fdefault'} | ${400} | ${'[request params.groupID]: must be A-z, 0-9, _, . are allowed. It must not be ., .. or all.'} + ${'Route not found'} | ${'admin'} | ${'../default'} | ${404} | ${/Not Found/} + ${'Invalid parameters'} | ${'../../etc'} | ${'..%2fdefault'} | ${400} | ${'[request params.groupID]: must be A-z, 0-9, _, . are allowed. It must not be ., .. or all.'} + ${'Route not found'} | ${'../../etc'} | ${'../default'} | ${404} | ${/Not Found/} + `( + `$testTitle: GET /reports/groups/$groupID - $responseStatusCode username: $username - responseBodyMessage: $responseBodyMessage`, async ({ username, groupID, responseStatusCode, responseBodyMessage }) => { - const response = await supertest(innerServer.listener) - .post(`/reports/groups/${groupID}`) - .set('x-test-username', username) - .send({ - browserTimezone: '', - components: { '1': true }, - section: '', - apiId: 'default' - }) - .expect(responseStatusCode); - if (responseBodyMessage) { - expect(response.body.message).toMatch(responseBodyMessage); - }; - }); + responseBodyMessage: $responseBodyMessage`, + async ({ username, groupID, responseStatusCode, responseBodyMessage }) => { + const response = await supertest(innerServer.listener) + .post(`/reports/groups/${groupID}`) + .set('x-test-username', username) + .send({ + browserTimezone: '', + components: { '1': true }, + section: '', + apiId: 'default', + }) + .expect(responseStatusCode); + if (responseBodyMessage) { + expect(response.body.message).toMatch(responseBodyMessage); + } + }, + ); }); describe('[endpoint][security] POST /reports/agents/{agentID} - Parameters validation', () => { it.each` - testTitle |username | agentID | responseStatusCode | responseBodyMessage - ${'Invalid parameters'} | ${'admin'} | ${'..001'} | ${400} | ${/\[request params.agentID\]: must be 0-9 are allowed/} - ${'Route not found'} | ${'admin'} | ${'../001'} | ${404} | ${/Not Found/} - ${'Invalid parameters'} | ${'admin'} | ${'..%2f001'} | ${400} | ${/\[request params.agentID\]: must be 0-9 are allowed/} - ${'Invalid parameters'} | ${'admin'} | ${'1'} | ${400} | ${/\[request params.agentID\]: value has length \[1\] but it must have a minimum length of \[3\]./} - ${'Invalid parameters'} | ${'../../etc'} | ${'..001'} | ${400} | ${/\[request params.agentID\]: must be 0-9 are allowed/} - ${'Route not found'} | ${'../../etc'} | ${'../001'} | ${404} | ${/Not Found/} - ${'Invalid parameters'} | ${'../../etc'} | ${'..%2f001'} | ${400} | ${/\[request params.agentID\]: must be 0-9 are allowed/} - ${'Invalid parameters'} | ${'../../etc'} | ${'1'} | ${400} | ${/\[request params.agentID\]: value has length \[1\] but it must have a minimum length of \[3\]./} - `(`$testTitle: GET /reports/agents/$agentID - $responseStatusCode + testTitle | username | agentID | responseStatusCode | responseBodyMessage + ${'Invalid parameters'} | ${'admin'} | ${'..001'} | ${400} | ${/\[request params.agentID\]: must be 0-9 are allowed/} + ${'Route not found'} | ${'admin'} | ${'../001'} | ${404} | ${/Not Found/} + ${'Invalid parameters'} | ${'admin'} | ${'..%2f001'} | ${400} | ${/\[request params.agentID\]: must be 0-9 are allowed/} + ${'Invalid parameters'} | ${'admin'} | ${'1'} | ${400} | ${/\[request params.agentID\]: value has length \[1\] but it must have a minimum length of \[3\]./} + ${'Invalid parameters'} | ${'../../etc'} | ${'..001'} | ${400} | ${/\[request params.agentID\]: must be 0-9 are allowed/} + ${'Route not found'} | ${'../../etc'} | ${'../001'} | ${404} | ${/Not Found/} + ${'Invalid parameters'} | ${'../../etc'} | ${'..%2f001'} | ${400} | ${/\[request params.agentID\]: must be 0-9 are allowed/} + ${'Invalid parameters'} | ${'../../etc'} | ${'1'} | ${400} | ${/\[request params.agentID\]: value has length \[1\] but it must have a minimum length of \[3\]./} + `( + `$testTitle: GET /reports/agents/$agentID - $responseStatusCode username: $username - responseBodyMessage: $responseBodyMessage`, async ({ username, agentID, responseStatusCode, responseBodyMessage }) => { - const response = await supertest(innerServer.listener) - .post(`/reports/agents/${agentID}`) - .set('x-test-username', username) - .send({ - array: [], - agents: agentID, - browserTimezone: '', - searchBar: '', - filters: [], - time: { - from: '', - to: '' - }, - tables: [], - section: 'overview', - indexPatternTitle: 'wazuh-alerts-*', - apiId: 'default' - }) - .expect(responseStatusCode); - if (responseBodyMessage) { - expect(response.body.message).toMatch(responseBodyMessage); - }; - }); + responseBodyMessage: $responseBodyMessage`, + async ({ username, agentID, responseStatusCode, responseBodyMessage }) => { + const response = await supertest(innerServer.listener) + .post(`/reports/agents/${agentID}`) + .set('x-test-username', username) + .send({ + array: [], + agents: agentID, + browserTimezone: '', + searchBar: '', + filters: [], + time: { + from: '', + to: '', + }, + tables: [], + section: 'overview', + indexPatternTitle: 'wazuh-alerts-*', + apiId: 'default', + }) + .expect(responseStatusCode); + if (responseBodyMessage) { + expect(response.body.message).toMatch(responseBodyMessage); + } + }, + ); }); describe('[endpoint][security] POST /reports/agents/{agentID}/inventory - Parameters validation', () => { it.each` - testTitle | username | agentID | responseStatusCode | responseBodyMessage - ${'Invalid parameters'} | ${'admin'} | ${'..001'} | ${400} | ${/\[request params.agentID\]: must be 0-9 are allowed/} - ${'Route not found'} | ${'admin'} | ${'../001'} | ${404} | ${/Not Found/} - ${'Invalid parameters'} | ${'admin'} | ${'..%2f001'} | ${400} | ${/\[request params.agentID\]: must be 0-9 are allowed/} - ${'Invalid parameters'} | ${'admin'} | ${'1'} | ${400} | ${/\[request params.agentID\]: value has length \[1\] but it must have a minimum length of \[3\]./} - ${'Invalid parameters'} | ${'../../etc'} | ${'..001'} | ${400} | ${/\[request params.agentID\]: must be 0-9 are allowed/} - ${'Route not found'} | ${'../../etc'} | ${'../001'} | ${404} | ${/Not Found/} - ${'Invalid parameters'} | ${'../../etc'} | ${'..%2f001'} | ${400} | ${/\[request params.agentID\]: must be 0-9 are allowed/} - ${'Invalid parameters'} | ${'../../etc'} | ${'1'} | ${400} | ${/\[request params.agentID\]: value has length \[1\] but it must have a minimum length of \[3\]./} - `(`$testTitle: GET /reports/agents/$agentID/inventory - $responseStatusCode + testTitle | username | agentID | responseStatusCode | responseBodyMessage + ${'Invalid parameters'} | ${'admin'} | ${'..001'} | ${400} | ${/\[request params.agentID\]: must be 0-9 are allowed/} + ${'Route not found'} | ${'admin'} | ${'../001'} | ${404} | ${/Not Found/} + ${'Invalid parameters'} | ${'admin'} | ${'..%2f001'} | ${400} | ${/\[request params.agentID\]: must be 0-9 are allowed/} + ${'Invalid parameters'} | ${'admin'} | ${'1'} | ${400} | ${/\[request params.agentID\]: value has length \[1\] but it must have a minimum length of \[3\]./} + ${'Invalid parameters'} | ${'../../etc'} | ${'..001'} | ${400} | ${/\[request params.agentID\]: must be 0-9 are allowed/} + ${'Route not found'} | ${'../../etc'} | ${'../001'} | ${404} | ${/Not Found/} + ${'Invalid parameters'} | ${'../../etc'} | ${'..%2f001'} | ${400} | ${/\[request params.agentID\]: must be 0-9 are allowed/} + ${'Invalid parameters'} | ${'../../etc'} | ${'1'} | ${400} | ${/\[request params.agentID\]: value has length \[1\] but it must have a minimum length of \[3\]./} + `( + `$testTitle: GET /reports/agents/$agentID/inventory - $responseStatusCode username: $username - responseBodyMessage: $responseBodyMessage`, async ({ username, agentID, responseStatusCode, responseBodyMessage }) => { - const response = await supertest(innerServer.listener) - .post(`/reports/agents/${agentID}/inventory`) - .set('x-test-username', username) - .send({ - browserTimezone: '', - components: { '1': true }, - section: '', - apiId: 'default' - }) - .expect(responseStatusCode); - if (responseBodyMessage) { - expect(response.body.message).toMatch(responseBodyMessage); - }; - }); + responseBodyMessage: $responseBodyMessage`, + async ({ username, agentID, responseStatusCode, responseBodyMessage }) => { + const response = await supertest(innerServer.listener) + .post(`/reports/agents/${agentID}/inventory`) + .set('x-test-username', username) + .send({ + browserTimezone: '', + components: { '1': true }, + section: '', + apiId: 'default', + }) + .expect(responseStatusCode); + if (responseBodyMessage) { + expect(response.body.message).toMatch(responseBodyMessage); + } + }, + ); }); describe('[endpoint][security] DELETE /reports/{name} - Parameters validation', () => { it.each` - testTitle | username | filename | responseStatusCode | responseBodyMessage - ${'Delete report file'} | ${'admin'} | ${'wazuh-module-overview-general-1234.pdf'} | ${200} | ${null} - ${'Invalid parameters'} | ${'admin'} | ${'..%2fwazuh-module-overview-general-1234.pdf'} | ${400} | ${'[request params.name]: must be A-z, 0-9, _, ., and - are allowed. It must end with .pdf.'} - ${'Invalid parameters'} | ${'admin'} | ${'custom..%2fwazuh-module-overview-general-1234.pdf'} | ${400} | ${'[request params.name]: must be A-z, 0-9, _, ., and - are allowed. It must end with .pdf.'} - ${'Route not found'} | ${'admin'} | ${'../wazuh-module-overview-general-1234.pdf'} | ${404} | ${/Not Found/} - ${'Delete report file'} | ${'../../etc'} | ${'wazuh-module-overview-general-1234.pdf'} | ${200} | ${null} - ${'Invalid parameters'} | ${'../../etc'} | ${'..%2fwazuh-module-overview-general-1234.pdf'} | ${400} | ${'[request params.name]: must be A-z, 0-9, _, ., and - are allowed. It must end with .pdf.'} - ${'Invalid parameters'} | ${'../../etc'} | ${'custom..%2fwazuh-module-overview-general-1234.pdf'} | ${400} | ${'[request params.name]: must be A-z, 0-9, _, ., and - are allowed. It must end with .pdf.'} - ${'Route not found'} | ${'../../etc'} | ${'../wazuh-module-overview-general-1234.pdf'} | ${404} | ${/Not Found/} - `(`$testTitle: DELETE /reports/$filename - $responseStatusCode + testTitle | username | filename | responseStatusCode | responseBodyMessage + ${'Delete report file'} | ${'admin'} | ${'wazuh-module-overview-general-1234.pdf'} | ${200} | ${null} + ${'Invalid parameters'} | ${'admin'} | ${'..%2fwazuh-module-overview-general-1234.pdf'} | ${400} | ${'[request params.name]: must be A-z, 0-9, _, ., and - are allowed. It must end with .pdf.'} + ${'Invalid parameters'} | ${'admin'} | ${'custom..%2fwazuh-module-overview-general-1234.pdf'} | ${400} | ${'[request params.name]: must be A-z, 0-9, _, ., and - are allowed. It must end with .pdf.'} + ${'Route not found'} | ${'admin'} | ${'../wazuh-module-overview-general-1234.pdf'} | ${404} | ${/Not Found/} + ${'Delete report file'} | ${'../../etc'} | ${'wazuh-module-overview-general-1234.pdf'} | ${200} | ${null} + ${'Invalid parameters'} | ${'../../etc'} | ${'..%2fwazuh-module-overview-general-1234.pdf'} | ${400} | ${'[request params.name]: must be A-z, 0-9, _, ., and - are allowed. It must end with .pdf.'} + ${'Invalid parameters'} | ${'../../etc'} | ${'custom..%2fwazuh-module-overview-general-1234.pdf'} | ${400} | ${'[request params.name]: must be A-z, 0-9, _, ., and - are allowed. It must end with .pdf.'} + ${'Route not found'} | ${'../../etc'} | ${'../wazuh-module-overview-general-1234.pdf'} | ${404} | ${/Not Found/} + `( + `$testTitle: DELETE /reports/$filename - $responseStatusCode username: $username - responseBodyMessage: $responseBodyMessage`, async ({ username, filename, responseStatusCode, responseBodyMessage }) => { - const response = await supertest(innerServer.listener) - .delete(`/reports/${filename}`) - .set('x-test-username', username) - .expect(responseStatusCode); - if (responseBodyMessage) { - expect(response.body.message).toMatch(responseBodyMessage); - }; - }); -}); \ No newline at end of file + responseBodyMessage: $responseBodyMessage`, + async ({ username, filename, responseStatusCode, responseBodyMessage }) => { + const response = await supertest(innerServer.listener) + .delete(`/reports/${filename}`) + .set('x-test-username', username) + .expect(responseStatusCode); + if (responseBodyMessage) { + expect(response.body.message).toMatch(responseBodyMessage); + } + }, + ); +}); diff --git a/plugins/main/server/controllers/wazuh-reporting.ts b/plugins/main/server/controllers/wazuh-reporting.ts index 1f9d5c2a06..5c00d692b0 100644 --- a/plugins/main/server/controllers/wazuh-reporting.ts +++ b/plugins/main/server/controllers/wazuh-reporting.ts @@ -27,7 +27,6 @@ import { buildAgentsTable, } from '../lib/reporting/extended-information'; import { ReportPrinter } from '../lib/reporting/printer'; -import { log } from '../lib/logger'; import { WAZUH_DATA_DOWNLOADS_DIRECTORY_PATH, WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH, @@ -46,25 +45,19 @@ interface AgentsFilter { } export class WazuhReportingCtrl { - constructor() { } + constructor() {} /** * This do format to filters * @param {String} filters E.g: cluster.name: wazuh AND rule.groups: vulnerability * @param {String} searchBar search term */ private sanitizeKibanaFilters( + context: any, filters: any, searchBar?: string, ): [string, AgentsFilter] { - log( - 'reporting:sanitizeKibanaFilters', - `Started to sanitize filters`, - 'info', - ); - log( - 'reporting:sanitizeKibanaFilters', - `filters: ${filters.length}, searchBar: ${searchBar}`, - 'debug', + context.wazuh.logger.debug( + `Started to sanitize filters. filters: ${filters.length}, searchBar: ${searchBar}`, ); let str = ''; @@ -109,10 +102,8 @@ export class WazuhReportingCtrl { .map(filter => filter.meta.value) .join(','); - log( - 'reporting:sanitizeKibanaFilters', + context.wazuh.logger.debug( `str: ${str}, agentsFilterStr: ${agentsFilter.agentsText}`, - 'debug', ); return [str, agentsFilter]; @@ -128,10 +119,8 @@ export class WazuhReportingCtrl { */ private async renderHeader(context, printer, section, tab, isAgents, apiId) { try { - log( - 'reporting:renderHeader', + context.wazuh.logger.debug( `section: ${section}, tab: ${tab}, isAgents: ${isAgents}, apiId: ${apiId}`, - 'debug', ); if (section && typeof section === 'string') { if (!['agentConfig', 'groupConfig'].includes(section)) { @@ -199,13 +188,13 @@ export class WazuhReportingCtrl { }); } } catch (error) { - log('reporting:renderHeader', error.message || error); + context.wazuh.logger.error(error.message || error); return Promise.reject(error); } } - private getConfigRows(data, labels) { - log('reporting:getConfigRows', `Building configuration rows`, 'info'); + private getConfigRows(context, data, labels) { + context.wazuh.logger.debug('Building configuration rows'); const result = []; for (let prop in data || []) { if (Array.isArray(data[prop])) { @@ -221,8 +210,8 @@ export class WazuhReportingCtrl { return result; } - private getConfigTables(data, section, tab, array = []) { - log('reporting:getConfigTables', `Building configuration tables`, 'info'); + private getConfigTables(context, data, section, tab, array = []) { + context.wazuh.logger.debug('Building configuration tables'); let plainData = {}; const nestedData = []; const tableData = []; @@ -259,10 +248,10 @@ export class WazuhReportingCtrl { title: (section.options || {}).hideHeader ? '' : (section.tabs || [])[tab] || - (section.isGroupConfig ? ((section.labels || [])[0] || [])[tab] : ''), + (section.isGroupConfig ? ((section.labels || [])[0] || [])[tab] : ''), columns: ['', ''], type: 'config', - rows: this.getConfigRows(plainData, (section.labels || [])[0]), + rows: this.getConfigRows(context, plainData, (section.labels || [])[0]), }); for (let key in tableData) { const columns = Object.keys(tableData[key][0]); @@ -296,7 +285,7 @@ export class WazuhReportingCtrl { }); } nestedData.forEach(nest => { - this.getConfigTables(nest, section, tab + 1, array); + this.getConfigTables(context, nest, section, tab + 1, array); }); return array; } @@ -315,7 +304,7 @@ export class WazuhReportingCtrl { response: OpenSearchDashboardsResponseFactory, ) => { try { - log('reporting:createReportsModules', `Report started`, 'info'); + context.wazuh.logger.debug('Report started'); const { array, agents, @@ -333,7 +322,9 @@ export class WazuhReportingCtrl { const { from, to } = time || {}; let additionalTables = []; // Init - const printer = new ReportPrinter(); + const printer = new ReportPrinter( + context.wazuh.logger.get('report-printer'), + ); createDataDirectoryIfNotExists(); createDirectoryIfNotExists(WAZUH_DATA_DOWNLOADS_DIRECTORY_PATH); @@ -355,7 +346,7 @@ export class WazuhReportingCtrl { ); const [sanitizedFilters, agentsFilter] = filters - ? this.sanitizeKibanaFilters(filters, searchBar) + ? this.sanitizeKibanaFilters(context, filters, searchBar) : [false, null]; if (time && sanitizedFilters) { @@ -426,11 +417,13 @@ export class WazuhReportingCtrl { response: OpenSearchDashboardsResponseFactory, ) => { try { - log('reporting:createReportsGroups', `Report started`, 'info'); + context.wazuh.logger.debug('Report started'); const { components, apiId } = request.body; const { groupID } = request.params; // Init - const printer = new ReportPrinter(); + const printer = new ReportPrinter( + context.wazuh.logger.get('report-printer'), + ); createDataDirectoryIfNotExists(); createDirectoryIfNotExists(WAZUH_DATA_DOWNLOADS_DIRECTORY_PATH); @@ -594,7 +587,9 @@ export class WazuhReportingCtrl { }); } else { for (let _d2 of config.config[_d]) { - tables.push(...this.getConfigTables(_d2, section, idx)); + tables.push( + ...this.getConfigTables(context, _d2, section, idx), + ); } } } else { @@ -603,7 +598,12 @@ export class WazuhReportingCtrl { const directories = config.config[_d].directories; delete config.config[_d].directories; tables.push( - ...this.getConfigTables(config.config[_d], section, idx), + ...this.getConfigTables( + context, + config.config[_d], + section, + idx, + ), ); let diffOpts = []; Object.keys(section.opts).forEach(x => { @@ -640,7 +640,12 @@ export class WazuhReportingCtrl { }); } else { tables.push( - ...this.getConfigTables(config.config[_d], section, idx), + ...this.getConfigTables( + context, + config.config[_d], + section, + idx, + ), ); } } @@ -682,7 +687,7 @@ export class WazuhReportingCtrl { }, }); } catch (error) { - log('reporting:createReportsGroups', error.message || error); + context.wazuh.logger.error(error.message || error); return ErrorResponse(error.message || error, 5029, 500, response); } }, @@ -705,15 +710,13 @@ export class WazuhReportingCtrl { response: OpenSearchDashboardsResponseFactory, ) => { try { - log( - 'reporting:createReportsAgentsConfiguration', - `Report started`, - 'info', - ); + context.wazuh.logger.debug('Report started'); const { components, apiId } = request.body; const { agentID } = request.params; - const printer = new ReportPrinter(); + const printer = new ReportPrinter( + context.wazuh.logger.get('report-printer'), + ); createDataDirectoryIfNotExists(); createDirectoryIfNotExists(WAZUH_DATA_DOWNLOADS_DIRECTORY_PATH); createDirectoryIfNotExists( @@ -737,7 +740,7 @@ export class WazuhReportingCtrl { { apiHostID: apiId }, ); } catch (error) { - log('reporting:report', error.message || error, 'debug'); + context.wazuh.logger.debug(error.message || error); } await this.renderHeader( @@ -752,10 +755,8 @@ export class WazuhReportingCtrl { let idxComponent = 0; for (let config of AgentConfiguration.configurations) { let titleOfSection = false; - log( - 'reporting:createReportsAgentsConfiguration', + context.wazuh.logger.debug( `Iterate over ${config.sections.length} configuration sections`, - 'debug', ); for (let section of config.sections) { let titleOfSubsection = false; @@ -767,10 +768,8 @@ export class WazuhReportingCtrl { const configs = (section.config || []).concat( section.wodle || [], ); - log( - 'reporting:createReportsAgentsConfiguration', + context.wazuh.logger.debug( `Iterate over ${configs.length} configuration blocks`, - 'debug', ); for (let conf of configs) { let agentConfigResponse = {}; @@ -875,6 +874,7 @@ export class WazuhReportingCtrl { ) { tables.push( ...this.getConfigTables( + context, agentConfig[agentConfigKey], section, idx, @@ -883,7 +883,12 @@ export class WazuhReportingCtrl { } else { for (let _d2 of agentConfig[agentConfigKey]) { tables.push( - ...this.getConfigTables(_d2, section, idx), + ...this.getConfigTables( + context, + _d2, + section, + idx, + ), ); } } @@ -898,9 +903,15 @@ export class WazuhReportingCtrl { ...rest } = agentConfig[agentConfigKey]; tables.push( - ...this.getConfigTables(rest, section, idx), + ...this.getConfigTables( + context, + rest, + section, + idx, + ), ...(diff && diff.disk_quota ? this.getConfigTables( + context, diff.disk_quota, { tabs: ['Disk quota'] }, 0, @@ -908,6 +919,7 @@ export class WazuhReportingCtrl { : []), ...(diff && diff.file_size ? this.getConfigTables( + context, diff.file_size, { tabs: ['File size'] }, 0, @@ -915,6 +927,7 @@ export class WazuhReportingCtrl { : []), ...(synchronization ? this.getConfigTables( + context, synchronization, { tabs: ['Synchronization'] }, 0, @@ -922,6 +935,7 @@ export class WazuhReportingCtrl { : []), ...(file_limit ? this.getConfigTables( + context, file_limit, { tabs: ['File limit'] }, 0, @@ -965,6 +979,7 @@ export class WazuhReportingCtrl { } else { tables.push( ...this.getConfigTables( + context, agentConfig[agentConfigKey], section, idx, @@ -988,7 +1003,7 @@ export class WazuhReportingCtrl { }); } } catch (error) { - log('reporting:report', error.message || error, 'debug'); + context.wazuh.logger.debug(error.message || error); } idx++; } @@ -1010,10 +1025,7 @@ export class WazuhReportingCtrl { }, }); } catch (error) { - log( - 'reporting:createReportsAgentsConfiguration', - error.message || error, - ); + context.wazuh.logger.debug(error.message || error); return ErrorResponse(error.message || error, 5029, 500, response); } }, @@ -1036,11 +1048,7 @@ export class WazuhReportingCtrl { response: OpenSearchDashboardsResponseFactory, ) => { try { - log( - 'reporting:createReportsAgentsInventory', - `Report started`, - 'info', - ); + context.wazuh.logger.debug('Report started'); const { searchBar, filters, @@ -1052,7 +1060,9 @@ export class WazuhReportingCtrl { const { agentID } = request.params; const { from, to } = time || {}; // Init - const printer = new ReportPrinter(); + const printer = new ReportPrinter( + context.wazuh.logger.get('report-printer'), + ); const { hashUsername } = await context.wazuh.security.getCurrentUser( request, @@ -1070,13 +1080,9 @@ export class WazuhReportingCtrl { ), ); - log( - 'reporting:createReportsAgentsInventory', - `Syscollector report`, - 'debug', - ); + context.wazuh.logger.debug('Syscollector report'); const [sanitizedFilters, agentsFilter] = filters - ? this.sanitizeKibanaFilters(filters, searchBar) + ? this.sanitizeKibanaFilters(context, filters, searchBar) : [false, null]; // Get the agent OS @@ -1101,11 +1107,7 @@ export class WazuhReportingCtrl { agentOs = (isAgentWindows && 'windows') || (isAgentLinux && 'linux') || ''; } catch (error) { - log( - 'reporting:createReportsAgentsInventory', - error.message || error, - 'debug', - ); + context.wazuh.logger.debug(error.message || error); } // Add title @@ -1244,11 +1246,7 @@ export class WazuhReportingCtrl { const requestInventory = async agentRequestInventory => { try { - log( - 'reporting:createReportsAgentsInventory', - agentRequestInventory.loggerMessage, - 'debug', - ); + context.wazuh.logger.debug(agentRequestInventory.loggerMessage); const inventoryResponse = await context.wazuh.api.client.asCurrentUser.request( @@ -1272,11 +1270,7 @@ export class WazuhReportingCtrl { }; } } catch (error) { - log( - 'reporting:createReportsAgentsInventory', - error.message || error, - 'debug', - ); + context.wazuh.logger.debug(error.message || error); } }; @@ -1320,7 +1314,7 @@ export class WazuhReportingCtrl { }, }); } catch (error) { - log('reporting:createReportsAgents', error.message || error); + context.wazuh.logger.error(error.message || error); return ErrorResponse(error.message || error, 5029, 500, response); } }, @@ -1341,7 +1335,7 @@ export class WazuhReportingCtrl { response: OpenSearchDashboardsResponseFactory, ) { try { - log('reporting:getReports', `Fetching created reports`, 'info'); + context.wazuh.logger.debug('Fetching created reports'); const { hashUsername } = await context.wazuh.security.getCurrentUser( request, context, @@ -1354,11 +1348,7 @@ export class WazuhReportingCtrl { hashUsername, ); createDirectoryIfNotExists(userReportsDirectoryPath); - log( - 'reporting:getReports', - `Directory: ${userReportsDirectoryPath}`, - 'debug', - ); + context.wazuh.logger.debug(`Directory: ${userReportsDirectoryPath}`); const sortReportsByDate = (a, b) => a.date < b.date ? 1 : a.date > b.date ? -1 : 0; @@ -1376,18 +1366,16 @@ export class WazuhReportingCtrl { date: stats[birthTimeField], }; }); - log( - 'reporting:getReports', + context.wazuh.logger.debug( `Using TimSort for sorting ${reports.length} items`, - 'debug', ); TimSort.sort(reports, sortReportsByDate); - log('reporting:getReports', `Total reports: ${reports.length}`, 'debug'); + context.wazuh.logger.debug(`Total reports: ${reports.length}`); return response.ok({ body: { reports }, }); } catch (error) { - log('reporting:getReports', error.message || error); + context.wazuh.logger.error(error.message || error); return ErrorResponse(error.message || error, 5031, 500, response); } } @@ -1406,10 +1394,8 @@ export class WazuhReportingCtrl { response: OpenSearchDashboardsResponseFactory, ) => { try { - log( - 'reporting:getReportByName', + context.wazuh.logger.debug( `Getting ${context.wazuhEndpointParams.pathFilename} report`, - 'debug', ); const reportFileBuffer = fs.readFileSync( context.wazuhEndpointParams.pathFilename, @@ -1419,7 +1405,7 @@ export class WazuhReportingCtrl { body: reportFileBuffer, }); } catch (error) { - log('reporting:getReportByName', error.message || error); + context.wazuh.logger.error(error.message || error); return ErrorResponse(error.message || error, 5030, 500, response); } }, @@ -1440,22 +1426,18 @@ export class WazuhReportingCtrl { response: OpenSearchDashboardsResponseFactory, ) => { try { - log( - 'reporting:deleteReportByName', + context.wazuh.logger.debug( `Deleting ${context.wazuhEndpointParams.pathFilename} report`, - 'debug', ); fs.unlinkSync(context.wazuhEndpointParams.pathFilename); - log( - 'reporting:deleteReportByName', + context.wazuh.logger.info( `${context.wazuhEndpointParams.pathFilename} report was deleted`, - 'info', ); return response.ok({ body: { error: 0 }, }); } catch (error) { - log('reporting:deleteReportByName', error.message || error); + context.wazuh.logger.error(error.message || error); return ErrorResponse(error.message || error, 5032, 500, response); } }, @@ -1480,19 +1462,15 @@ export class WazuhReportingCtrl { ); const filename = reportFileNameAccessor(request); const pathFilename = path.join(userReportsDirectoryPath, filename); - log( - 'reporting:checkReportsUserDirectoryIsValidRouteDecorator', + context.wazuh.logger.debug( `Checking the user ${username}(${hashUsername}) can do actions in the reports file: ${pathFilename}`, - 'debug', ); if ( !pathFilename.startsWith(userReportsDirectoryPath) || pathFilename.includes('../') ) { - log( - 'security:reporting:checkReportsUserDirectoryIsValidRouteDecorator', + context.wazuh.logger.warn( `User ${username}(${hashUsername}) tried to access to a non user report file: ${pathFilename}`, - 'warn', ); return response.badRequest({ body: { @@ -1500,10 +1478,8 @@ export class WazuhReportingCtrl { }, }); } - log( - 'reporting:checkReportsUserDirectoryIsValidRouteDecorator', + context.wazuh.logger.debug( 'Checking the user can do actions in the reports file', - 'debug', ); return await routeHandler.bind(this)( { @@ -1514,10 +1490,7 @@ export class WazuhReportingCtrl { response, ); } catch (error) { - log( - 'reporting:checkReportsUserDirectoryIsValidRouteDecorator', - error.message || error, - ); + context.wazuh.logger.error(error.message || error); return ErrorResponse(error.message || error, 5040, 500, response); } }; diff --git a/plugins/main/server/controllers/wazuh-utils/ui-logs.controller.test.ts b/plugins/main/server/controllers/wazuh-utils/ui-logs.controller.test.ts index 8828560c61..71235b33d7 100644 --- a/plugins/main/server/controllers/wazuh-utils/ui-logs.controller.test.ts +++ b/plugins/main/server/controllers/wazuh-utils/ui-logs.controller.test.ts @@ -1,8 +1,21 @@ import { UiLogsCtrl } from './ui-logs.controller'; -import { WAZUH_UI_LOGS_RAW_PATH } from '../../../common/constants'; -import uiLogger from '../../lib/ui-logger'; -const readLastLines = require('read-last-lines'); +const buildMockContext = () => { + return { + wazuh: { + logger: { + get() { + return { + debug: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + }; + }, + }, + }, + }; +}; const buildMockResponse = () => { const res = {}; @@ -19,26 +32,13 @@ const buildMockRequest = () => { describe('Spec UiLogsCtrl', function () { describe('Check method getUiLogs ', () => { - it('Should 200 and return correct value', async () => { - const result = { body: { error: 0, rawLogs: ['my test mocked'] } }; - const mockResponse = buildMockResponse(); - jest.spyOn(readLastLines, 'read').mockReturnValue('my test mocked'); - jest.spyOn(uiLogger, 'checkFileExist').mockReturnValue(true); - - const controller = new UiLogsCtrl(); - await controller.getUiLogs(mockResponse); - - expect(mockResponse.ok).toHaveBeenCalledTimes(1); - expect(mockResponse.ok.mock.calls.length).toBe(1); - expect(mockResponse.ok).toHaveBeenCalledWith(result); - }); - it('Should 200 and return message Log has been added', async () => { - const result = { body: { error: 0, message: 'Log has been added', statusCode: 200 } }; - const mockResponse = buildMockResponse(); - jest.spyOn(readLastLines, 'read').mockReturnValue('Log has been added'); - jest.spyOn(uiLogger, 'checkFileExist').mockReturnValue(true); + const result = { + body: { error: 0, message: 'Log has been added', statusCode: 200 }, + }; + const mockContext = buildMockContext(); + const mockResponse = buildMockResponse(); const mockRequest = buildMockRequest(); mockRequest.body = { level: 'error', @@ -47,18 +47,11 @@ describe('Spec UiLogsCtrl', function () { }; const controller = new UiLogsCtrl(); - await controller.createUiLogs(mockRequest, mockResponse); + await controller.createUiLogs(mockContext, mockRequest, mockResponse); expect(mockResponse.ok).toHaveBeenCalledTimes(1); expect(mockResponse.ok.mock.calls.length).toBe(1); expect(mockResponse.ok).toHaveBeenCalledWith(result); }); - - it('Should return a Array logs', async () => { - const controller = new UiLogsCtrl(); - let res = await controller.getUiFileLogs(WAZUH_UI_LOGS_RAW_PATH); - - expect(Array.isArray(res)).toBe(true); - }); }); }); diff --git a/plugins/main/server/controllers/wazuh-utils/ui-logs.controller.ts b/plugins/main/server/controllers/wazuh-utils/ui-logs.controller.ts index 76afd26add..64fda22920 100644 --- a/plugins/main/server/controllers/wazuh-utils/ui-logs.controller.ts +++ b/plugins/main/server/controllers/wazuh-utils/ui-logs.controller.ts @@ -12,10 +12,11 @@ // Require some libraries import { ErrorResponse } from '../../lib/error-response'; -import { read } from 'read-last-lines'; -import { WAZUH_UI_LOGS_RAW_PATH } from '../../../common/constants'; -import { OpenSearchDashboardsRequest, OpenSearchDashboardsResponseFactory } from 'src/core/server'; -import uiLogger from '../../lib/ui-logger'; +import { + OpenSearchDashboardsRequest, + OpenSearchDashboardsResponseFactory, + RequestHandlerContext, +} from 'src/core/server'; export class UiLogsCtrl { /** @@ -25,45 +26,22 @@ export class UiLogsCtrl { constructor() {} /** - * Returns Wazuh ui logs - * @param {Object} response - * @returns {Array} app logs or ErrorResponse - */ - async getUiLogs(response: OpenSearchDashboardsResponseFactory) { - try { - return uiLogger.initDirectory().then(async () => { - if (!uiLogger.checkFileExist(WAZUH_UI_LOGS_RAW_PATH)) { - return response.ok({ - body: { - error: 0, - rawLogs: [], - }, - }); - } else { - let arrayLog = await this.getUiFileLogs(WAZUH_UI_LOGS_RAW_PATH); - return response.ok({ - body: { - error: 0, - rawLogs: arrayLog.filter((item) => typeof item === 'string' && item.length), - }, - }); - } - }); - } catch (error) { - return ErrorResponse(error.message || error, 3036, 500, response); - } - } - - /** - * Add new UI Log entry in ui logs file + * Add new UI Log entry to the platform logs + * @param context * @param request * @param response * @returns success message or ErrorResponse */ - async createUiLogs(request: OpenSearchDashboardsRequest, response: OpenSearchDashboardsResponseFactory) { + async createUiLogs( + context: RequestHandlerContext, + request: OpenSearchDashboardsRequest, + response: OpenSearchDashboardsResponseFactory, + ) { try { const { location, message, level } = request.body; - await uiLogger.log(location, message, level); + const loggerUI = context.wazuh.logger.get('ui'); + const loggerByLevel = loggerUI?.[level] || loggerUI.error; + loggerByLevel(`${location}: ${message}`); return response.ok({ body: { statusCode: 200, @@ -75,18 +53,4 @@ export class UiLogsCtrl { return ErrorResponse(error.message || error, 3021, 500, response); } } - - /** - * Get UI logs from specific log file - * @param filepath - * @returns Array - */ - async getUiFileLogs(filepath) { - try { - const lastLogs = await read(filepath, 50); - return lastLogs.split('\n'); - } catch (err) { - throw err; - } - } } diff --git a/plugins/main/server/controllers/wazuh-utils/wazuh-utils.ts b/plugins/main/server/controllers/wazuh-utils/wazuh-utils.ts index dfaba3b656..e3a7f856e4 100644 --- a/plugins/main/server/controllers/wazuh-utils/wazuh-utils.ts +++ b/plugins/main/server/controllers/wazuh-utils/wazuh-utils.ts @@ -14,11 +14,16 @@ import { ErrorResponse } from '../../lib/error-response'; import { getConfiguration } from '../../lib/get-configuration'; import { read } from 'read-last-lines'; -import { UpdateConfigurationFile } from '../../lib/update-configuration'; import jwtDecode from 'jwt-decode'; -import { WAZUH_ROLE_ADMINISTRATOR_ID, WAZUH_DATA_LOGS_RAW_PATH, PLUGIN_SETTINGS } from '../../../common/constants'; -import { ManageHosts } from '../../lib/manage-hosts'; -import { OpenSearchDashboardsRequest, RequestHandlerContext, OpenSearchDashboardsResponseFactory } from 'src/core/server'; +import { + WAZUH_ROLE_ADMINISTRATOR_ID, + PLUGIN_SETTINGS, +} from '../../../common/constants'; +import { + OpenSearchDashboardsRequest, + RequestHandlerContext, + OpenSearchDashboardsResponseFactory, +} from 'src/core/server'; import { getCookieValueByName } from '../../lib/cookie'; import fs from 'fs'; import path from 'path'; @@ -26,17 +31,13 @@ import { createDirectoryIfNotExists } from '../../lib/filesystem'; import glob from 'glob'; import { getFileExtensionFromBuffer } from '../../../common/services/file-extension'; -const updateConfigurationFile = new UpdateConfigurationFile(); - // TODO: these controllers have no logs. We should include them. export class WazuhUtilsCtrl { /** * Constructor * @param {*} server */ - constructor() { - this.manageHosts = new ManageHosts(); - } + constructor() {} /** * Returns the wazuh.yml file parsed @@ -45,7 +46,11 @@ export class WazuhUtilsCtrl { * @param {Object} response * @returns {Object} Configuration File or ErrorResponse */ - getConfigurationFile(context: RequestHandlerContext, request: OpenSearchDashboardsRequest, response: OpenSearchDashboardsResponseFactory) { + getConfigurationFile( + context: RequestHandlerContext, + request: OpenSearchDashboardsRequest, + response: OpenSearchDashboardsResponseFactory, + ) { try { const configFile = getConfiguration(); @@ -53,8 +58,8 @@ export class WazuhUtilsCtrl { body: { statusCode: 200, error: 0, - data: configFile || {} - } + data: configFile || {}, + }, }); } catch (error) { return ErrorResponse(error.message || error, 3019, 500, response); @@ -68,64 +73,74 @@ export class WazuhUtilsCtrl { * @param {Object} response * @returns {Object} Configuration File or ErrorResponse */ - updateConfigurationFile = this.routeDecoratorProtectedAdministratorRoleValidToken( - async (context: RequestHandlerContext, request: OpenSearchDashboardsRequest, response: OpenSearchDashboardsResponseFactory) => { - - let requiresRunningHealthCheck: boolean = false, - requiresReloadingBrowserTab: boolean = false, - requiresRestartingPluginPlatform: boolean = false; - - // Plugin settings configurables in the configuration file. - const pluginSettingsConfigurableFile = Object.keys(request.body) - .filter(pluginSettingKey => PLUGIN_SETTINGS[pluginSettingKey].isConfigurableFromFile) - .reduce((accum, pluginSettingKey: string) => ({ ...accum, [pluginSettingKey]: request.body[pluginSettingKey] }), {}); + updateConfigurationFile = + this.routeDecoratorProtectedAdministratorRoleValidToken( + async ( + context: RequestHandlerContext, + request: OpenSearchDashboardsRequest, + response: OpenSearchDashboardsResponseFactory, + ) => { + let requiresRunningHealthCheck: boolean = false, + requiresReloadingBrowserTab: boolean = false, + requiresRestartingPluginPlatform: boolean = false; - if (Object.keys(pluginSettingsConfigurableFile).length) { - // Update the configuration file. - await updateConfigurationFile.updateConfiguration(pluginSettingsConfigurableFile); + // Plugin settings configurables in the configuration file. + const pluginSettingsConfigurableFile = Object.keys(request.body) + .filter( + pluginSettingKey => + PLUGIN_SETTINGS[pluginSettingKey].isConfigurableFromFile, + ) + .reduce( + (accum, pluginSettingKey: string) => ({ + ...accum, + [pluginSettingKey]: request.body[pluginSettingKey], + }), + {}, + ); - requiresRunningHealthCheck = Object.keys(pluginSettingsConfigurableFile).some((pluginSettingKey: string) => Boolean(PLUGIN_SETTINGS[pluginSettingKey].requiresRunningHealthCheck)) || requiresRunningHealthCheck; - requiresReloadingBrowserTab = Object.keys(pluginSettingsConfigurableFile).some((pluginSettingKey: string) => Boolean(PLUGIN_SETTINGS[pluginSettingKey].requiresReloadingBrowserTab)) || requiresReloadingBrowserTab; - requiresRestartingPluginPlatform = Object.keys(pluginSettingsConfigurableFile).some((pluginSettingKey: string) => Boolean(PLUGIN_SETTINGS[pluginSettingKey].requiresRestartingPluginPlatform)) || requiresRestartingPluginPlatform; - }; + if (Object.keys(pluginSettingsConfigurableFile).length) { + // Update the configuration file. + await context.wazuh_core.updateConfigurationFile.updateConfiguration( + pluginSettingsConfigurableFile, + ); - return response.ok({ - body: { - data: { requiresRunningHealthCheck, requiresReloadingBrowserTab, requiresRestartingPluginPlatform, updatedConfiguration: pluginSettingsConfigurableFile } + requiresRunningHealthCheck = + Object.keys(pluginSettingsConfigurableFile).some( + (pluginSettingKey: string) => + Boolean( + PLUGIN_SETTINGS[pluginSettingKey].requiresRunningHealthCheck, + ), + ) || requiresRunningHealthCheck; + requiresReloadingBrowserTab = + Object.keys(pluginSettingsConfigurableFile).some( + (pluginSettingKey: string) => + Boolean( + PLUGIN_SETTINGS[pluginSettingKey].requiresReloadingBrowserTab, + ), + ) || requiresReloadingBrowserTab; + requiresRestartingPluginPlatform = + Object.keys(pluginSettingsConfigurableFile).some( + (pluginSettingKey: string) => + Boolean( + PLUGIN_SETTINGS[pluginSettingKey] + .requiresRestartingPluginPlatform, + ), + ) || requiresRestartingPluginPlatform; } - }); - }, - 3021 - ) - /** - * Returns Wazuh app logs - * @param {Object} context - * @param {Object} request - * @param {Object} response - * @returns {Array} app logs or ErrorResponse - */ - async getAppLogs(context: RequestHandlerContext, request: OpenSearchDashboardsRequest, response: OpenSearchDashboardsResponseFactory) { - try { - const lastLogs = await read( - WAZUH_DATA_LOGS_RAW_PATH, - 50 - ); - const spliterLog = lastLogs.split('\n'); - return spliterLog && Array.isArray(spliterLog) - ? response.ok({ + return response.ok({ body: { - error: 0, - lastLogs: spliterLog.filter( - item => typeof item === 'string' && item.length - ) - } - }) - : response.ok({ error: 0, lastLogs: [] }); - } catch (error) { - return ErrorResponse(error.message || error, 3036, 500, response); - } - } + data: { + requiresRunningHealthCheck, + requiresReloadingBrowserTab, + requiresRestartingPluginPlatform, + updatedConfiguration: pluginSettingsConfigurableFile, + }, + }, + }); + }, + 3021, + ); /** * Upload a file @@ -135,7 +150,11 @@ export class WazuhUtilsCtrl { * @returns {Object} Configuration File or ErrorResponse */ uploadFile = this.routeDecoratorProtectedAdministratorRoleValidToken( - async (context: RequestHandlerContext, request: KibanaRequest, response: KibanaResponseFactory) => { + async ( + context: RequestHandlerContext, + request: KibanaRequest, + response: KibanaResponseFactory, + ) => { const { key } = request.params; const { file: bufferFile } = request.body; const pluginSetting = PLUGIN_SETTINGS[key]; @@ -144,16 +163,24 @@ export class WazuhUtilsCtrl { const fileExtension = getFileExtensionFromBuffer(bufferFile); // Check if the extension is valid for the setting. - if (!pluginSetting.options.file.extensions.includes(`.${fileExtension}`)) { + if ( + !pluginSetting.options.file.extensions.includes(`.${fileExtension}`) + ) { return response.badRequest({ - body: `File extension is not valid for setting [${key}] setting. Allowed file extensions: ${pluginSetting.options.file.extensions.join(', ')}` + body: `File extension is not valid for setting [${key}] setting. Allowed file extensions: ${pluginSetting.options.file.extensions.join( + ', ', + )}`, }); - }; + } const fileNamePath = `${key}.${fileExtension}`; // Create target directory - const targetDirectory = path.join(__dirname, '../../..', pluginSetting.options.file.store.relativePathFileSystem); + const targetDirectory = path.join( + __dirname, + '../../..', + pluginSetting.options.file.store.relativePathFileSystem, + ); createDirectoryIfNotExists(targetDirectory); // Get the files related to the setting and remove them const files = glob.sync(path.join(targetDirectory, `${key}.*`)); @@ -163,24 +190,33 @@ export class WazuhUtilsCtrl { fs.writeFileSync(path.join(targetDirectory, fileNamePath), bufferFile); // Update the setting in the configuration cache - const pluginSettingValue = pluginSetting.options.file.store.resolveStaticURL(fileNamePath); - await updateConfigurationFile.updateConfiguration({ [key]: pluginSettingValue }); + const pluginSettingValue = + pluginSetting.options.file.store.resolveStaticURL(fileNamePath); + await context.wazuh_core.updateConfigurationFile.updateConfiguration({ + [key]: pluginSettingValue, + }); return response.ok({ body: { data: { - requiresRunningHealthCheck: Boolean(pluginSetting.requiresRunningHealthCheck), - requiresReloadingBrowserTab: Boolean(pluginSetting.requiresReloadingBrowserTab), - requiresRestartingPluginPlatform: Boolean(pluginSetting.requiresRestartingPluginPlatform), + requiresRunningHealthCheck: Boolean( + pluginSetting.requiresRunningHealthCheck, + ), + requiresReloadingBrowserTab: Boolean( + pluginSetting.requiresReloadingBrowserTab, + ), + requiresRestartingPluginPlatform: Boolean( + pluginSetting.requiresRestartingPluginPlatform, + ), updatedConfiguration: { - [key]: pluginSettingValue - } - } - } + [key]: pluginSettingValue, + }, + }, + }, }); }, - 3022 - ) + 3022, + ); /** * Delete a file @@ -190,64 +226,96 @@ export class WazuhUtilsCtrl { * @returns {Object} Configuration File or ErrorResponse */ deleteFile = this.routeDecoratorProtectedAdministratorRoleValidToken( - async (context: RequestHandlerContext, request: KibanaRequest, response: KibanaResponseFactory) => { + async ( + context: RequestHandlerContext, + request: KibanaRequest, + response: KibanaResponseFactory, + ) => { const { key } = request.params; const pluginSetting = PLUGIN_SETTINGS[key]; // Get the files related to the setting and remove them - const targetDirectory = path.join(__dirname, '../../..', pluginSetting.options.file.store.relativePathFileSystem); + const targetDirectory = path.join( + __dirname, + '../../..', + pluginSetting.options.file.store.relativePathFileSystem, + ); const files = glob.sync(path.join(targetDirectory, `${key}.*`)); files.forEach(fs.unlinkSync); // Update the setting in the configuration cache const pluginSettingValue = pluginSetting.defaultValue; - await updateConfigurationFile.updateConfiguration({ [key]: pluginSettingValue }); + await context.wazuh_core.updateConfigurationFile.updateConfiguration({ + [key]: pluginSettingValue, + }); return response.ok({ body: { - message: 'All files were removed and the configuration file was updated.', + message: + 'All files were removed and the configuration file was updated.', data: { - requiresRunningHealthCheck: Boolean(pluginSetting.requiresRunningHealthCheck), - requiresReloadingBrowserTab: Boolean(pluginSetting.requiresReloadingBrowserTab), - requiresRestartingPluginPlatform: Boolean(pluginSetting.requiresRestartingPluginPlatform), + requiresRunningHealthCheck: Boolean( + pluginSetting.requiresRunningHealthCheck, + ), + requiresReloadingBrowserTab: Boolean( + pluginSetting.requiresReloadingBrowserTab, + ), + requiresRestartingPluginPlatform: Boolean( + pluginSetting.requiresRestartingPluginPlatform, + ), updatedConfiguration: { - [key]: pluginSettingValue - } - } - } + [key]: pluginSettingValue, + }, + }, + }, }); }, - 3023 - ) + 3023, + ); - private routeDecoratorProtectedAdministratorRoleValidToken(routeHandler, errorCode: number) { + private routeDecoratorProtectedAdministratorRoleValidToken( + routeHandler, + errorCode: number, + ) { return async (context, request, response) => { try { // Check if user has administrator role in token const token = getCookieValueByName(request.headers.cookie, 'wz-token'); if (!token) { return ErrorResponse('No token provided', 401, 401, response); - }; + } const decodedToken = jwtDecode(token); if (!decodedToken) { return ErrorResponse('No permissions in token', 401, 401, response); - }; - if (!decodedToken.rbac_roles || !decodedToken.rbac_roles.includes(WAZUH_ROLE_ADMINISTRATOR_ID)) { + } + if ( + !decodedToken.rbac_roles || + !decodedToken.rbac_roles.includes(WAZUH_ROLE_ADMINISTRATOR_ID) + ) { return ErrorResponse('No administrator role', 401, 401, response); - }; + } // Check the provided token is valid - const apiHostID = getCookieValueByName(request.headers.cookie, 'wz-api'); + const apiHostID = getCookieValueByName( + request.headers.cookie, + 'wz-api', + ); if (!apiHostID) { return ErrorResponse('No API id provided', 401, 401, response); - }; - const responseTokenIsWorking = await context.wazuh.api.client.asCurrentUser.request('GET', '/', {}, { apiHostID }); + } + const responseTokenIsWorking = + await context.wazuh.api.client.asCurrentUser.request( + 'GET', + '/', + {}, + { apiHostID }, + ); if (responseTokenIsWorking.status !== 200) { return ErrorResponse('Token is not valid', 401, 401, response); - }; - return await routeHandler(context, request, response) + } + return await routeHandler(context, request, response); } catch (error) { return ErrorResponse(error.message || error, errorCode, 500, response); } - } + }; } } diff --git a/plugins/main/server/lib/api-interceptor.ts b/plugins/main/server/lib/api-interceptor.ts deleted file mode 100644 index 256eaede05..0000000000 --- a/plugins/main/server/lib/api-interceptor.ts +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Wazuh app - Interceptor API entries - * Copyright (C) 2015-2022 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 axios, { AxiosResponse } from 'axios'; -import { ManageHosts } from './manage-hosts'; -import https from 'https'; - -const httpsAgent = new https.Agent({ - rejectUnauthorized: false, -}); - -const _axios = axios.create({ httpsAgent }); - -interface APIHost{ - url: string - port: string - username: string - password: string -} - -export interface APIInterceptorRequestOptions{ - apiHostID: string - token: string - forceRefresh?: boolean -} - -export interface APIInterceptorRequestOptionsInternalUser{ - apiHostID: string - forceRefresh?: boolean -} - -const manageHosts = new ManageHosts(); - -// Cache to save the token for the internal user by API host ID -const CacheInternalUserAPIHostToken = new Map(); - -export const authenticate = async (apiHostID: string, authContext?: any): Promise => { - try{ - const api: APIHost = await manageHosts.getHostById(apiHostID); - const optionsRequest = { - method: 'POST', - headers: { - 'content-type': 'application/json', - }, - auth: { - username: api.username, - password: api.password, - }, - url: `${api.url}:${api.port}/security/user/authenticate${!!authContext ? '/run_as' : ''}`, - ...(!!authContext ? { data: authContext } : {}) - }; - - const response: AxiosResponse = await _axios(optionsRequest); - const token: string = (((response || {}).data || {}).data || {}).token; - if (!authContext) { - CacheInternalUserAPIHostToken.set(apiHostID, token); - }; - return token; - }catch(error){ - throw error; - } -}; - -const buildRequestOptions = async (method: string, path: string, data: any, { apiHostID, forceRefresh, token }: APIInterceptorRequestOptions) => { - const api = await manageHosts.getHostById(apiHostID); - const { body, params, headers, ...rest } = data; - return { - method: method, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer ' + token, - ...(headers ? headers : {}) - }, - data: body || rest || {}, - params: params || {}, - url: `${api.url}:${api.port}${path}`, - } -} - -export const requestAsInternalUser = async (method: string, path: string, data: any, options: APIInterceptorRequestOptionsInternalUser) => { - try{ - const token = CacheInternalUserAPIHostToken.has(options.apiHostID) && !options.forceRefresh - ? CacheInternalUserAPIHostToken.get(options.apiHostID) - : await authenticate(options.apiHostID); - return await request(method, path, data, {...options, token}); - }catch(error){ - if (error.response && error.response.status === 401) { - try{ - const token: string = await authenticate(options.apiHostID); - return await request(method, path, data, {...options, token}); - }catch(error){ - throw error; - } - } - throw error; - } -}; - -export const requestAsCurrentUser = async (method: string, path: string, data: any, options: APIInterceptorRequestOptions) => { - return await request(method, path, data, options) -}; - -const request = async (method: string, path: string, data: any, options: any): Promise => { - try{ - const optionsRequest = await buildRequestOptions(method, path, data, options); - const response: AxiosResponse = await _axios(optionsRequest); - return response; - }catch(error){ - throw error; - } -}; diff --git a/plugins/main/server/lib/base-logger.ts b/plugins/main/server/lib/base-logger.ts deleted file mode 100644 index cfc6d4f2b1..0000000000 --- a/plugins/main/server/lib/base-logger.ts +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Wazuh app - Settings controller - * Copyright (C) 2015-2022 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 winston from 'winston'; -import fs from 'fs'; -import path from 'path'; -import { getConfiguration } from './get-configuration'; -import { createDataDirectoryIfNotExists, createLogFileIfNotExists } from './filesystem'; - -import { WAZUH_DATA_LOGS_DIRECTORY_PATH, MAX_MB_LOG_FILES } from '../../common/constants'; - -export interface IUIPlainLoggerSettings { - level: string; - message?: string; - data?: any; -} - -export interface IUILoggerSettings extends IUIPlainLoggerSettings { - date: Date; - location: string; -} - -export class BaseLogger { - allowed: boolean = false; - wazuhLogger: winston.Logger | undefined = undefined; - wazuhPlainLogger: winston.Logger | undefined = undefined; - PLAIN_LOGS_PATH: string = ''; - PLAIN_LOGS_FILE_NAME: string = ''; - RAW_LOGS_PATH: string = ''; - RAW_LOGS_FILE_NAME: string = ''; - - constructor(plainLogsFile: string, rawLogsFile: string) { - this.PLAIN_LOGS_PATH = path.join(WAZUH_DATA_LOGS_DIRECTORY_PATH, plainLogsFile); - this.RAW_LOGS_PATH = path.join(WAZUH_DATA_LOGS_DIRECTORY_PATH, rawLogsFile); - this.PLAIN_LOGS_FILE_NAME = plainLogsFile; - this.RAW_LOGS_FILE_NAME = rawLogsFile; - } - - /** - * Initialize loggers, plain and raw logger - */ - private initLogger = () => { - const configurationFile = getConfiguration(); - const level = - typeof (configurationFile || {})['logs.level'] !== 'undefined' && - ['info', 'debug'].includes(configurationFile['logs.level']) - ? configurationFile['logs.level'] - : 'info'; - - // JSON logger - this.wazuhLogger = winston.createLogger({ - level, - format: winston.format.json(), - transports: [ - new winston.transports.File({ - filename: this.RAW_LOGS_PATH, - }), - ], - }); - - // Prevents from exit on error related to the logger. - this.wazuhLogger.exitOnError = false; - - // Plain text logger - this.wazuhPlainLogger = winston.createLogger({ - level, - format: winston.format.simple(), - transports: [ - new winston.transports.File({ - filename: this.PLAIN_LOGS_PATH, - }), - ], - }); - - // Prevents from exit on error related to the logger. - this.wazuhPlainLogger.exitOnError = false; - }; - - /** - * Checks if wazuh/logs exists. If it doesn't exist, it will be created. - */ - initDirectory = async () => { - try { - createDataDirectoryIfNotExists(); - createDataDirectoryIfNotExists('logs'); - if (typeof this.wazuhLogger === 'undefined' || typeof this.wazuhPlainLogger === 'undefined') { - this.initLogger(); - } - this.allowed = true; - return; - } catch (error) { - this.allowed = false; - return Promise.reject(error); - } - }; - - /** - * Returns given file size in MB, if the file doesn't exist returns 0 - * @param {*} filename Path to the file - */ - getFilesizeInMegaBytes = (filename) => { - if (this.allowed) { - if (fs.existsSync(filename)) { - const stats = fs.statSync(filename); - const fileSizeInMegaBytes = stats.size; - - return fileSizeInMegaBytes / 1000000.0; - } - } - return 0; - }; - - /** - * Check if file exist - * @param filename - * @returns boolean - */ - checkFileExist = (filename) => { - return fs.existsSync(filename); - }; - - rotateFiles = (file: string, pathFile: string, log?: string) => { - if (this.getFilesizeInMegaBytes(pathFile) >= MAX_MB_LOG_FILES) { - const fileExtension = path.extname(file); - const fileName = path.basename(file, fileExtension); - fs.renameSync( - pathFile, - `${WAZUH_DATA_LOGS_DIRECTORY_PATH}/${fileName}-${new Date().getTime()}${fileExtension}` - ); - if (log) { - fs.writeFileSync(pathFile, log + '\n'); - } - } - }; - - /** - * Checks if the wazuhapp.log file size is greater than 100MB, if so it rotates the file. - */ - private checkFiles = () => { - createLogFileIfNotExists(this.RAW_LOGS_PATH); - createLogFileIfNotExists(this.PLAIN_LOGS_PATH); - if (this.allowed) { - // check raw log file - this.rotateFiles( - this.RAW_LOGS_FILE_NAME, - this.RAW_LOGS_PATH, - JSON.stringify({ - date: new Date(), - level: 'info', - location: 'logger', - message: 'Rotated log file', - }) - ); - // check log file - this.rotateFiles(this.PLAIN_LOGS_FILE_NAME, this.PLAIN_LOGS_PATH); - } - }; - - /** - * Get Current Date - * @returns string - */ - private yyyymmdd = () => { - const now = new Date(); - const y = now.getFullYear(); - const m = now.getMonth() + 1; - const d = now.getDate(); - const seconds = now.getSeconds(); - const minutes = now.getMinutes(); - const hour = now.getHours(); - return `${y}/${m < 10 ? '0' : ''}${m}/${d < 10 ? '0' : ''}${d} ${hour}:${minutes}:${seconds}`; - }; - - /** - * This function filter some known interfaces to avoid log hug objects - * @param data string | object - * @returns the data parsed - */ - private parseData = (data: any) => { - let parsedData = - data instanceof Error - ? { - message: data.message, - stack: data.stack, - } - : data; - - // when error is AxiosError, it extends from Error - if (data.isAxiosError) { - const { config } = data; - parsedData = { - ...parsedData, - config: { - url: config.url, - method: config.method, - data: config.data, - params: config.params, - }, - }; - } - - if (typeof parsedData === 'object') parsedData.toString = () => JSON.stringify(parsedData); - - return parsedData; - }; - - /** - * Main function to add a new log - * @param {*} location File where the log is being thrown - * @param {*} data Message or object to log - * @param {*} level Optional, default is 'error' - */ - async log(location: string, data: any, level: string) { - const parsedData = this.parseData(data); - return this.initDirectory() - .then(() => { - if (this.allowed) { - this.checkFiles(); - const plainLogData: IUIPlainLoggerSettings = { - level: level || 'error', - message: `${this.yyyymmdd()}: ${location || 'Unknown origin'}: ${ - parsedData.toString() || 'An error occurred' - }`, - }; - - this.wazuhPlainLogger.log(plainLogData); - - const logData: IUILoggerSettings = { - date: new Date(), - level: level || 'error', - location: location || 'Unknown origin', - data: parsedData || 'An error occurred', - }; - - if (typeof data == 'string') { - logData.message = parsedData; - delete logData.data; - } - - this.wazuhLogger.log(logData); - } - }) - .catch((error) => { - console.error(`Cannot create the logs directory due to:\n${error.message || error}`); - throw error; - }); - } -} diff --git a/plugins/main/server/lib/cache-api-user-has-run-as.ts b/plugins/main/server/lib/cache-api-user-has-run-as.ts deleted file mode 100644 index 725ec3771b..0000000000 --- a/plugins/main/server/lib/cache-api-user-has-run-as.ts +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Wazuh app - Service which caches the API user allow run as - * Copyright (C) 2015-2022 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 ApiInterceptor from './api-interceptor'; -import { ManageHosts } from './manage-hosts'; -import { log } from './logger'; -// Private variable to save the cache -const _cache = {}; - -// Export an interface which interacts with the private cache object -export const CacheInMemoryAPIUserAllowRunAs = { - // Set an entry with API ID, username and allow_run_as - set: (apiID: string, username: string, allow_run_as : number): void => { - if(!_cache[apiID]){ - _cache[apiID] = {}; // Create a API ID entry if it doesn't exist in cache object - }; - _cache[apiID][username] = allow_run_as; - }, - // Get the value of an entry with API ID and username from cache - get: (apiID: string, username: string): number => _cache[apiID] && typeof _cache[apiID][username] !== 'undefined' ? _cache[apiID][username] : API_USER_STATUS_RUN_AS.ALL_DISABLED, - // Check if it exists the API ID and username in the cache - has: (apiID: string, username: string): boolean => _cache[apiID] && typeof _cache[apiID][username] !== 'undefined' ? true : false -}; - -const manageHosts = new ManageHosts(); - -export const APIUserAllowRunAs = { - async check(apiId: string): Promise{ - try{ - const api = await manageHosts.getHostById(apiId); - log('APIUserAllowRunAs:check', `Check if API user ${api.username} (${apiId}) has run_as`, 'debug'); - // Check if api.run_as is false or undefined, then it set to false in cache - if(!api.run_as){ - CacheInMemoryAPIUserAllowRunAs.set(apiId, api.username, API_USER_STATUS_RUN_AS.HOST_DISABLED); - }; - // Check if the API user is cached and returns it - if(CacheInMemoryAPIUserAllowRunAs.has(apiId, api.username)){ - return CacheInMemoryAPIUserAllowRunAs.get(apiId, api.username); - }; - const response = await ApiInterceptor.requestAsInternalUser( - 'get', - '/security/users/me', - {}, - { apiHostID: apiId } - ); - const statusUserAllowRunAs = response.data.data.affected_items[0].allow_run_as ? API_USER_STATUS_RUN_AS.ENABLED : API_USER_STATUS_RUN_AS.USER_NOT_ALLOWED; - - // Cache the run_as for the API user - CacheInMemoryAPIUserAllowRunAs.set(apiId, api.username, statusUserAllowRunAs); - return statusUserAllowRunAs; - }catch(error){ - log('APIUserAllowRunAs:check', error.message || error); - return API_USER_STATUS_RUN_AS.ALL_DISABLED; - } - }, - async canUse(apiId: string): Promise{ - const ApiUserCanUseStatus = await APIUserAllowRunAs.check(apiId); - if(ApiUserCanUseStatus === API_USER_STATUS_RUN_AS.USER_NOT_ALLOWED){ - const api = await manageHosts.getHostById(apiId); - throw new Error(`API with host ID [${apiId}] misconfigured. The Wazuh API user [${api.username}] is not allowed to use [run_as]. Allow it in the user configuration or set [run_as] host setting with [false] value.`); - } - return ApiUserCanUseStatus; - } -}; - -/** - * @example - * HOST = set in wazuh.yml config - * USER = set in user interface - * - * ALL_DISABLED - * binary 00 = decimal 0 ---> USER 0 y HOST 0 - * - * USER_NOT_ALLOWED - * binary 01 = decimal 1 ---> USER 0 y HOST 1 - * - * HOST_DISABLED - * binary 10 = decimal 2 ---> USER 1 y HOST 0 - * - * ENABLED - * binary 11 = decimal 3 ---> USER 1 y HOST 1 - */ -export enum API_USER_STATUS_RUN_AS{ - ALL_DISABLED = 0, // Wazuh HOST and USER API user configured with run_as=false or undefined - USER_NOT_ALLOWED = 1, // Wazuh HOST API user configured with run_as = TRUE in wazuh.yml but it has not run_as in Wazuh API - HOST_DISABLED = 2, // Wazuh HOST API user configured with run_as=false in wazuh.yml but it has not run_as in Wazuh API - ENABLED = 3 // Wazuh API user configured with run_as=true and allow run_as -} diff --git a/plugins/main/server/lib/logger.ts b/plugins/main/server/lib/logger.ts deleted file mode 100644 index c21394e4c4..0000000000 --- a/plugins/main/server/lib/logger.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Wazuh app - Module for logging functions - * Copyright (C) 2015-2022 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 { BaseLogger } from './base-logger'; -import { - WAZUH_DATA_LOGS_PLAIN_FILENAME, - WAZUH_DATA_LOGS_RAW_FILENAME, -} from '../../common/constants'; - -const logger = new BaseLogger(WAZUH_DATA_LOGS_PLAIN_FILENAME, WAZUH_DATA_LOGS_RAW_FILENAME); - -export const log = (location, message, level) => { - logger.log(location, message, level); -}; diff --git a/plugins/main/server/lib/manage-hosts.ts b/plugins/main/server/lib/manage-hosts.ts deleted file mode 100644 index 65959623eb..0000000000 --- a/plugins/main/server/lib/manage-hosts.ts +++ /dev/null @@ -1,387 +0,0 @@ -/* - * Wazuh app - Module to update the configuration file - * Copyright (C) 2015-2022 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 fs from 'fs'; -import yml from 'js-yaml'; -import { log } from './logger'; -import { UpdateRegistry } from './update-registry'; -import { initialWazuhConfig } from './initial-wazuh-config'; -import { WAZUH_DATA_CONFIG_APP_PATH } from '../../common/constants'; -import { createDataDirectoryIfNotExists } from '../lib/filesystem'; - -export class ManageHosts { - busy: boolean; - file: string; - updateRegistry: UpdateRegistry; - initialConfig: string; - constructor() { - this.busy = false; - this.file = WAZUH_DATA_CONFIG_APP_PATH; - this.updateRegistry = new UpdateRegistry(); - this.initialConfig = initialWazuhConfig; - } - - /** - * Composes the host structure - * @param {Object} host - * @param {String} id - */ - composeHost(host, id) { - try { - log('manage-hosts:composeHost', 'Composing host', 'debug'); - return ` - ${!id ? new Date().getTime() : id}: - url: ${host.url} - port: ${host.port} - username: ${host.username || host.user} - password: ${host.password}`; - } catch (error) { - log('manage-hosts:composeHost', error.message || error); - throw error; - } - } - - /** - * Regex to build the host - * @param {Object} host - */ - composeRegex(host) { - try { - const hostId = Object.keys(host)[0]; - const reg = `\\s*-\\s*${hostId}\\s*:\\s*\\n*\\s*url\\s*:\\s*\\S*\\s*\\n*\\s*port\\s*:\\s*\\S*\\s*\\n*\\s*username\\s*:\\s*\\S*\\s*\\n*\\s*password\\s*:\\s*\\S*`; - log('manage-hosts:composeRegex', 'Composing regex', 'debug'); - return new RegExp(`${reg}`, 'gm'); - } catch (error) { - log('manage-hosts:composeRegex', error.message || error); - throw error; - } - } - - /** - * Returns the hosts in the wazuh.yml - */ - async getHosts() { - try { - this.checkBusy(); - this.busy = true; - createDataDirectoryIfNotExists(); - createDataDirectoryIfNotExists('config'); - if (!fs.existsSync(WAZUH_DATA_CONFIG_APP_PATH)) { - await fs.writeFileSync(this.file, this.initialConfig, { - encoding: 'utf8', - mode: 0o600, - }); - } - const raw = fs.readFileSync(this.file, { encoding: 'utf-8' }); - this.busy = false; - const content = yml.load(raw); - log('manage-hosts:getHosts', 'Getting hosts', 'debug'); - const entries = (content || {})['hosts'] || []; - return entries; - } catch (error) { - this.busy = false; - log('manage-hosts:getHosts', error.message || error); - return Promise.reject(error); - } - } - - /** - * This function checks if the hosts: key exists in the wazuh.yml for preventing duplicate in case of there's not any host defined - */ - async checkIfHostsKeyExists() { - try { - log('manage-hosts:checkIfHostsKeyExists', 'Checking hosts key', 'debug'); - this.busy = true; - const raw = fs.readFileSync(this.file, { encoding: 'utf-8' }); - this.busy = false; - const content = yml.load(raw); - return Object.keys(content || {}).includes('hosts'); - } catch (error) { - log('manage-hosts:checkIfHostsKeyExists', error.message || error); - this.busy = false; - return Promise.reject(error); - } - } - - /** - * Returns the IDs of the current hosts in the wazuh.yml - */ - async getCurrentHostsIds() { - try { - const hosts = await this.getHosts(); - const ids = hosts.map(h => { - return Object.keys(h)[0]; - }); - log('manage-hosts:getCurrentHostsIds', 'Getting hosts ids', 'debug'); - return ids; - } catch (error) { - log('manage-hosts:getCurrentHostsIds', error.message || error); - return Promise.reject(error); - } - } - - /** - * Get host by id - * @param {String} id - */ - async getHostById(id) { - try { - log('manage-hosts:getHostById', `Getting host ${id}`, 'debug'); - const hosts = await this.getHosts(); - const host = hosts.filter(h => { - return Object.keys(h)[0] == id; - }); - if (host && !host.length) { - throw new Error('Selected API is no longer available in wazuh.yml'); - } - const key = Object.keys(host[0])[0]; - const result = Object.assign(host[0][key], { id: key }) || {}; - return result; - } catch (error) { - log('manage-hosts:getHostById', error.message || error); - return Promise.reject(error); - } - } - - /** - * Decodes the API password - * @param {String} password - */ - decodeApiPassword(password) { - return Buffer.from(password, 'base64').toString('ascii'); - } - - /** - * Iterate the array with the API entries in given from the .wazuh index in order to create a valid array - * @param {Object} apiEntries - */ - transformIndexedApis(apiEntries) { - const entries = []; - try { - apiEntries.map(entry => { - const id = entry._id; - const host = entry._source; - const api = { - id: id, - url: host.url, - port: host.api_port, - username: host.api_username, - password: this.decodeApiPassword(host.api_password), - cluster_info: host.cluster_info, - }; - entries.push(api); - }); - log( - 'manage-hosts:transformIndexedApis', - 'Transforming index API schedule to wazuh.yml', - 'debug', - ); - } catch (error) { - log('manage-hosts:transformIndexedApis', error.message || error); - throw error; - } - return entries; - } - - /** - * Calls transformIndexedApis() to get the entries to migrate and after that calls addSeveralHosts() - * @param {Object} apiEntries - */ - async migrateFromIndex(apiEntries) { - try { - const apis = this.transformIndexedApis(apiEntries); - return await this.addSeveralHosts(apis); - } catch (error) { - log('manage-hosts:migrateFromIndex', error.message || error); - return Promise.reject(error); - } - } - - /** - * Receives an array of hosts and checks if any host is already in the wazuh.yml, in this case is removed from the received array and returns the resulting array - * @param {Array} hosts - */ - async cleanExistingHosts(hosts) { - try { - const currentHosts = await this.getCurrentHostsIds(); - const cleanHosts = hosts.filter(h => { - return !currentHosts.includes(h.id); - }); - log( - 'manage-hosts:cleanExistingHosts', - 'Preventing add existings hosts', - 'debug', - ); - return cleanHosts; - } catch (error) { - log('manage-hosts:cleanExistingHosts', error.message || error); - return Promise.reject(error); - } - } - - /** - * Throws an error is the wazuh.yml is busy - */ - checkBusy() { - if (this.busy) - throw new Error('Another process is writting the configuration file'); - } - - /** - * Recursive function used to add several APIs entries - * @param {Array} hosts - */ - async addSeveralHosts(hosts) { - try { - log('manage-hosts:addSeveralHosts', 'Adding several', 'debug'); - const hostsToAdd = await this.cleanExistingHosts(hosts); - if (!hostsToAdd.length) return 'There are not APIs entries to migrate'; - for (let idx in hostsToAdd) { - const entry = hostsToAdd[idx]; - await this.addHost(entry); - } - return 'All APIs entries were migrated to the wazuh.yml'; - } catch (error) { - log('manage-hosts:addSeveralHosts', error.message || error); - return Promise.reject(error); - } - } - - /** - * Add a single host - * @param {Obeject} host - */ - async addHost(host) { - const id = host.id || new Date().getTime(); - const compose = this.composeHost(host, id); - let data = await fs.readFileSync(this.file, { encoding: 'utf-8' }); - try { - this.checkBusy(); - const hosts = (await this.getHosts()) || []; - this.busy = true; - if (!hosts.length) { - const hostsExists = await this.checkIfHostsKeyExists(); - const result = !hostsExists - ? `${data}\nhosts:\n${compose}\n` - : `${data}\n${compose}\n`; - await fs.writeFileSync(this.file, result, 'utf8'); - } else { - const lastHost = (hosts || []).pop(); - if (lastHost) { - const lastHostObject = this.composeHost( - lastHost[Object.keys(lastHost)[0]], - Object.keys(lastHost)[0], - ); - const regex = this.composeRegex(lastHost); - const replace = data.replace( - regex, - `\n${lastHostObject}\n${compose}\n`, - ); - await fs.writeFileSync(this.file, replace, 'utf8'); - } - } - this.busy = false; - this.updateRegistry.migrateToRegistry( - id, - host.cluster_info, - ); - log('manage-hosts:addHost', `Host ${id} was properly added`, 'debug'); - return id; - } catch (error) { - this.busy = false; - log('manage-hosts:addHost', error.message || error); - return Promise.reject(error); - } - } - - /** - * Delete a host from the wazuh.yml - * @param {Object} req - */ - async deleteHost(req) { - let data = await fs.readFileSync(this.file, { encoding: 'utf-8' }); - try { - this.checkBusy(); - const hosts = (await this.getHosts()) || []; - this.busy = true; - if (!hosts.length) { - throw new Error('There are not configured hosts.'); - } else { - const hostsNumber = hosts.length; - const target = (hosts || []).find(element => { - return Object.keys(element)[0] === req.params.id; - }); - if (!target) { - throw new Error(`Host ${req.params.id} not found.`); - } - const regex = this.composeRegex(target); - const result = data.replace(regex, ``); - await fs.writeFileSync(this.file, result, 'utf8'); - if (hostsNumber === 1) { - data = await fs.readFileSync(this.file, { encoding: 'utf-8' }); - const clearHosts = data.replace( - new RegExp(`hosts:\\s*[\\n\\r]`, 'gm'), - '', - ); - await fs.writeFileSync(this.file, clearHosts, 'utf8'); - } - } - this.busy = false; - log( - 'manage-hosts:deleteHost', - `Host ${req.params.id} was properly deleted`, - 'debug', - ); - return true; - } catch (error) { - this.busy = false; - log('manage-hosts:deleteHost', error.message || error); - return Promise.reject(error); - } - } - - /** - * Updates the hosts information - * @param {String} id - * @param {Object} host - */ - async updateHost(id, host) { - let data = await fs.readFileSync(this.file, { encoding: 'utf-8' }); - try { - this.checkBusy(); - const hosts = (await this.getHosts()) || []; - this.busy = true; - if (!hosts.length) { - throw new Error('There are not configured hosts.'); - } else { - const target = (hosts || []).find(element => { - return Object.keys(element)[0] === id; - }); - if (!target) { - throw new Error(`Host ${id} not found.`); - } - const regex = this.composeRegex(target); - const result = data.replace(regex, `\n${this.composeHost(host, id)}`); - await fs.writeFileSync(this.file, result, 'utf8'); - } - this.busy = false; - log( - 'manage-hosts:updateHost', - `Host ${id} was properly updated`, - 'debug', - ); - return true; - } catch (error) { - this.busy = false; - log('manage-hosts:updateHost', error.message || error); - return Promise.reject(error); - } - } -} diff --git a/plugins/main/server/lib/parse-cron.ts b/plugins/main/server/lib/parse-cron.ts index 5330e065d2..45927118e7 100644 --- a/plugins/main/server/lib/parse-cron.ts +++ b/plugins/main/server/lib/parse-cron.ts @@ -9,7 +9,6 @@ * * Find more information about this on the LICENSE file. */ -import { log } from './logger'; import cron from 'node-cron'; import { WAZUH_MONITORING_DEFAULT_CRON_FREQ } from '../../common/constants'; @@ -17,34 +16,30 @@ export function parseCron(interval: string) { try { if (!interval) throw new Error('Interval not found'); - const intervalToNumber = parseInt(interval); + const intervalToNumber: number = parseInt(interval); - if (!intervalToNumber || typeof intervalToNumber !== 'number'){ + if (!intervalToNumber || typeof intervalToNumber !== 'number') { throw new Error('Interval not valid'); - }; - if (intervalToNumber < 60){ // 60 seconds / 1 minute + } + if (intervalToNumber < 60) { + // 60 seconds / 1 minute throw new Error('Interval too low'); - }; - if (intervalToNumber >= 84600){ + } + if (intervalToNumber >= 84600) { throw new Error('Interval too high'); - } + } const minutes = parseInt(intervalToNumber / 60); const cronstr = `0 */${minutes} * * * *`; - if (!cron.validate(cronstr)){ + if (!cron.validate(cronstr)) { throw new Error( - 'Generated cron expression not valid for node-cron module' + 'Generated cron expression not valid for node-cron module', ); } - log('cron:parse-interval', `Using the next interval: ${cronstr}`, 'debug'); return cronstr; } catch (error) { - log( - 'cron:parse-interval', - `Using default value ${WAZUH_MONITORING_DEFAULT_CRON_FREQ} due to: ${error.message || error}` - ); return WAZUH_MONITORING_DEFAULT_CRON_FREQ; } } diff --git a/plugins/main/server/lib/reporting/extended-information.ts b/plugins/main/server/lib/reporting/extended-information.ts index 377ba9408c..ffb0b8f89d 100644 --- a/plugins/main/server/lib/reporting/extended-information.ts +++ b/plugins/main/server/lib/reporting/extended-information.ts @@ -1,4 +1,3 @@ -import { log } from '../logger'; import SummaryTable from './summary-table'; import summaryTablesDefinitions from './summary-tables-definitions'; import * as VulnerabilityRequest from './vulnerability-request'; @@ -16,33 +15,41 @@ import { ReportPrinter } from './printer'; import moment from 'moment'; import { getSettingDefaultValue } from '../../../common/services/settings'; - - - /** - * This build the agents table - * @param {Array} ids ids of agents - * @param {String} apiId API id - */ -export async function buildAgentsTable(context, printer: ReportPrinter, agentIDs: string[], apiId: string, groupID: string = '') { + * This build the agents table + * @param {Array} ids ids of agents + * @param {String} apiId API id + */ +export async function buildAgentsTable( + context, + printer: ReportPrinter, + agentIDs: string[], + apiId: string, + groupID: string = '', +) { const dateFormat = await context.core.uiSettings.client.get('dateFormat'); if ((!agentIDs || !agentIDs.length) && !groupID) return; - log('reporting:buildAgentsTable', `${agentIDs.length} agents for API ${apiId}`, 'info'); + printer.logger.debug(`${agentIDs.length} agents for API ${apiId}`); try { let agentsData = []; if (groupID) { let totalAgentsInGroup = null; do { - const { data: { data: { affected_items, total_affected_items } } } = await context.wazuh.api.client.asCurrentUser.request( + const { + data: { + data: { affected_items, total_affected_items }, + }, + } = await context.wazuh.api.client.asCurrentUser.request( 'GET', `/groups/${groupID}/agents`, { params: { offset: agentsData.length, - select: 'dateAdd,id,ip,lastKeepAlive,manager,name,os.name,os.version,version', - } + select: + 'dateAdd,id,ip,lastKeepAlive,manager,name,os.name,os.version,version', + }, }, - { apiHostID: apiId } + { apiHostID: apiId }, ); !totalAgentsInGroup && (totalAgentsInGroup = total_affected_items); agentsData = [...agentsData, ...affected_items]; @@ -50,24 +57,27 @@ export async function buildAgentsTable(context, printer: ReportPrinter, agentIDs } else { for (const agentID of agentIDs) { try { - const { data: { data: { affected_items: [agent] } } } = await context.wazuh.api.client.asCurrentUser.request( + const { + data: { + data: { + affected_items: [agent], + }, + }, + } = await context.wazuh.api.client.asCurrentUser.request( 'GET', `/agents`, { params: { q: `id=${agentID}`, - select: 'dateAdd,id,ip,lastKeepAlive,manager,name,os.name,os.version,version', - } + select: + 'dateAdd,id,ip,lastKeepAlive,manager,name,os.name,os.version,version', + }, }, - { apiHostID: apiId } + { apiHostID: apiId }, ); agentsData.push(agent); } catch (error) { - log( - 'reporting:buildAgentsTable', - `Skip agent due to: ${error.message || error}`, - 'debug' - ); + printer.logger.debug(`Skip agent due to: ${error.message || error}`); } } } @@ -87,13 +97,16 @@ export async function buildAgentsTable(context, printer: ReportPrinter, agentIDs ], items: agentsData .filter(agent => agent) // Remove undefined agents when Wazuh API no longer finds and agentID - .map((agent) => { + .map(agent => { return { ...agent, - os: (agent.os && agent.os.name && agent.os.version) ? `${agent.os.name} ${agent.os.version}` : '', + os: + agent.os && agent.os.name && agent.os.version + ? `${agent.os.name} ${agent.os.version}` + : '', lastKeepAlive: moment(agent.lastKeepAlive).format(dateFormat), - dateAdd: moment(agent.dateAdd).format(dateFormat) - } + dateAdd: moment(agent.dateAdd).format(dateFormat), + }; }), }); } else if (!agentsData.length && groupID) { @@ -103,9 +116,8 @@ export async function buildAgentsTable(context, printer: ReportPrinter, agentIDs style: { fontSize: 12, color: '#000' }, }); } - } catch (error) { - log('reporting:buildAgentsTable', error.message || error); + printer.logger.error(error.message || error); return Promise.reject(error); } } @@ -138,36 +150,34 @@ export async function extendedInformation( agent = null, ) { try { - log( - 'reporting:extendedInformation', - `Section ${section} and tab ${tab}, API is ${apiId}. From ${from} to ${to}. Filters ${JSON.stringify(filters)}. Index pattern ${pattern}`, - 'info' + printer.logger.debug( + `Section ${section} and tab ${tab}, API is ${apiId}. From ${from} to ${to}. Filters ${JSON.stringify( + filters, + )}. Index pattern ${pattern}`, ); if (section === 'agents' && !agent) { - throw new Error('Reporting for specific agent needs an agent ID in order to work properly'); + throw new Error( + 'Reporting for specific agent needs an agent ID in order to work properly', + ); } const agents = await context.wazuh.api.client.asCurrentUser.request( 'GET', '/agents', { params: { limit: 1 } }, - { apiHostID: apiId } + { apiHostID: apiId }, ); const totalAgents = agents.data.data.total_affected_items; //--- OVERVIEW - VULS if (section === 'overview' && tab === 'vuls') { - log( - 'reporting:extendedInformation', - 'Fetching overview vulnerability detector metrics', - 'debug' - ); + printer.logger.debug('Fetching overview vulnerability detector metrics'); const vulnerabilitiesLevels = ['Low', 'Medium', 'High', 'Critical']; const vulnerabilitiesResponsesCount = ( await Promise.all( - vulnerabilitiesLevels.map(async (vulnerabilitiesLevel) => { + vulnerabilitiesLevels.map(async vulnerabilitiesLevel => { try { const count = await VulnerabilityRequest.uniqueSeverityCount( context, @@ -176,25 +186,23 @@ export async function extendedInformation( vulnerabilitiesLevel, filters, allowedAgentsFilter, - pattern + pattern, ); return count ? `${count} of ${totalAgents} agents have ${vulnerabilitiesLevel.toLocaleLowerCase()} vulnerabilities.` : undefined; - } catch (error) { } - }) + } catch (error) {} + }), ) - ).filter((vulnerabilitiesResponse) => vulnerabilitiesResponse); + ).filter(vulnerabilitiesResponse => vulnerabilitiesResponse); printer.addList({ title: { text: 'Summary', style: 'h2' }, list: vulnerabilitiesResponsesCount, }); - log( - 'reporting:extendedInformation', + printer.logger.debug( 'Fetching overview vulnerability detector top 3 agents by category', - 'debug' ); const lowRank = await VulnerabilityRequest.topAgentCount( context, @@ -203,7 +211,7 @@ export async function extendedInformation( 'Low', filters, allowedAgentsFilter, - pattern + pattern, ); const mediumRank = await VulnerabilityRequest.topAgentCount( context, @@ -212,7 +220,7 @@ export async function extendedInformation( 'Medium', filters, allowedAgentsFilter, - pattern + pattern, ); const highRank = await VulnerabilityRequest.topAgentCount( context, @@ -221,7 +229,7 @@ export async function extendedInformation( 'High', filters, allowedAgentsFilter, - pattern + pattern, ); const criticalRank = await VulnerabilityRequest.topAgentCount( context, @@ -230,12 +238,10 @@ export async function extendedInformation( 'Critical', filters, allowedAgentsFilter, - pattern + pattern, ); - log( - 'reporting:extendedInformation', + printer.logger.debug( 'Adding overview vulnerability detector top 3 agents by category', - 'debug' ); if (criticalRank && criticalRank.length) { printer.addContentWithNewLine({ @@ -273,17 +279,18 @@ export async function extendedInformation( printer.addNewLine(); } - log( - 'reporting:extendedInformation', + printer.logger.debug( 'Fetching overview vulnerability detector top 3 CVEs', - 'debug' ); - const cveRank = await VulnerabilityRequest.topCVECount(context, from, to, filters, allowedAgentsFilter, pattern); - log( - 'reporting:extendedInformation', - 'Adding overview vulnerability detector top 3 CVEs', - 'debug' + const cveRank = await VulnerabilityRequest.topCVECount( + context, + from, + to, + filters, + allowedAgentsFilter, + pattern, ); + printer.logger.debug('Adding overview vulnerability detector top 3 CVEs'); if (cveRank && cveRank.length) { printer.addSimpleTable({ title: { text: 'Top 3 CVE', style: 'h2' }, @@ -291,18 +298,28 @@ export async function extendedInformation( { id: 'top', label: 'Top' }, { id: 'cve', label: 'CVE' }, ], - items: cveRank.map((item) => ({ top: cveRank.indexOf(item) + 1, cve: item })), + items: cveRank.map(item => ({ + top: cveRank.indexOf(item) + 1, + cve: item, + })), }); } } //--- OVERVIEW - GENERAL if (section === 'overview' && tab === 'general') { - log('reporting:extendedInformation', 'Fetching top 3 agents with level 15 alerts', 'debug'); + printer.logger.debug('Fetching top 3 agents with level 15 alerts'); - const level15Rank = await OverviewRequest.topLevel15(context, from, to, filters, allowedAgentsFilter, pattern); + const level15Rank = await OverviewRequest.topLevel15( + context, + from, + to, + filters, + allowedAgentsFilter, + pattern, + ); - log('reporting:extendedInformation', 'Adding top 3 agents with level 15 alerts', 'debug'); + printer.logger.debug('Adding top 3 agents with level 15 alerts'); if (level15Rank.length) { printer.addContent({ text: 'Top 3 agents with level 15 alerts', @@ -314,16 +331,16 @@ export async function extendedInformation( //--- OVERVIEW - PM if (section === 'overview' && tab === 'pm') { - log('reporting:extendedInformation', 'Fetching most common rootkits', 'debug'); + printer.logger.debug('Fetching most common rootkits'); const top5RootkitsRank = await RootcheckRequest.top5RootkitsDetected( context, from, to, filters, allowedAgentsFilter, - pattern + pattern, ); - log('reporting:extendedInformation', 'Adding most common rootkits', 'debug'); + printer.logger.debug('Adding most common rootkits'); if (top5RootkitsRank && top5RootkitsRank.length) { printer .addContentWithNewLine({ @@ -331,12 +348,11 @@ export async function extendedInformation( style: 'h2', }) .addContentWithNewLine({ - text: - 'Rootkits are a set of software tools that enable an unauthorized user to gain control of a computer system without being detected.', + text: 'Rootkits are a set of software tools that enable an unauthorized user to gain control of a computer system without being detected.', style: 'standard', }) .addSimpleTable({ - items: top5RootkitsRank.map((item) => { + items: top5RootkitsRank.map(item => { return { top: top5RootkitsRank.indexOf(item) + 1, name: item }; }), columns: [ @@ -345,14 +361,14 @@ export async function extendedInformation( ], }); } - log('reporting:extendedInformation', 'Fetching hidden pids', 'debug'); + printer.logger.debug('Fetching hidden pids'); const hiddenPids = await RootcheckRequest.agentsWithHiddenPids( context, from, to, filters, allowedAgentsFilter, - pattern + pattern, ); hiddenPids && printer.addContent({ @@ -371,7 +387,7 @@ export async function extendedInformation( to, filters, allowedAgentsFilter, - pattern + pattern, ); hiddenPorts && printer.addContent({ @@ -388,14 +404,14 @@ export async function extendedInformation( //--- OVERVIEW/AGENTS - PCI if (['overview', 'agents'].includes(section) && tab === 'pci') { - log('reporting:extendedInformation', 'Fetching top PCI DSS requirements', 'debug'); + printer.logger.debug('Fetching top PCI DSS requirements'); const topPciRequirements = await PCIRequest.topPCIRequirements( context, from, to, filters, allowedAgentsFilter, - pattern + pattern, ); printer.addContentWithNewLine({ text: 'Most common PCI DSS requirements alerts found', @@ -409,13 +425,18 @@ export async function extendedInformation( filters, allowedAgentsFilter, item, - pattern + pattern, ); - printer.addContentWithNewLine({ text: `Requirement ${item}`, style: 'h3' }); + printer.addContentWithNewLine({ + text: `Requirement ${item}`, + style: 'h3', + }); if (PCI[item]) { const content = - typeof PCI[item] === 'string' ? { text: PCI[item], style: 'standard' } : PCI[item]; + typeof PCI[item] === 'string' + ? { text: PCI[item], style: 'standard' } + : PCI[item]; printer.addContentWithNewLine(content); } @@ -434,14 +455,14 @@ export async function extendedInformation( //--- OVERVIEW/AGENTS - TSC if (['overview', 'agents'].includes(section) && tab === 'tsc') { - log('reporting:extendedInformation', 'Fetching top TSC requirements', 'debug'); + printer.logger.debug('Fetching top TSC requirements'); const topTSCRequirements = await TSCRequest.topTSCRequirements( context, from, to, filters, allowedAgentsFilter, - pattern + pattern, ); printer.addContentWithNewLine({ text: 'Most common TSC requirements alerts found', @@ -455,13 +476,18 @@ export async function extendedInformation( filters, allowedAgentsFilter, item, - pattern + pattern, ); - printer.addContentWithNewLine({ text: `Requirement ${item}`, style: 'h3' }); + printer.addContentWithNewLine({ + text: `Requirement ${item}`, + style: 'h3', + }); if (TSC[item]) { const content = - typeof TSC[item] === 'string' ? { text: TSC[item], style: 'standard' } : TSC[item]; + typeof TSC[item] === 'string' + ? { text: TSC[item], style: 'standard' } + : TSC[item]; printer.addContentWithNewLine(content); } @@ -480,14 +506,14 @@ export async function extendedInformation( //--- OVERVIEW/AGENTS - GDPR if (['overview', 'agents'].includes(section) && tab === 'gdpr') { - log('reporting:extendedInformation', 'Fetching top GDPR requirements', 'debug'); + printer.logger.debug('Fetching top GDPR requirements'); const topGdprRequirements = await GDPRRequest.topGDPRRequirements( context, from, to, filters, allowedAgentsFilter, - pattern + pattern, ); printer.addContentWithNewLine({ text: 'Most common GDPR requirements alerts found', @@ -501,13 +527,18 @@ export async function extendedInformation( filters, allowedAgentsFilter, item, - pattern + pattern, ); - printer.addContentWithNewLine({ text: `Requirement ${item}`, style: 'h3' }); + printer.addContentWithNewLine({ + text: `Requirement ${item}`, + style: 'h3', + }); if (GDPR && GDPR[item]) { const content = - typeof GDPR[item] === 'string' ? { text: GDPR[item], style: 'standard' } : GDPR[item]; + typeof GDPR[item] === 'string' + ? { text: GDPR[item], style: 'standard' } + : GDPR[item]; printer.addContentWithNewLine(content); } @@ -527,19 +558,18 @@ export async function extendedInformation( //--- OVERVIEW - AUDIT if (section === 'overview' && tab === 'audit') { - log( - 'reporting:extendedInformation', + printer.logger.debug( 'Fetching agents with high number of failed sudo commands', - 'debug' - ); - const auditAgentsNonSuccess = await AuditRequest.getTop3AgentsSudoNonSuccessful( - context, - from, - to, - filters, - allowedAgentsFilter, - pattern ); + const auditAgentsNonSuccess = + await AuditRequest.getTop3AgentsSudoNonSuccessful( + context, + from, + to, + filters, + allowedAgentsFilter, + pattern, + ); if (auditAgentsNonSuccess && auditAgentsNonSuccess.length) { printer.addContent({ text: 'Agents with high number of failed sudo commands', @@ -547,14 +577,15 @@ export async function extendedInformation( }); await buildAgentsTable(context, printer, auditAgentsNonSuccess, apiId); } - const auditAgentsFailedSyscall = await AuditRequest.getTop3AgentsFailedSyscalls( - context, - from, - to, - filters, - allowedAgentsFilter, - pattern - ); + const auditAgentsFailedSyscall = + await AuditRequest.getTop3AgentsFailedSyscalls( + context, + from, + to, + filters, + allowedAgentsFilter, + pattern, + ); if (auditAgentsFailedSyscall && auditAgentsFailedSyscall.length) { printer.addSimpleTable({ columns: [ @@ -562,7 +593,7 @@ export async function extendedInformation( { id: 'syscall_id', label: 'Syscall ID' }, { id: 'syscall_syscall', label: 'Syscall' }, ], - items: auditAgentsFailedSyscall.map((item) => ({ + items: auditAgentsFailedSyscall.map(item => ({ agent: item.agent, syscall_id: item.syscall.id, syscall_syscall: item.syscall.syscall, @@ -577,25 +608,41 @@ export async function extendedInformation( //--- OVERVIEW - FIM if (section === 'overview' && tab === 'fim') { - log('reporting:extendedInformation', 'Fetching top 3 rules for FIM', 'debug'); - const rules = await SyscheckRequest.top3Rules(context, from, to, filters, allowedAgentsFilter, pattern); + printer.logger.debug('Fetching top 3 rules for FIM'); + const rules = await SyscheckRequest.top3Rules( + context, + from, + to, + filters, + allowedAgentsFilter, + pattern, + ); if (rules && rules.length) { - printer.addContentWithNewLine({ text: 'Top 3 FIM rules', style: 'h2' }).addSimpleTable({ - columns: [ - { id: 'ruleID', label: 'Rule ID' }, - { id: 'ruleDescription', label: 'Description' }, - ], - items: rules, - title: { - text: 'Top 3 rules that are generating most alerts.', - style: 'standard', - }, - }); + printer + .addContentWithNewLine({ text: 'Top 3 FIM rules', style: 'h2' }) + .addSimpleTable({ + columns: [ + { id: 'ruleID', label: 'Rule ID' }, + { id: 'ruleDescription', label: 'Description' }, + ], + items: rules, + title: { + text: 'Top 3 rules that are generating most alerts.', + style: 'standard', + }, + }); } - log('reporting:extendedInformation', 'Fetching top 3 agents for FIM', 'debug'); - const agents = await SyscheckRequest.top3agents(context, from, to, filters, allowedAgentsFilter, pattern); + printer.logger.debug('Fetching top 3 agents for FIM'); + const agents = await SyscheckRequest.top3agents( + context, + from, + to, + filters, + allowedAgentsFilter, + pattern, + ); if (agents && agents.length) { printer.addContentWithNewLine({ @@ -603,8 +650,7 @@ export async function extendedInformation( style: 'h2', }); printer.addContentWithNewLine({ - text: - 'Top 3 agents that have most FIM alerts from level 7 to level 15. Take care about them.', + text: 'Top 3 agents that have most FIM alerts from level 7 to level 15. Take care about them.', style: 'standard', }); await buildAgentsTable(context, printer, agents, apiId); @@ -613,14 +659,14 @@ export async function extendedInformation( //--- AGENTS - AUDIT if (section === 'agents' && tab === 'audit') { - log('reporting:extendedInformation', `Fetching most common failed syscalls`, 'debug'); + printer.logger.debug('Fetching most common failed syscalls'); const auditFailedSyscall = await AuditRequest.getTopFailedSyscalls( context, from, to, filters, allowedAgentsFilter, - pattern + pattern, ); auditFailedSyscall && auditFailedSyscall.length && @@ -636,18 +682,15 @@ export async function extendedInformation( //--- AGENTS - FIM if (section === 'agents' && tab === 'fim') { - log( - 'reporting:extendedInformation', - `Fetching syscheck database for agent ${agent}`, - 'debug' - ); + printer.logger.debug(`Fetching syscheck database for agent ${agent}`); - const lastScanResponse = await context.wazuh.api.client.asCurrentUser.request( - 'GET', - `/syscheck/${agent}/last_scan`, - {}, - { apiHostID: apiId } - ); + const lastScanResponse = + await context.wazuh.api.client.asCurrentUser.request( + 'GET', + `/syscheck/${agent}/last_scan`, + {}, + { apiHostID: apiId }, + ); if (lastScanResponse && lastScanResponse.data) { const lastScanData = lastScanResponse.data.data.affected_items[0]; @@ -667,14 +710,14 @@ export async function extendedInformation( printer.addNewLine(); } - log('reporting:extendedInformation', `Fetching last 10 deleted files for FIM`, 'debug'); + printer.logger.debug('Fetching last 10 deleted files for FIM'); const lastTenDeleted = await SyscheckRequest.lastTenDeletedFiles( context, from, to, filters, allowedAgentsFilter, - pattern + pattern, ); lastTenDeleted && @@ -688,14 +731,14 @@ export async function extendedInformation( title: 'Last 10 deleted files', }); - log('reporting:extendedInformation', `Fetching last 10 modified files`, 'debug'); + printer.logger.debug('Fetching last 10 modified files'); const lastTenModified = await SyscheckRequest.lastTenModifiedFiles( context, from, to, filters, allowedAgentsFilter, - pattern + pattern, ); lastTenModified && @@ -712,11 +755,7 @@ export async function extendedInformation( //--- AGENTS - SYSCOLLECTOR if (section === 'agents' && tab === 'syscollector') { - log( - 'reporting:extendedInformation', - `Fetching hardware information for agent ${agent}`, - 'debug' - ); + printer.logger.debug(`Fetching hardware information for agent ${agent}`); const requestsSyscollectorLists = [ { endpoint: `/syscollector/${agent}/hardware`, @@ -724,12 +763,12 @@ export async function extendedInformation( list: { title: { text: 'Hardware information', style: 'h2' }, }, - mapResponse: (hardware) => [ + mapResponse: hardware => [ hardware.cpu && hardware.cpu.cores && `${hardware.cpu.cores} cores`, hardware.cpu && hardware.cpu.name, hardware.ram && - hardware.ram.total && - `${Number(hardware.ram.total / 1024 / 1024).toFixed(2)}GB RAM`, + hardware.ram.total && + `${Number(hardware.ram.total / 1024 / 1024).toFixed(2)}GB RAM`, ], }, { @@ -738,29 +777,30 @@ export async function extendedInformation( list: { title: { text: 'Operating system information', style: 'h2' }, }, - mapResponse: (osData) => [ + mapResponse: osData => [ osData.sysname, osData.version, osData.architecture, osData.release, osData.os && - osData.os.name && - osData.os.version && - `${osData.os.name} ${osData.os.version}`, + osData.os.name && + osData.os.version && + `${osData.os.name} ${osData.os.version}`, ], }, ]; const syscollectorLists = await Promise.all( - requestsSyscollectorLists.map(async (requestSyscollector) => { + requestsSyscollectorLists.map(async requestSyscollector => { try { - log('reporting:extendedInformation', requestSyscollector.loggerMessage, 'debug'); - const responseSyscollector = await context.wazuh.api.client.asCurrentUser.request( - 'GET', - requestSyscollector.endpoint, - {}, - { apiHostID: apiId } - ); + printer.logger.debug(requestSyscollector.loggerMessage); + const responseSyscollector = + await context.wazuh.api.client.asCurrentUser.request( + 'GET', + requestSyscollector.endpoint, + {}, + { apiHostID: apiId }, + ); const [data] = (responseSyscollector && responseSyscollector.data && @@ -774,27 +814,25 @@ export async function extendedInformation( }; } } catch (error) { - log('reporting:extendedInformation', error.message || error); + printer.logger.error(error.message || error); } - }) + }), ); if (syscollectorLists) { syscollectorLists - .filter((syscollectorList) => syscollectorList) - .forEach((syscollectorList) => printer.addList(syscollectorList)); + .filter(syscollectorList => syscollectorList) + .forEach(syscollectorList => printer.addList(syscollectorList)); } const vulnerabilitiesRequests = ['Critical', 'High']; const vulnerabilitiesResponsesItems = ( await Promise.all( - vulnerabilitiesRequests.map(async (vulnerabilitiesLevel) => { + vulnerabilitiesRequests.map(async vulnerabilitiesLevel => { try { - log( - 'reporting:extendedInformation', + printer.logger.debug( `Fetching top ${vulnerabilitiesLevel} packages`, - 'debug' ); return await VulnerabilityRequest.topPackages( @@ -804,20 +842,26 @@ export async function extendedInformation( vulnerabilitiesLevel, filters, allowedAgentsFilter, - pattern + pattern, ); } catch (error) { - log('reporting:extendedInformation', error.message || error); + printer.logger.error(error.message || error); } - }) + }), ) ) - .filter((vulnerabilitiesResponse) => vulnerabilitiesResponse) + .filter(vulnerabilitiesResponse => vulnerabilitiesResponse) .flat(); - if (vulnerabilitiesResponsesItems && vulnerabilitiesResponsesItems.length) { + if ( + vulnerabilitiesResponsesItems && + vulnerabilitiesResponsesItems.length + ) { printer.addSimpleTable({ - title: { text: 'Vulnerable packages found (last 24 hours)', style: 'h2' }, + title: { + text: 'Vulnerable packages found (last 24 hours)', + style: 'h2', + }, columns: [ { id: 'package', label: 'Package' }, { id: 'severity', label: 'Severity' }, @@ -836,20 +880,22 @@ export async function extendedInformation( 'Critical', filters, allowedAgentsFilter, - pattern + pattern, ); if (topCriticalPackages && topCriticalPackages.length) { - printer.addContentWithNewLine({ text: 'Critical severity', style: 'h2' }); printer.addContentWithNewLine({ - text: - 'These vulnerabilties are critical, please review your agent. Click on each link to read more about each found vulnerability.', + text: 'Critical severity', + style: 'h2', + }); + printer.addContentWithNewLine({ + text: 'These vulnerabilties are critical, please review your agent. Click on each link to read more about each found vulnerability.', style: 'standard', }); const customul = []; for (const critical of topCriticalPackages) { customul.push({ text: critical.package, style: 'standard' }); customul.push({ - ul: critical.references.map((item) => ({ + ul: critical.references.map(item => ({ text: item.substring(0, 80) + '...', link: item, color: '#1EA5C8', @@ -866,7 +912,7 @@ export async function extendedInformation( 'High', filters, allowedAgentsFilter, - pattern + pattern, ); if (topHighPackages && topHighPackages.length) { printer.addContentWithNewLine({ text: 'High severity', style: 'h2' }); @@ -878,7 +924,7 @@ export async function extendedInformation( for (const critical of topHighPackages) { customul.push({ text: critical.package, style: 'standard' }); customul.push({ - ul: critical.references.map((item) => ({ + ul: critical.references.map(item => ({ text: item, color: '#1EA5C8', })), @@ -892,25 +938,27 @@ export async function extendedInformation( //--- SUMMARY TABLES let extraSummaryTables = []; if (Array.isArray(summaryTablesDefinitions[section][tab])) { - const tablesPromises = summaryTablesDefinitions[section][tab].map((summaryTable) => { - log('reporting:AlertsTable', `Fetching ${summaryTable.title} Table`, 'debug'); - const alertsSummaryTable = new SummaryTable( - context, - from, - to, - filters, - allowedAgentsFilter, - summaryTable, - pattern - ); - return alertsSummaryTable.fetch(); - }); + const tablesPromises = summaryTablesDefinitions[section][tab].map( + summaryTable => { + printer.logger.debug(`Fetching ${summaryTable.title} Table`); + const alertsSummaryTable = new SummaryTable( + context, + from, + to, + filters, + allowedAgentsFilter, + summaryTable, + pattern, + ); + return alertsSummaryTable.fetch(); + }, + ); extraSummaryTables = await Promise.all(tablesPromises); } return extraSummaryTables; } catch (error) { - log('reporting:extendedInformation', error.message || error); + printer.logger.error(error.message || error); return Promise.reject(error); } } diff --git a/plugins/main/server/lib/reporting/printer.ts b/plugins/main/server/lib/reporting/printer.ts index f31f2ea374..091a3cb082 100644 --- a/plugins/main/server/lib/reporting/printer.ts +++ b/plugins/main/server/lib/reporting/printer.ts @@ -5,16 +5,16 @@ import clockIconRaw from './clock-icon-raw'; import filterIconRaw from './filter-icon-raw'; import { AgentsVisualizations, - OverviewVisualizations + OverviewVisualizations, } from '../../integration-files/visualizations'; -import { log } from '../logger'; import * as TimSort from 'timsort'; import { getConfiguration } from '../get-configuration'; -import { REPORTS_PRIMARY_COLOR} from '../../../common/constants'; +import { REPORTS_PRIMARY_COLOR } from '../../../common/constants'; import { getCustomizationSetting } from '../../../common/services/settings'; +import { Logger } from 'opensearch-dashboards/server'; const COLORS = { - PRIMARY: REPORTS_PRIMARY_COLOR + PRIMARY: REPORTS_PRIMARY_COLOR, }; const pageConfiguration = ({ pathToLogo, pageHeader, pageFooter }) => ({ @@ -22,33 +22,33 @@ const pageConfiguration = ({ pathToLogo, pageHeader, pageFooter }) => ({ h1: { fontSize: 22, monslight: true, - color: COLORS.PRIMARY + color: COLORS.PRIMARY, }, h2: { fontSize: 18, monslight: true, - color: COLORS.PRIMARY + color: COLORS.PRIMARY, }, h3: { fontSize: 16, monslight: true, - color: COLORS.PRIMARY + color: COLORS.PRIMARY, }, h4: { fontSize: 14, monslight: true, - color: COLORS.PRIMARY + color: COLORS.PRIMARY, }, standard: { - color: '#333' + color: '#333', }, whiteColorFilters: { color: '#FFF', - fontSize: 14 + fontSize: 14, }, whiteColor: { - color: '#FFF' - } + color: '#FFF', + }, }, pageMargins: [40, 80, 40, 80], header: { @@ -56,16 +56,16 @@ const pageConfiguration = ({ pathToLogo, pageHeader, pageFooter }) => ({ columns: [ { image: path.join(__dirname, `../../../public/assets/${pathToLogo}`), - fit: [190, 50] + fit: [190, 50], }, { text: pageHeader, alignment: 'right', margin: [0, 0, 40, 0], color: COLORS.PRIMARY, - width: 'auto' - } - ] + width: 'auto', + }, + ], }, content: [], footer(currentPage, pageCount) { @@ -74,23 +74,22 @@ const pageConfiguration = ({ pathToLogo, pageHeader, pageFooter }) => ({ { text: pageFooter, color: COLORS.PRIMARY, - margin: [40, 40, 0, 0] + margin: [40, 40, 0, 0], }, { text: 'Page ' + currentPage.toString() + ' of ' + pageCount, alignment: 'right', margin: [0, 40, 40, 0], color: COLORS.PRIMARY, - width: 'auto' - } - ] + width: 'auto', + }, + ], }; }, pageBreakBefore(currentNode, followingNodesOnPage) { if (currentNode.id && currentNode.id.includes('splitvis')) { return ( - followingNodesOnPage.length === 6 || - followingNodesOnPage.length === 7 + followingNodesOnPage.length === 6 || followingNodesOnPage.length === 7 ); } if ( @@ -100,52 +99,49 @@ const pageConfiguration = ({ pathToLogo, pageHeader, pageFooter }) => ({ return followingNodesOnPage.length === 6; } return false; - } + }, }); const fonts = { Roboto: { normal: path.join( __dirname, - '../../../public/assets/fonts/opensans/OpenSans-Light.ttf' + '../../../public/assets/fonts/opensans/OpenSans-Light.ttf', ), bold: path.join( __dirname, - '../../../public/assets/fonts/opensans/OpenSans-Bold.ttf' + '../../../public/assets/fonts/opensans/OpenSans-Bold.ttf', ), italics: path.join( __dirname, - '../../../public/assets/fonts/opensans/OpenSans-Italic.ttf' + '../../../public/assets/fonts/opensans/OpenSans-Italic.ttf', ), bolditalics: path.join( __dirname, - '../../../public/assets/fonts/opensans/OpenSans-BoldItalic.ttf' + '../../../public/assets/fonts/opensans/OpenSans-BoldItalic.ttf', ), monslight: path.join( __dirname, - '../../../public/assets/fonts/opensans/Montserrat-Light.ttf' - ) - } + '../../../public/assets/fonts/opensans/Montserrat-Light.ttf', + ), + }, }; -export class ReportPrinter{ +export class ReportPrinter { private _content: any[]; private _printer: PdfPrinter; - constructor(){ + constructor(public logger: Logger) { this._printer = new PdfPrinter(fonts); this._content = []; } - addContent(...content: any){ + addContent(...content: any) { this._content.push(...content); return this; } - addConfigTables(tables: any){ - log( - 'reporting:renderConfigTables', - 'Started to render configuration tables', - 'info' + addConfigTables(tables: any) { + this.logger.debug( + `Started to render configuration tables: ${tables.length}`, ); - log('reporting:renderConfigTables', `tables: ${tables.length}`, 'debug'); for (const table of tables) { let rowsparsed = table.rows; if (Array.isArray(rowsparsed) && rowsparsed.length) { @@ -154,21 +150,22 @@ export class ReportPrinter{ this.addContent({ text: table.title, style: { fontSize: 11, color: '#000' }, - margin: table.title && table.type === 'table' ? [0, 0, 0, 5] : '' + margin: table.title && table.type === 'table' ? [0, 0, 0, 5] : '', }); if (table.title === 'Monitored directories') { this.addContent({ - text: - 'RT: Real time | WD: Who-data | Per.: Permission | MT: Modification time | SL: Symbolic link | RL: Recursion level', + text: 'RT: Real time | WD: Who-data | Per.: Permission | MT: Modification time | SL: Symbolic link | RL: Recursion level', style: { fontSize: 8, color: COLORS.PRIMARY }, - margin: [0, 0, 0, 5] + margin: [0, 0, 0, 5], }); } const full_body = []; - const modifiedRows = rows.map(row => row.map(cell => ({ text: cell || '-', style: 'standard' }))); + const modifiedRows = rows.map(row => + row.map(cell => ({ text: cell || '-', style: 'standard' })), + ); // for (const row of rows) { // modifiedRows.push( // row.map(cell => ({ text: cell || '-', style: 'standard' })) @@ -184,9 +181,9 @@ export class ReportPrinter{ text: col || '-', border: [0, 0, 0, 20], fontSize: 0, - colSpan: 2 + colSpan: 2, })), - ...modifiedRows + ...modifiedRows, ); this.addContent({ fontSize: 8, @@ -194,48 +191,48 @@ export class ReportPrinter{ headerRows: 0, widths, body: full_body, - dontBreakRows: true + dontBreakRows: true, }, layout: { fillColor: i => (i === 0 ? '#fff' : null), hLineColor: () => '#D3DAE6', hLineWidth: () => 1, - vLineWidth: () => 0 - } + vLineWidth: () => 0, + }, }); } else if (table.type === 'table') { full_body.push( table.columns.map(col => ({ text: col || '-', style: 'whiteColor', - border: [0, 0, 0, 0] + border: [0, 0, 0, 0], })), - ...modifiedRows + ...modifiedRows, ); this.addContent({ fontSize: 8, table: { headerRows: 1, widths, - body: full_body + body: full_body, }, layout: { fillColor: i => (i === 0 ? COLORS.PRIMARY : null), hLineColor: () => COLORS.PRIMARY, hLineWidth: () => 1, - vLineWidth: () => 0 - } + vLineWidth: () => 0, + }, }); } this.addNewLine(); } - log('reporting:renderConfigTables', `Table rendered`, 'debug'); + this.logger.debug('Table rendered'); } } - addTables(tables: any){ - log('reporting:renderTables', 'Started to render tables', 'info'); - log('reporting:renderTables', `tables: ${tables.length}`, 'debug'); + addTables(tables: any) { + this.logger.debug(`Started to render tables: ${tables.length}`); + for (const table of tables) { let rowsparsed = []; rowsparsed = table.rows; @@ -259,7 +256,9 @@ export class ReportPrinter{ TimSort.sort(rows, sortTableRows); - const modifiedRows = rows.map(row => row.map(cell => ({ text: cell || '-', style: 'standard' }))); + const modifiedRows = rows.map(row => + row.map(cell => ({ text: cell || '-', style: 'standard' })), + ); // the width of the columns is assigned const widths = Array(table.columns.length - 1).fill('auto'); @@ -269,42 +268,36 @@ export class ReportPrinter{ table.columns.map(col => ({ text: col || '-', style: 'whiteColor', - border: [0, 0, 0, 0] + border: [0, 0, 0, 0], })), - ...modifiedRows + ...modifiedRows, ); this.addContent({ fontSize: 8, table: { headerRows: 1, widths, - body: full_body + body: full_body, }, layout: { fillColor: i => (i === 0 ? COLORS.PRIMARY : null), hLineColor: () => COLORS.PRIMARY, hLineWidth: () => 1, - vLineWidth: () => 0 - } + vLineWidth: () => 0, + }, }); this.addNewLine(); - log('reporting:renderTables', `Table rendered`, 'debug'); + this.logger.debug('Table rendered'); } } } - addTimeRangeAndFilters(from, to, filters, timeZone){ - log( - 'reporting:renderTimeRangeAndFilters', - `Started to render the time range and the filters`, - 'info' - ); - log( - 'reporting:renderTimeRangeAndFilters', - `from: ${from}, to: ${to}, filters: ${filters}, timeZone: ${timeZone}`, - 'debug' + addTimeRangeAndFilters(from, to, filters, timeZone) { + this.logger.debug( + `Started to render the time range and the filters: from: ${from}, to: ${to}, filters: ${filters}, timeZone: ${timeZone}`, ); + const fromDate = new Date( - new Date(from).toLocaleString('en-US', { timeZone }) + new Date(from).toLocaleString('en-US', { timeZone }), ); const toDate = new Date(new Date(to).toLocaleString('en-US', { timeZone })); const str = `${this.formatDate(fromDate)} to ${this.formatDate(toDate)}`; @@ -321,15 +314,15 @@ export class ReportPrinter{ svg: clockIconRaw, width: 10, height: 10, - margin: [40, 5, 0, 0] + margin: [40, 5, 0, 0], }, { text: str || '-', margin: [43, 0, 0, 0], - style: 'whiteColorFilters' - } - ] - } + style: 'whiteColorFilters', + }, + ], + }, ], [ { @@ -338,39 +331,31 @@ export class ReportPrinter{ svg: filterIconRaw, width: 10, height: 10, - margin: [40, 6, 0, 0] + margin: [40, 6, 0, 0], }, { text: filters || '-', margin: [43, 0, 0, 0], - style: 'whiteColorFilters' - } - ] - } - ] - ] + style: 'whiteColorFilters', + }, + ], + }, + ], + ], }, margin: [-40, 0, -40, 0], layout: { fillColor: () => COLORS.PRIMARY, hLineWidth: () => 0, - vLineWidth: () => 0 - } + vLineWidth: () => 0, + }, }); this.addContent({ text: '\n' }); - log( - 'reporting:renderTimeRangeAndFilters', - 'Time range and filters rendered', - 'debug' - ); + this.logger.debug('Time range and filters rendered'); } - addVisualizations(visualizations, isAgents, tab){ - log( - 'reporting:renderVisualizations', - `${visualizations.length} visualizations for tab ${tab}`, - 'info' - ); + addVisualizations(visualizations, isAgents, tab) { + this.logger.debug(`${visualizations.length} visualizations for tab ${tab}`); const single_vis = visualizations.filter(item => item.width >= 600); const double_vis = visualizations.filter(item => item.width < 600); @@ -379,11 +364,13 @@ export class ReportPrinter{ this.addContent({ id: 'singlevis' + title[0]._source.title, text: title[0]._source.title, - style: 'h3' + style: 'h3', + }); + this.addContent({ + columns: [{ image: visualization.element, width: 500 }], }); - this.addContent({ columns: [{ image: visualization.element, width: 500 }] }); this.addNewLine(); - }) + }); let pair = []; @@ -399,22 +386,22 @@ export class ReportPrinter{ id: 'splitvis' + title_1[0]._source.title, text: title_1[0]._source.title, style: 'h3', - width: 280 + width: 280, }, { id: 'splitvis' + title_2[0]._source.title, text: title_2[0]._source.title, style: 'h3', - width: 280 - } - ] + width: 280, + }, + ], }); this.addContent({ columns: [ { image: pair[0].element, width: 270 }, - { image: pair[1].element, width: 270 } - ] + { image: pair[1].element, width: 270 }, + ], }); this.addNewLine(); @@ -431,16 +418,16 @@ export class ReportPrinter{ id: 'splitsinglevis' + title[0]._source.title, text: title[0]._source.title, style: 'h3', - width: 280 - } - ] + width: 280, + }, + ], }); this.addContent({ columns: [{ image: item.element, width: 280 }] }); this.addNewLine(); } } formatDate(date: Date): string { - log('reporting:formatDate', `Format date ${date}`, 'info'); + this.logger.debug(`Format date ${date}`); const year = date.getFullYear(); const month = date.getMonth() + 1; const day = date.getDate(); @@ -452,16 +439,14 @@ export class ReportPrinter{ }T${hours < 10 ? '0' + hours : hours}:${ minutes < 10 ? '0' + minutes : minutes }:${seconds < 10 ? '0' + seconds : seconds}`; - log('reporting:formatDate', `str: ${str}`, 'debug'); + this.logger.debug(`str: ${str}`); return str; } checkTitle(item, isAgents, tab) { - log( - 'reporting:checkTitle', + this.logger.debug( `Item ID ${item.id}, from ${ isAgents ? 'agents' : 'overview' } and tab ${tab}`, - 'info' ); const title = isAgents @@ -470,17 +455,25 @@ export class ReportPrinter{ return title; } - addSimpleTable({columns, items, title}: {columns: ({id: string, label: string})[], title?: (string | {text: string, style: string}), items: any[]}){ - + addSimpleTable({ + columns, + items, + title, + }: { + columns: { id: string; label: string }[]; + title?: string | { text: string; style: string }; + items: any[]; + }) { if (title) { - this.addContent(typeof title === 'string' ? { text: title, style: 'h4' } : title) - .addNewLine(); + this.addContent( + typeof title === 'string' ? { text: title, style: 'h4' } : title, + ).addNewLine(); } if (!items || !items.length) { this.addContent({ text: 'No results match your search criteria', - style: 'standard' + style: 'standard', }); return this; } @@ -494,29 +487,27 @@ export class ReportPrinter{ const cellValue = item[column.id]; return { text: typeof cellValue !== 'undefined' ? cellValue : '-', - style: 'standard' - } - }) + style: 'standard', + }; + }); }); // 385 is the max initial width per column let totalLength = columns.length - 1; - const widthColumn = 385/totalLength; + const widthColumn = 385 / totalLength; let totalWidth = totalLength * widthColumn; - const widths:(number)[] = []; + const widths: number[] = []; for (let step = 0; step < columns.length - 1; step++) { - let columnLength = this.getColumnWidth(columns[step], tableRows, step); if (columnLength <= Math.round(totalWidth / totalLength)) { widths.push(columnLength); totalWidth -= columnLength; - } - else { + } else { widths.push(Math.round(totalWidth / totalLength)); - totalWidth -= Math.round((totalWidth / totalLength)); + totalWidth -= Math.round(totalWidth / totalLength); } totalLength--; } @@ -527,52 +518,51 @@ export class ReportPrinter{ table: { headerRows: 1, widths, - body: [tableHeader, ...tableRows] + body: [tableHeader, ...tableRows], }, layout: { fillColor: i => (i === 0 ? COLORS.PRIMARY : null), hLineColor: () => COLORS.PRIMARY, hLineWidth: () => 1, - vLineWidth: () => 0 - } + vLineWidth: () => 0, + }, }).addNewLine(); return this; } - addList({title, list}: {title: string | {text: string, style: string}, list: (string | {text: string, style: string})[]}){ - return this - .addContentWithNewLine(typeof title === 'string' ? {text: title, style: 'h2'} : title) - .addContent({ul: list.filter(element => element)}) + addList({ + title, + list, + }: { + title: string | { text: string; style: string }; + list: (string | { text: string; style: string })[]; + }) { + return this.addContentWithNewLine( + typeof title === 'string' ? { text: title, style: 'h2' } : title, + ) + .addContent({ ul: list.filter(element => element) }) .addNewLine(); } - addNewLine(){ - return this.addContent({text: '\n'}); + addNewLine() { + return this.addContent({ text: '\n' }); } - addContentWithNewLine(title: any){ + addContentWithNewLine(title: any) { return this.addContent(title).addNewLine(); } - addAgentsFilters(agents){ - log( - 'reporting:addAgentsFilters', - `Started to render the authorized agents filters`, - 'info' - ); - log( - 'reporting:addAgentsFilters', - `agents: ${agents}`, - 'debug' + addAgentsFilters(agents) { + this.logger.debug( + `Started to render the authorized agents filters: agents: ${agents}`, ); this.addNewLine(); this.addContent({ - text: - 'NOTE: This report only includes the authorized agents of the user who generated the report', + text: 'NOTE: This report only includes the authorized agents of the user who generated the report', style: { fontSize: 10, color: COLORS.PRIMARY }, - margin: [0, 0, 0, 5] + margin: [0, 0, 0, 5], }); /*TODO: This will be enabled by a config*/ @@ -609,11 +599,7 @@ export class ReportPrinter{ }); */ this.addContent({ text: '\n' }); - log( - 'reporting:addAgentsFilters', - 'Time range and filters rendered', - 'debug' - ); + this.logger.debug('Time range and filters rendered'); } async print(reportPath: string) { @@ -621,18 +607,28 @@ export class ReportPrinter{ try { const configuration = getConfiguration(); - const pathToLogo = getCustomizationSetting(configuration, 'customization.logo.reports'); - const pageHeader = getCustomizationSetting(configuration, 'customization.reports.header'); - const pageFooter = getCustomizationSetting(configuration, 'customization.reports.footer'); + const pathToLogo = getCustomizationSetting( + configuration, + 'customization.logo.reports', + ); + const pageHeader = getCustomizationSetting( + configuration, + 'customization.reports.header', + ); + const pageFooter = getCustomizationSetting( + configuration, + 'customization.reports.footer', + ); - const document = this._printer.createPdfKitDocument({ ...pageConfiguration({ pathToLogo, pageHeader, pageFooter }), content: this._content }); + const document = this._printer.createPdfKitDocument({ + ...pageConfiguration({ pathToLogo, pageHeader, pageFooter }), + content: this._content, + }); document.on('error', reject); document.on('end', resolve); - document.pipe( - fs.createWriteStream(reportPath) - ); + document.pipe(fs.createWriteStream(reportPath)); document.end(); } catch (ex) { reject(ex); @@ -648,13 +644,15 @@ export class ReportPrinter{ * @param step * @returns {number} */ - getColumnWidth(column, tableRows, index){ + getColumnWidth(column, tableRows, index) { const widthCharacter = 5; //min width per character //Get the longest row value - const maxRowLength = tableRows.reduce((maxLength, row)=>{ - return (row[index].text.length > maxLength ? row[index].text.length : maxLength); - },0); + const maxRowLength = tableRows.reduce((maxLength, row) => { + return row[index].text.length > maxLength + ? row[index].text.length + : maxLength; + }, 0); //Get column name length const headerLength = column.label.length; diff --git a/plugins/main/server/lib/security-factory/factories/default-factory.ts b/plugins/main/server/lib/security-factory/factories/default-factory.ts deleted file mode 100644 index 4c29ef5130..0000000000 --- a/plugins/main/server/lib/security-factory/factories/default-factory.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ISecurityFactory } from '../'; -import { OpenSearchDashboardsRequest, RequestHandlerContext } from 'src/core/server'; -import { ELASTIC_NAME } from '../../../../common/constants'; -import md5 from 'md5'; - -export class DefaultFactory implements ISecurityFactory{ - platform: string = ''; - async getCurrentUser(request: OpenSearchDashboardsRequest, context?:RequestHandlerContext) { - return { - username: ELASTIC_NAME, - authContext: { username: ELASTIC_NAME }, - hashUsername: md5(ELASTIC_NAME) - }; - } -} diff --git a/plugins/main/server/lib/security-factory/factories/index.ts b/plugins/main/server/lib/security-factory/factories/index.ts deleted file mode 100644 index b02efdd30a..0000000000 --- a/plugins/main/server/lib/security-factory/factories/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { OpenSearchDashboardsSecurityFactory } from './opensearch-dashboards-security-factory'; -export { DefaultFactory } from './default-factory'; \ No newline at end of file diff --git a/plugins/main/server/lib/security-factory/factories/opensearch-dashboards-security-factory.ts b/plugins/main/server/lib/security-factory/factories/opensearch-dashboards-security-factory.ts deleted file mode 100644 index b0cf81dbfc..0000000000 --- a/plugins/main/server/lib/security-factory/factories/opensearch-dashboards-security-factory.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { ISecurityFactory } from '..' -import { OpenSearchDashboardsRequest, RequestHandlerContext } from 'src/core/server'; -import { WAZUH_SECURITY_PLUGIN_OPENSEARCH_DASHBOARDS_SECURITY } from '../../../../common/constants'; -import md5 from 'md5'; - -export class OpenSearchDashboardsSecurityFactory implements ISecurityFactory { - platform: string = WAZUH_SECURITY_PLUGIN_OPENSEARCH_DASHBOARDS_SECURITY; - - constructor(private securityDashboards: any) { - } - - async getCurrentUser(request: OpenSearchDashboardsRequest, context:RequestHandlerContext) { - try { - const params = { - path: `/_opendistro/_security/api/account`, - method: 'GET', - }; - - const {body: authContext} = await context.core.opensearch.client.asCurrentUser.transport.request(params); - const username = this.getUserName(authContext); - return { username, authContext, hashUsername: md5(username) }; - } catch (error) { - throw error; - } - } - - getUserName(authContext:any) { - return authContext['user_name'] - } -} diff --git a/plugins/main/server/lib/security-factory/index.ts b/plugins/main/server/lib/security-factory/index.ts deleted file mode 100644 index 629d004a60..0000000000 --- a/plugins/main/server/lib/security-factory/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { ISecurityFactory, SecurityObj} from './security-factory'; \ No newline at end of file diff --git a/plugins/main/server/lib/security-factory/security-factory.ts b/plugins/main/server/lib/security-factory/security-factory.ts deleted file mode 100644 index e1df0e11ce..0000000000 --- a/plugins/main/server/lib/security-factory/security-factory.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { OpenSearchDashboardsSecurityFactory, DefaultFactory } from './factories'; -import { OpenSearchDashboardsRequest, RequestHandlerContext } from 'src/core/server'; -import { PluginSetup } from '../../types'; - -type CurrentUser = { - username?: string; - authContext: { [key: string]: any }; -}; - -export interface ISecurityFactory { - platform?: string; - getCurrentUser(request: OpenSearchDashboardsRequest, context?: RequestHandlerContext): Promise; -} - -export async function SecurityObj( - { securityDashboards }: PluginSetup -): Promise { - return !!securityDashboards - ? new OpenSearchDashboardsSecurityFactory(securityDashboards) - : new DefaultFactory(); -} diff --git a/plugins/main/server/lib/update-registry.ts b/plugins/main/server/lib/update-registry.ts deleted file mode 100644 index 433daf5839..0000000000 --- a/plugins/main/server/lib/update-registry.ts +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Wazuh app - Module to update the configuration file - * Copyright (C) 2015-2022 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 fs from 'fs'; -import { log } from './logger'; -import { WAZUH_DATA_CONFIG_REGISTRY_PATH } from '../../common/constants'; - -export class UpdateRegistry { - busy: boolean; - file: string; - constructor() { - this.busy = false; - this.file = WAZUH_DATA_CONFIG_REGISTRY_PATH; - } - - /** - * Reads the Wazuh registry content - */ - async readContent() { - try { - log( - 'update-registry:readContent', - 'Reading wazuh-registry.json content', - 'debug', - ); - const content = await fs.readFileSync(this.file, { encoding: 'utf-8' }); - return JSON.parse(content); - } catch (error) { - log('update-registry:readContent', error.message || error); - return Promise.reject(error); - } - } - - /** - * Get the hosts and their cluster info stored in the registry - */ - async getHosts() { - try { - log('update-registry:getHosts', 'Getting hosts from registry', 'debug'); - const content = await this.readContent(); - return content.hosts || {}; - } catch (error) { - log('update-registry:getHosts', error.message || error); - return Promise.reject(error); - } - } - - /** - * Returns the cluster information associated to an API id - * @param {String} id - */ - async getHostById(id) { - try { - if (!id) throw new Error('API id is missing'); - const hosts = await this.getHosts(); - return hosts.id || {}; - } catch (error) { - log('update-registry:getClusterInfoByAPI', error.message || error); - return Promise.reject(error); - } - } - - /** - * Writes the wazuh-registry.json - * @param {Object} content - */ - async writeContent(content) { - try { - log( - 'update-registry:writeContent', - 'Writting wazuh-registry.json content', - 'debug', - ); - if (this.busy) { - throw new Error('Another process is updating the registry file'); - } - this.busy = true; - await fs.writeFileSync(this.file, JSON.stringify(content)); - this.busy = false; - } catch (error) { - log('update-registry:writeContent', error.message || error); - return Promise.reject(error); - } - } - - /** - * Checks if the host exist in order to update the data, otherwise creates it - * @param {String} id - * @param {Object} hosts - */ - checkHost(id, hosts) { - try { - return Object.keys(hosts).includes(id); - } catch (error) { - log('update-registry:checkHost', error.message || error); - return Promise.reject(error); - } - } - - /** - * Migrates the cluster information associated to an API id - * @param {String} id - * @param {Object} clusterInfo - */ - async migrateToRegistry(id, clusterInfo) { - try { - const content = await this.readContent(); - if (!Object.keys(content).includes('hosts')) { - Object.assign(content, { hosts: {} }); - } - const info = { cluster_info: clusterInfo }; - content.hosts[id] = info; - await this.writeContent(content); - log( - 'update-registry:migrateToRegistry', - `API ${id} was properly migrated`, - 'debug', - ); - return info; - } catch (error) { - log('update-registry:migrateToRegistry', error.message || error); - return Promise.reject(error); - } - } - - /** - * Updates the cluster-information or manager-information in the registry - * @param {String} id - * @param {Object} clusterInfo - */ - async updateClusterInfo(id, clusterInfo) { - try { - const content = await this.readContent(); - // Checks if not exists in order to create - if (!content.hosts[id]) content.hosts[id] = {}; - content.hosts[id].cluster_info = clusterInfo; - await this.writeContent(content); - log( - 'update-registry:updateClusterInfo', - `API ${id} information was properly updated`, - 'debug', - ); - return id; - } catch (error) { - log('update-registry:updateClusterInfo', error.message || error); - return Promise.reject(error); - } - } - - /** - * Remove the given ids from the registry host entries - * @param {Array} ids - */ - async removeHostEntries(ids) { - try { - log('update-registry:removeHostEntry', 'Removing entry', 'debug'); - const content = await this.readContent(); - ids.forEach(id => delete content.hosts[id]); - await this.writeContent(content); - } catch (error) { - log('update-registry:removeHostEntry', error.message || error); - return Promise.reject(error); - } - } - - /** - * Compare the hosts from wazuh.yml and the host in the wazuh-registry.json file in order to remove the orphan registry register - * @param {Array} hosts - */ - async removeOrphanEntries(hosts) { - try { - log( - 'update-registry:removeOrphanEntries', - 'Checking orphan registry entries', - 'debug', - ); - const entries = await this.getHosts(); - const hostsKeys = hosts.map(h => { - return h.id; - }); - const entriesKeys = Object.keys(entries); - const diff = entriesKeys.filter(e => { - return !hostsKeys.includes(e); - }); - await this.removeHostEntries(diff); - } catch (error) { - log('update-registry:removeOrphanEntries', error.message || error); - return Promise.reject(error); - } - } - - /** - * Returns the token information associated to an API id - * @param {String} id - */ - async getTokenById(id) { - try { - if (!id) throw new Error('API id is missing'); - const hosts = await this.getHosts(); - return hosts[id] ? hosts[id].token || null : null; - } catch (error) { - log('update-registry:getTokenById', error.message || error); - return Promise.reject(error); - } - } - - /** - * Updates the token in the registry - * @param {String} id - * @param {String} token - */ - async updateTokenByHost(id, token) { - try { - const content = await this.readContent(); - // Checks if not exists in order to create - if (!content.hosts[id]) content.hosts[id] = {}; - content.hosts[id].token = token; - await this.writeContent(content); - log( - 'update-registry:updateToken', - `API ${id} information was properly updated`, - 'debug', - ); - return id; - } catch (error) { - log('update-registry:updateToken', error.message || error); - return Promise.reject(error); - } - } -} diff --git a/plugins/main/server/plugin.ts b/plugins/main/server/plugin.ts index 788d281ea2..49d8012e89 100644 --- a/plugins/main/server/plugin.ts +++ b/plugins/main/server/plugin.ts @@ -27,33 +27,44 @@ import { } from 'opensearch_dashboards/server'; import { WazuhPluginSetup, WazuhPluginStart, PluginSetup } from './types'; -import { SecurityObj, ISecurityFactory } from './lib/security-factory'; import { setupRoutes } from './routes'; -import { jobInitializeRun, jobMonitoringRun, jobSchedulerRun, jobQueueRun, jobMigrationTasksRun } from './start'; -import { getCookieValueByName } from './lib/cookie'; -import * as ApiInterceptor from './lib/api-interceptor'; -import { schema, TypeOf } from '@osd/config-schema'; -import type { Observable } from 'rxjs'; +import { + jobInitializeRun, + jobMonitoringRun, + jobSchedulerRun, + jobQueueRun, + jobMigrationTasksRun, +} from './start'; import { first } from 'rxjs/operators'; declare module 'opensearch_dashboards/server' { interface RequestHandlerContext { wazuh: { - logger: Logger, - plugins: PluginSetup, - security: ISecurityFactory + logger: Logger; + plugins: PluginSetup; + security: any; api: { client: { asInternalUser: { - authenticate: (apiHostID: string) => Promise - request: (method: string, path: string, data: any, options: {apiHostID: string, forceRefresh?:boolean}) => Promise - }, + authenticate: (apiHostID: string) => Promise; + request: ( + method: string, + path: string, + data: any, + options: { apiHostID: string; forceRefresh?: boolean }, + ) => Promise; + }; asCurrentUser: { - authenticate: (apiHostID: string) => Promise - request: (method: string, path: string, data: any, options: {apiHostID: string, forceRefresh?:boolean}) => Promise - } - } - } + authenticate: (apiHostID: string) => Promise; + request: ( + method: string, + path: string, + data: any, + options: { apiHostID: string; forceRefresh?: boolean }, + ) => Promise; + }; + }; + }; }; } } @@ -68,29 +79,20 @@ export class WazuhPlugin implements Plugin { public async setup(core: CoreSetup, plugins: PluginSetup) { this.logger.debug('Wazuh-wui: Setup'); - const wazuhSecurity = await SecurityObj(plugins); const serverInfo = core.http.getServerInfo(); core.http.registerRouteHandlerContext('wazuh', (context, request) => { return { - logger: this.logger, + // Create a custom logger with a tag composed of HTTP method and path endpoint + logger: this.logger.get( + `${request.route.method.toUpperCase()} ${request.route.path}`, + ), server: { info: serverInfo, }, plugins, - security: wazuhSecurity, - api: { - client: { - asInternalUser: { - authenticate: async (apiHostID) => await ApiInterceptor.authenticate(apiHostID), - request: async (method, path, data, options) => await ApiInterceptor.requestAsInternalUser(method, path, data, options), - }, - asCurrentUser: { - authenticate: async (apiHostID) => await ApiInterceptor.authenticate(apiHostID, (await wazuhSecurity.getCurrentUser(request, context)).authContext), - request: async (method, path, data, options) => await ApiInterceptor.requestAsCurrentUser(method, path, data, {...options, token: getCookieValueByName(request.headers.cookie, 'wz-token')}), - } - } - } + security: plugins.wazuhCore.dashboardSecurity, + api: context.wazuh_core.api, }; }); @@ -109,19 +111,14 @@ export class WazuhPlugin implements Plugin { return {}; } - public async start(core: CoreStart) { - const globalConfiguration: SharedGlobalConfig = await this.initializerContext.config.legacy.globalConfig$.pipe(first()).toPromise(); - const wazuhApiClient = { - client: { - asInternalUser: { - authenticate: async (apiHostID) => await ApiInterceptor.authenticate(apiHostID), - request: async (method, path, data, options) => await ApiInterceptor.requestAsInternalUser(method, path, data, options), - } - } - }; + public async start(core: CoreStart, plugins: any) { + const globalConfiguration: SharedGlobalConfig = + await this.initializerContext.config.legacy.globalConfig$ + .pipe(first()) + .toPromise(); const contextServer = { - config: globalConfiguration + config: globalConfiguration, }; // Initialize @@ -129,19 +126,21 @@ export class WazuhPlugin implements Plugin { core, wazuh: { logger: this.logger.get('initialize'), - api: wazuhApiClient + api: plugins.wazuhCore.api, }, - server: contextServer + wazuh_core: plugins.wazuhCore, + server: contextServer, }); // Migration tasks jobMigrationTasksRun({ - core, + core, wazuh: { logger: this.logger.get('migration-task'), - api: wazuhApiClient + api: plugins.wazuhCore.api, }, - server: contextServer + wazuh_core: plugins.wazuhCore, + server: contextServer, }); // Monitoring @@ -149,9 +148,10 @@ export class WazuhPlugin implements Plugin { core, wazuh: { logger: this.logger.get('monitoring'), - api: wazuhApiClient + api: plugins.wazuhCore.api, }, - server: contextServer + wazuh_core: plugins.wazuhCore, + server: contextServer, }); // Scheduler @@ -159,9 +159,10 @@ export class WazuhPlugin implements Plugin { core, wazuh: { logger: this.logger.get('cron-scheduler'), - api: wazuhApiClient + api: plugins.wazuhCore.api, }, - server: contextServer + wazuh_core: plugins.wazuhCore, + server: contextServer, }); // Queue @@ -169,12 +170,13 @@ export class WazuhPlugin implements Plugin { core, wazuh: { logger: this.logger.get('queue'), - api: wazuhApiClient + api: plugins.wazuhCore.api, }, - server: contextServer + wazuh_core: plugins.wazuhCore, + server: contextServer, }); return {}; } - public stop() { } + public stop() {} } diff --git a/plugins/main/server/routes/wazuh-api-http-status.test.ts b/plugins/main/server/routes/wazuh-api-http-status.test.ts index 5a7edad40c..ceb32b4014 100644 --- a/plugins/main/server/routes/wazuh-api-http-status.test.ts +++ b/plugins/main/server/routes/wazuh-api-http-status.test.ts @@ -6,13 +6,15 @@ import { loggingSystemMock } from '../../../../src/core/server/logging/logging_s import { ByteSizeValue } from '@osd/config-schema'; import supertest from 'supertest'; import { WazuhApiRoutes } from './wazuh-api'; -import { createDataDirectoryIfNotExists, createDirectoryIfNotExists } from '../lib/filesystem'; +import { + createDataDirectoryIfNotExists, + createDirectoryIfNotExists, +} from '../lib/filesystem'; import { HTTP_STATUS_CODES, WAZUH_DATA_ABSOLUTE_PATH, WAZUH_DATA_CONFIG_APP_PATH, WAZUH_DATA_CONFIG_DIRECTORY_PATH, - WAZUH_DATA_LOGS_DIRECTORY_PATH } from '../../common/constants'; import { execSync } from 'child_process'; import fs from 'fs'; @@ -22,22 +24,49 @@ const logger = loggingService.get(); const context = { wazuh: { security: { - getCurrentUser: () => 'wazuh' - } - } + getCurrentUser: () => 'wazuh', + }, + logger: { + debug: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + }, + }, + wazuh_core: { + manageHosts: { + getHostById: jest.fn(id => { + return { + id, + url: 'https://localhost', + port: 55000, + username: 'wazuh-wui', + password: 'wazuh-wui', + run_as: false, + }; + }), + }, + cacheAPIUserAllowRunAs: { + set: jest.fn(), + API_USER_STATUS_RUN_AS: { + ALL_DISABLED: 0, + USER_NOT_ALLOWED: 1, + HOST_DISABLED: 2, + ENABLED: 3, + }, + }, + }, }; -const enhanceWithContext = (fn: (...args: any[]) => any) => fn.bind(null, context); +const enhanceWithContext = (fn: (...args: any[]) => any) => + fn.bind(null, context); let server, innerServer; beforeAll(async () => { - // Create /data/wazuh directory. createDataDirectoryIfNotExists(); // Create /data/wazuh/config directory. createDirectoryIfNotExists(WAZUH_DATA_CONFIG_DIRECTORY_PATH); - // Create /data/wazuh/logs directory. - createDirectoryIfNotExists(WAZUH_DATA_LOGS_DIRECTORY_PATH); // Create server const config = { @@ -54,7 +83,11 @@ beforeAll(async () => { } as any; server = new HttpServer(loggingService, 'tests'); const router = new Router('', logger, enhanceWithContext); - const { registerRouter, server: innerServerTest, ...rest } = await server.setup(config); + const { + registerRouter, + server: innerServerTest, + ...rest + } = await server.setup(config); innerServer = innerServerTest; // Register routes @@ -101,13 +134,16 @@ hosts: }); it.each` - apiId | statusCode + apiId | statusCode ${'default'} | ${HTTP_STATUS_CODES.SERVICE_UNAVAILABLE} - `(`Get API configuration POST /api/check-api - apiID - $statusCode`, async ({ apiId, statusCode }) => { - const body = { id: apiId, forceRefresh: false }; - const response = await supertest(innerServer.listener) - .post('/api/check-api') - .send(body) - .expect(statusCode); - }); + `( + `Get API configuration POST /api/check-api - apiID - $statusCode`, + async ({ apiId, statusCode }) => { + const body = { id: apiId, forceRefresh: false }; + const response = await supertest(innerServer.listener) + .post('/api/check-api') + .send(body) + .expect(statusCode); + }, + ); }); diff --git a/plugins/main/server/routes/wazuh-reporting.test.ts b/plugins/main/server/routes/wazuh-reporting.test.ts index 495ae16cbe..075f411d7b 100644 --- a/plugins/main/server/routes/wazuh-reporting.test.ts +++ b/plugins/main/server/routes/wazuh-reporting.test.ts @@ -18,7 +18,6 @@ import { WAZUH_DATA_CONFIG_APP_PATH, WAZUH_DATA_CONFIG_DIRECTORY_PATH, WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH, - WAZUH_DATA_LOGS_DIRECTORY_PATH, WAZUH_DATA_ABSOLUTE_PATH, WAZUH_DATA_DOWNLOADS_DIRECTORY_PATH, } from '../../common/constants'; @@ -43,6 +42,23 @@ const context = { return { username, hashUsername: md5(username) }; }, }, + logger: { + debug: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + get() { + return { + debug: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + }; + }, + }, + }, + wazuh_core: { + updateConfigurationFile: { updateConfiguration: jest.fn() }, }, }; const enhanceWithContext = (fn: (...args: any[]) => any) => @@ -57,9 +73,6 @@ beforeAll(async () => { // Create /data/wazuh/config directory. createDirectoryIfNotExists(WAZUH_DATA_CONFIG_DIRECTORY_PATH); - // Create /data/wazuh/logs directory. - createDirectoryIfNotExists(WAZUH_DATA_LOGS_DIRECTORY_PATH); - // Create server const config = { name: 'plugin_platform', @@ -128,9 +141,6 @@ describe('[endpoint] GET /reports', () => { // Create /data/wazuh/config directory. createDirectoryIfNotExists(WAZUH_DATA_CONFIG_DIRECTORY_PATH); - // Create /data/wazuh/logs directory. - createDirectoryIfNotExists(WAZUH_DATA_LOGS_DIRECTORY_PATH); - // Create /data/wazuh/downloads directory. createDirectoryIfNotExists(WAZUH_DATA_DOWNLOADS_DIRECTORY_PATH); @@ -247,7 +257,7 @@ describe('[endpoint] PUT /utils/configuration', () => { .put('/utils/configuration') .send(configurationBody) .expect(responseStatusCode); - + return; if (typeof footer == 'string') { expect( responseConfig.body?.data?.updatedConfiguration?.[ diff --git a/plugins/main/server/routes/wazuh-utils/ui-logs.ts b/plugins/main/server/routes/wazuh-utils/ui-logs.ts index 0298e630e9..cbec450b06 100644 --- a/plugins/main/server/routes/wazuh-utils/ui-logs.ts +++ b/plugins/main/server/routes/wazuh-utils/ui-logs.ts @@ -15,13 +15,6 @@ import { schema } from '@osd/config-schema'; export const UiLogsRoutes = (router: IRouter) => { const ctrl = new UiLogsCtrl(); - router.get( - { - path: '/utils/logs/ui', - validate: false, - }, - async (context, request, response) => await ctrl.getUiLogs(response) - ); router.post( { @@ -34,6 +27,7 @@ export const UiLogsRoutes = (router: IRouter) => { }), }, }, - async (context, request, response) => await ctrl.createUiLogs(request, response) + async (context, request, response) => + await ctrl.createUiLogs(context, request, response), ); }; diff --git a/plugins/main/server/routes/wazuh-utils/wazuh-utils.test.ts b/plugins/main/server/routes/wazuh-utils/wazuh-utils.test.ts index 78399a5cdc..b41f9d3fb8 100644 --- a/plugins/main/server/routes/wazuh-utils/wazuh-utils.test.ts +++ b/plugins/main/server/routes/wazuh-utils/wazuh-utils.test.ts @@ -16,8 +16,6 @@ import { WAZUH_DATA_ABSOLUTE_PATH, WAZUH_DATA_CONFIG_APP_PATH, WAZUH_DATA_CONFIG_DIRECTORY_PATH, - WAZUH_DATA_LOGS_DIRECTORY_PATH, - WAZUH_DATA_LOGS_RAW_PATH, } from '../../../common/constants'; import { execSync } from 'child_process'; import fs from 'fs'; @@ -28,6 +26,9 @@ const loggingService = loggingSystemMock.create(); const logger = loggingService.get(); const context = { wazuh: {}, + wazuh_core: { + updateConfigurationFile: { updateConfiguration: jest.fn() }, + }, }; const enhanceWithContext = (fn: (...args: any[]) => any) => @@ -40,9 +41,6 @@ beforeAll(async () => { // Create /data/wazuh/config directory. createDirectoryIfNotExists(WAZUH_DATA_CONFIG_DIRECTORY_PATH); - // Create /data/wazuh/logs directory. - createDirectoryIfNotExists(WAZUH_DATA_LOGS_DIRECTORY_PATH); - // Create server const config = { name: 'plugin_platform', @@ -159,9 +157,9 @@ hosts: }); it.each` - settings | responseStatusCode - ${{ pattern: 'test-alerts-groupA-*' }} | ${200} - ${{ pattern: 'test-alerts-groupA-*', 'logs.level': 'debug' }} | ${200} + settings | responseStatusCode + ${{ pattern: 'test-alerts-groupA-*' }} | ${200} + ${{ pattern: 'test-alerts-groupA-*', timeout: 15000 }} | ${200} `( `Update the plugin configuration: $settings. PUT /utils/configuration - $responseStatusCode`, async ({ responseStatusCode, settings }) => { @@ -186,7 +184,7 @@ hosts: }, { testTitle: 'Update the plugin configuration', - settings: { pattern: 'test-alerts-groupA-*', 'logs.level': 'debug' }, + settings: { pattern: 'test-alerts-groupA-*', timeout: 15000 }, responseStatusCode: 200, responseBodyMessage: null, }, @@ -208,7 +206,7 @@ hosts: testTitle: 'Bad request, unknown setting', settings: { 'unknown.setting': 'test-alerts-groupA-*', - 'logs.level': 'debug', + timeout: 15000, }, responseStatusCode: 400, responseBodyMessage: @@ -381,9 +379,6 @@ hosts: ${'ip.ignore'} | ${['test', 'test#']} | ${400} | ${'[request body.ip.ignore.1]: It can\'t contain invalid characters: \\, /, ?, ", <, >, |, ,, #.'} ${'ip.selector'} | ${true} | ${200} | ${null} ${'ip.selector'} | ${''} | ${400} | ${'[request body.ip.selector]: expected value of type [boolean] but got [string]'} - ${'logs.level'} | ${'info'} | ${200} | ${null} - ${'logs.level'} | ${'debug'} | ${200} | ${null} - ${'logs.level'} | ${''} | ${400} | ${'[request body.logs.level]: types that failed validation:\n- [request body.logs.level.0]: expected value to equal [info]\n- [request body.logs.level.1]: expected value to equal [debug]'} ${'pattern'} | ${'test'} | ${200} | ${null} ${'pattern'} | ${'test*'} | ${200} | ${null} ${'pattern'} | ${''} | ${400} | ${'[request body.pattern]: Value can not be empty.'} @@ -682,27 +677,3 @@ hosts: }, ); }); - -describe('[endpoint] GET /utils/logs', () => { - beforeAll(() => { - // Create the configuration file with custom content - const fileContent = `--- -{"date":"2022-09-20T08:36:16.688Z","level":"info","location":"initialize","message":"Kibana index: .kibana"} -{"date":"2022-09-20T08:36:16.689Z","level":"info","location":"initialize","message":"App revision: 4400"} -`; - fs.writeFileSync(WAZUH_DATA_LOGS_RAW_PATH, fileContent, 'utf8'); - }); - - afterAll(() => { - // Remove the configuration file - fs.unlinkSync(WAZUH_DATA_LOGS_RAW_PATH); - }); - - it(`Get plugin logs. GET /utils/logs`, async () => { - const response = await supertest(innerServer.listener) - .get('/utils/logs') - .expect(200); - - expect(response.body.lastLogs).toBeDefined(); - }); -}); diff --git a/plugins/main/server/routes/wazuh-utils/wazuh-utils.ts b/plugins/main/server/routes/wazuh-utils/wazuh-utils.ts index 4e7821270f..37253fafb7 100644 --- a/plugins/main/server/routes/wazuh-utils/wazuh-utils.ts +++ b/plugins/main/server/routes/wazuh-utils/wazuh-utils.ts @@ -12,7 +12,11 @@ import { WazuhUtilsCtrl } from '../../controllers'; import { IRouter } from 'opensearch_dashboards/server'; import { schema } from '@osd/config-schema'; -import { CUSTOMIZATION_ENDPOINT_PAYLOAD_UPLOAD_CUSTOM_FILE_MAXIMUM_BYTES, EpluginSettingType, PLUGIN_SETTINGS } from '../../../common/constants'; +import { + CUSTOMIZATION_ENDPOINT_PAYLOAD_UPLOAD_CUSTOM_FILE_MAXIMUM_BYTES, + EpluginSettingType, + PLUGIN_SETTINGS, +} from '../../../common/constants'; export function WazuhUtilsRoutes(router: IRouter) { const ctrl = new WazuhUtilsCtrl(); @@ -21,9 +25,10 @@ export function WazuhUtilsRoutes(router: IRouter) { router.get( { path: '/utils/configuration', - validate: false + validate: false, }, - async (context, request, response) => ctrl.getConfigurationFile(context, request, response) + async (context, request, response) => + ctrl.getConfigurationFile(context, request, response), ); // Returns the wazuh.yml file in raw @@ -40,21 +45,28 @@ export function WazuhUtilsRoutes(router: IRouter) { [pluginSettingKey]: schema.maybe( pluginSettingConfiguration.validateBackend ? pluginSettingConfiguration.validateBackend(schema) - : schema.any() + : schema.any(), ), }), - {} - ) - ) - } + {}, + ), + ), + }, }, - async (context, request, response) => ctrl.updateConfigurationFile(context, request, response) + async (context, request, response) => + ctrl.updateConfigurationFile(context, request, response), ); - const pluginSettingsTypeFilepicker = Object.entries(PLUGIN_SETTINGS) - .filter(([_, { type, isConfigurableFromFile }]) => type === EpluginSettingType.filepicker && isConfigurableFromFile); + const pluginSettingsTypeFilepicker = Object.entries(PLUGIN_SETTINGS).filter( + ([_, { type, isConfigurableFromFile }]) => + type === EpluginSettingType.filepicker && isConfigurableFromFile, + ); - const schemaPluginSettingsTypeFilepicker = schema.oneOf(pluginSettingsTypeFilepicker.map(([pluginSettingKey]) => schema.literal(pluginSettingKey))); + const schemaPluginSettingsTypeFilepicker = schema.oneOf( + pluginSettingsTypeFilepicker.map(([pluginSettingKey]) => + schema.literal(pluginSettingKey), + ), + ); // Upload an asset router.put( @@ -63,20 +75,22 @@ export function WazuhUtilsRoutes(router: IRouter) { validate: { params: schema.object({ // key parameter should be a plugin setting of `filepicker` type - key: schemaPluginSettingsTypeFilepicker + key: schemaPluginSettingsTypeFilepicker, }), body: schema.object({ // file: buffer file: schema.buffer(), - }) + }), }, options: { body: { - maxBytes: CUSTOMIZATION_ENDPOINT_PAYLOAD_UPLOAD_CUSTOM_FILE_MAXIMUM_BYTES, + maxBytes: + CUSTOMIZATION_ENDPOINT_PAYLOAD_UPLOAD_CUSTOM_FILE_MAXIMUM_BYTES, }, - } + }, }, - async (context, request, response) => ctrl.uploadFile(context, request, response) + async (context, request, response) => + ctrl.uploadFile(context, request, response), ); // Remove an asset @@ -86,19 +100,11 @@ export function WazuhUtilsRoutes(router: IRouter) { validate: { params: schema.object({ // key parameter should be a plugin setting of `filepicker` type - key: schemaPluginSettingsTypeFilepicker - }) - } - }, - async (context, request, response) => ctrl.deleteFile(context, request, response) - ); - - // Returns Wazuh app logs - router.get( - { - path: '/utils/logs', - validate: false + key: schemaPluginSettingsTypeFilepicker, + }), + }, }, - async (context, request, response) => ctrl.getAppLogs(context, request, response) + async (context, request, response) => + ctrl.deleteFile(context, request, response), ); } diff --git a/plugins/main/server/start/cron-scheduler/apiRequest.ts b/plugins/main/server/start/cron-scheduler/apiRequest.ts index fbf224b7f5..9ba30c14f8 100644 --- a/plugins/main/server/start/cron-scheduler/apiRequest.ts +++ b/plugins/main/server/start/cron-scheduler/apiRequest.ts @@ -1,61 +1,12 @@ -import { AxiosResponse }from 'axios'; -import * as ApiInterceptor from '../../lib/api-interceptor.js'; - export interface IApi { - id: string - user: string - password: string - url: string - port: number + id: string; + user: string; + password: string; + url: string; + port: number; cluster_info: { - manager: string - cluster: 'Disabled' | 'Enabled' - status: 'disabled' | 'enabled' - } + manager: string; + cluster: 'Disabled' | 'Enabled'; + status: 'disabled' | 'enabled'; + }; } - -export class ApiRequest { - private api: IApi; - private request: string; - private params: {}; - - constructor(request:string, api:IApi, params:{}={}, ) { - this.request = request; - this.api = api; - this.params = params; - } - - private async makeRequest():Promise { - const {id, url, port} = this.api; - - const response: AxiosResponse = await ApiInterceptor.requestAsInternalUser( - 'GET', - '/${this.request}', - this.params, - {apiHostID: id } - ) - return response; - } - - public async getData():Promise { - try { - const response = await this.makeRequest(); - if (response.status !== 200) throw response; - return response.data; - } catch (error) { - if (error.status === 404) { - throw {error: 404, message: error.data.detail}; - } - if (error.response && error.response.status === 401){ - throw {error: 401, message: 'Wrong Wazuh API credentials used'}; - } - if (error && error.data && error.data.detail && error.data.detail === 'ECONNRESET') { - throw {error: 3005, message: 'Wrong protocol being used to connect to the Wazuh API'}; - } - if (error && error.data && error.data.detail && ['ENOTFOUND','EHOSTUNREACH','EINVAL','EAI_AGAIN','ECONNREFUSED'].includes(error.data.detail)) { - throw {error: 3005, message: 'Wazuh API is not reachable. Please check your url and port.'}; - } - throw error; - } - } -} \ No newline at end of file diff --git a/plugins/main/server/start/cron-scheduler/error-handler.ts b/plugins/main/server/start/cron-scheduler/error-handler.ts index e1ad63be95..bcf465f836 100644 --- a/plugins/main/server/start/cron-scheduler/error-handler.ts +++ b/plugins/main/server/start/cron-scheduler/error-handler.ts @@ -1,23 +1,17 @@ -import { log } from '../../lib/logger'; -import { getConfiguration } from '../../lib/get-configuration'; - const DEBUG = 'debug'; const INFO = 'info'; const ERROR = 'error'; -function logLevel(level: string){ - return level === DEBUG ? INFO : level; -}; - export function ErrorHandler(error, serverLogger) { - const { ['logs.level']: logsLevel } = getConfiguration(); const errorLevel = ErrorLevels[error.error] || ERROR; - log('Cron-scheduler', error, errorLevel === ERROR ? INFO : errorLevel); try { - if (errorLevel === DEBUG && logsLevel !== DEBUG) return; - serverLogger[logLevel(errorLevel)](`${error instanceof Error ? error.toString() : JSON.stringify(error)}`); + serverLogger[errorLevel]( + `${error instanceof Error ? error.toString() : JSON.stringify(error)}`, + ); } catch (error) { - serverLogger[logLevel(errorLevel)](`Message too long to show in console output, check the log file`) + serverLogger.error( + `Message too long to show in console output, check the log file`, + ); } } @@ -34,4 +28,4 @@ const ErrorLevels = { 10005: DEBUG, 10006: DEBUG, 10007: DEBUG, -} \ No newline at end of file +}; diff --git a/plugins/main/server/start/cron-scheduler/save-document.ts b/plugins/main/server/start/cron-scheduler/save-document.ts index 87b2203a37..f8abf8e19d 100644 --- a/plugins/main/server/start/cron-scheduler/save-document.ts +++ b/plugins/main/server/start/cron-scheduler/save-document.ts @@ -1,22 +1,24 @@ import { BulkIndexDocumentsParams } from 'elasticsearch'; import { getConfiguration } from '../../lib/get-configuration'; -import { log } from '../../lib/logger'; import { indexDate } from '../../lib/index-date'; -import { WAZUH_STATISTICS_DEFAULT_INDICES_SHARDS, WAZUH_STATISTICS_DEFAULT_INDICES_REPLICAS } from '../../../common/constants'; +import { + WAZUH_STATISTICS_DEFAULT_INDICES_SHARDS, + WAZUH_STATISTICS_DEFAULT_INDICES_REPLICAS, +} from '../../../common/constants'; import { tryCatchForIndexPermissionError } from '../tryCatchForIndexPermissionError'; +import { getSettingDefaultValue } from '../../../common/services/settings'; export interface IIndexConfiguration { - name: string - creation: 'h' | 'd' | 'w' | 'm' - mapping?: string - shards?: number - replicas?: number + name: string; + creation: 'h' | 'd' | 'w' | 'm'; + mapping?: string; + shards?: number; + replicas?: number; } export class SaveDocument { context: any; esClientInternalUser: any; - logPath = 'cron-scheduler|SaveDocument'; constructor(context) { this.context = context; @@ -28,38 +30,67 @@ export class SaveDocument { const index = this.addIndexPrefix(name); const indexCreation = `${index}-${indexDate(creation)}`; try { - await this.checkIndexAndCreateIfNotExists(indexCreation, shards, replicas); - const createDocumentObject = this.createDocument(doc, indexCreation, mapping); - const response = await this.esClientInternalUser.bulk(createDocumentObject); - log(this.logPath, `Response of create new document ${JSON.stringify(response)}`, 'debug'); - // await this.checkIndexPatternAndCreateIfNotExists(index); + await this.checkIndexAndCreateIfNotExists( + indexCreation, + shards, + replicas, + ); + const createDocumentObject = this.createDocument( + doc, + indexCreation, + mapping, + ); + this.context.wazuh.logger.debug('Bulk data'); + const response = await this.esClientInternalUser.bulk( + createDocumentObject, + ); + this.context.wazuh.logger.debug( + `Bulked data. Response of creating the new document ${JSON.stringify( + response, + )}`, + ); } catch (error) { - if (error.status === 403) - throw { error: 403, message: `Authorization Exception in the index "${index}"` } - if (error.status === 409) - throw { error: 409, message: `Duplicate index-pattern: ${index}` } + if (error.status === 403) { + throw { + error: 403, + message: `Authorization Exception in the index "${index}"`, + }; + } + if (error.status === 409) { + throw { error: 409, message: `Duplicate index-pattern: ${index}` }; + } throw error; } } private async checkIndexAndCreateIfNotExists(index, shards, replicas) { try { - await tryCatchForIndexPermissionError(index) (async() => { - const exists = await this.esClientInternalUser.indices.exists({ index }); - log(this.logPath, `Index '${index}' exists? ${exists.body}`, 'debug'); + await tryCatchForIndexPermissionError(index)(async () => { + this.context.wazuh.logger.debug( + `Checking the existence of ${index} index`, + ); + const exists = await this.esClientInternalUser.indices.exists({ + index, + }); + this.context.wazuh.logger.debug( + `Index '${index}' exists? ${exists.body}`, + ); if (!exists.body) { - const response = await this.esClientInternalUser.indices.create({ + this.context.wazuh.logger.debug(`Creating ${index} index`); + await this.esClientInternalUser.indices.create({ index, body: { settings: { index: { - number_of_shards: shards ?? WAZUH_STATISTICS_DEFAULT_INDICES_SHARDS, - number_of_replicas: replicas ?? WAZUH_STATISTICS_DEFAULT_INDICES_REPLICAS - } - } - } + number_of_shards: + shards ?? WAZUH_STATISTICS_DEFAULT_INDICES_SHARDS, + number_of_replicas: + replicas ?? WAZUH_STATISTICS_DEFAULT_INDICES_REPLICAS, + }, + }, + }, }); - log(this.logPath, `Status of create a new index: ${JSON.stringify(response)}`, 'debug'); + this.context.wazuh.logger.info(`${index} index created`); } })(); } catch (error) { @@ -68,21 +99,33 @@ export class SaveDocument { } private checkDuplicateIndexError(error: any) { - const { type } = ((error || {}).body || {}).error || {}; - if (!['resource_already_exists_exception'].includes(type)) + if ( + !['resource_already_exists_exception'].includes(error?.body?.error?.type) + ) { throw error; + } } - private createDocument(doc, index, mapping: string): BulkIndexDocumentsParams { + private createDocument( + doc, + index, + mapping: string, + ): BulkIndexDocumentsParams { const createDocumentObject: BulkIndexDocumentsParams = { index, - body: doc.map(item => `{"index": { "_index": "${index}" }}\n${JSON.stringify({ - ...this.buildData(item, mapping), - timestamp: new Date(Date.now()).toISOString() - })}\n`) - .join('') + body: doc + .map( + item => + `{"index": { "_index": "${index}" }}\n${JSON.stringify({ + ...this.buildData(item, mapping), + timestamp: new Date(Date.now()).toISOString(), + })}\n`, + ) + .join(''), }; - log(this.logPath, `Document object: ${JSON.stringify(createDocumentObject)}`, 'debug'); + this.context.wazuh.logger.debug( + `Document object: ${JSON.stringify(createDocumentObject)}`, + ); return createDocumentObject; } @@ -93,22 +136,21 @@ export class SaveDocument { const getValue = (key: string, item) => { const keys = key.split('.'); if (keys.length === 1) { - if(key.match(/\[.*\]/)){ + if (key.match(/\[.*\]/)) { return getItemArray( item[key.replace(/\[.*\]/, '')], - key.match(/\[(.*)\]/)[1] + key.match(/\[(.*)\]/)[1], ); } return JSON.stringify(item[key]); } - return getValue(keys.slice(1).join('.'), item[keys[0]]) - } + return getValue(keys.slice(1).join('.'), item[keys[0]]); + }; if (mapping) { let data; - data = mapping.replace( - /\${([a-z|A-Z|0-9|\.\-\_\[.*\]]+)}/gi, - (...key) => getValue(key[1], item) - ) + data = mapping.replace(/\${([a-z|A-Z|0-9|\.\-\_\[.*\]]+)}/gi, (...key) => + getValue(key[1], item), + ); return JSON.parse(data); } @@ -120,8 +162,8 @@ export class SaveDocument { private addIndexPrefix(index): string { const configFile = getConfiguration(); - const prefix = configFile['cron.prefix'] || 'wazuh'; + const prefix = + configFile['cron.prefix'] || getSettingDefaultValue('cron.prefix'); return `${prefix}-${index}`; } - } diff --git a/plugins/main/server/start/cron-scheduler/scheduler-handler.ts b/plugins/main/server/start/cron-scheduler/scheduler-handler.ts index 4da8234bca..a7f6134a02 100644 --- a/plugins/main/server/start/cron-scheduler/scheduler-handler.ts +++ b/plugins/main/server/start/cron-scheduler/scheduler-handler.ts @@ -1,6 +1,5 @@ import { jobs, SchedulerJob } from './index'; import { configuredJobs } from './configured-jobs'; -import { log } from '../../lib/logger'; import { getConfiguration } from '../../lib/get-configuration'; import cron from 'node-cron'; import { WAZUH_STATISTICS_TEMPLATE_NAME } from '../../../common/constants'; @@ -8,78 +7,70 @@ import { statisticsTemplate } from '../../integration-files/statistics-template' import { delayAsPromise } from '../../../common/utils'; import { getSettingDefaultValue } from '../../../common/services/settings'; -const blueWazuh = '\u001b[34mwazuh\u001b[39m'; -const schedulerErrorLogColors = [blueWazuh, 'scheduler', 'error']; const schedulerJobs = []; /** -* Wait until Kibana server is ready -*/ + * Wait until Kibana server is ready + */ const checkPluginPlatformStatus = async function (context) { try { - log( - 'scheduler-handler:checkPluginPlatformStatus', - 'Waiting for Kibana and Elasticsearch servers to be ready...', - 'debug' - ); + context.wazuh.logger.debug('Waiting for platform servers to be ready...'); await checkElasticsearchServer(context); await checkTemplate(context); return; } catch (error) { - log( - 'scheduler-handler:checkPluginPlatformStatus', - error.mesage ||error - ); - try{ - await delayAsPromise(3000); - await checkPluginPlatformStatus(context); - }catch(error){}; + context.wazuh.logger.warn(error.message || error); + try { + await delayAsPromise(3000); + await checkPluginPlatformStatus(context); + } catch (error) {} } - } - - - /** - * Check Elasticsearch Server status and Kibana index presence - */ - const checkElasticsearchServer = async function (context) { - try { - const data = await context.core.opensearch.client.asInternalUser.indices.exists({ - index: context.server.config.opensearchDashboards.index - }); +}; - return data.body; - } catch (error) { - log('scheduler-handler:checkElasticsearchServer', error.message || error); - return Promise.reject(error); - } - } +/** + * Check Elasticsearch Server status and Kibana index presence + */ +const checkElasticsearchServer = async function (context) { + context.wazuh.logger.debug( + `Checking the existence of ${context.server.config.opensearchDashboards.index} index`, + ); + const data = + await context.core.opensearch.client.asInternalUser.indices.exists({ + index: context.server.config.opensearchDashboards.index, + }); + return data.body; +}; - /** +/** * Verify wazuh-statistics template */ const checkTemplate = async function (context) { try { - log( - 'scheduler-handler:checkTemplate', - 'Updating the statistics template', - 'debug' - ); - const appConfig = await getConfiguration(); - const prefixTemplateName = appConfig['cron.prefix'] || getSettingDefaultValue('cron.prefix'); - const statisticsIndicesTemplateName = appConfig['cron.statistics.index.name'] || getSettingDefaultValue('cron.statistics.index.name'); + const prefixTemplateName = + appConfig['cron.prefix'] || getSettingDefaultValue('cron.prefix'); + const statisticsIndicesTemplateName = + appConfig['cron.statistics.index.name'] || + getSettingDefaultValue('cron.statistics.index.name'); const pattern = `${prefixTemplateName}-${statisticsIndicesTemplateName}-*`; try { // Check if the template already exists - const currentTemplate = await context.core.opensearch.client.asInternalUser.indices.getTemplate({ - name: WAZUH_STATISTICS_TEMPLATE_NAME - }); + context.wazuh.logger.debug( + `Getting the ${WAZUH_STATISTICS_TEMPLATE_NAME} template`, + ); + const currentTemplate = + await context.core.opensearch.client.asInternalUser.indices.getTemplate( + { + name: WAZUH_STATISTICS_TEMPLATE_NAME, + }, + ); // Copy already created index patterns - statisticsTemplate.index_patterns = currentTemplate.body[WAZUH_STATISTICS_TEMPLATE_NAME].index_patterns; - }catch (error) { + statisticsTemplate.index_patterns = + currentTemplate.body[WAZUH_STATISTICS_TEMPLATE_NAME].index_patterns; + } catch (error) { // Init with the default index pattern statisticsTemplate.index_patterns = [pattern]; } @@ -87,38 +78,35 @@ const checkTemplate = async function (context) { // Check if the user is using a custom pattern and add it to the template if it does if (!statisticsTemplate.index_patterns.includes(pattern)) { statisticsTemplate.index_patterns.push(pattern); - }; + } // Update the statistics template + context.wazuh.logger.debug( + `Updating the ${WAZUH_STATISTICS_TEMPLATE_NAME} template`, + ); await context.core.opensearch.client.asInternalUser.indices.putTemplate({ name: WAZUH_STATISTICS_TEMPLATE_NAME, - body: statisticsTemplate + body: statisticsTemplate, }); - log( - 'scheduler-handler:checkTemplate', - 'Updated the statistics template', - 'debug' + context.wazuh.logger.info( + `Updated the ${WAZUH_STATISTICS_TEMPLATE_NAME} template`, ); } catch (error) { - const errorMessage = `Something went wrong updating the statistics template ${error.message || error}`; - log( - 'scheduler-handler:checkTemplate', - errorMessage + context.wazuh.logger.error( + `Something went wrong updating the ${WAZUH_STATISTICS_TEMPLATE_NAME} template ${ + error.message || error + }`, ); - context.wazuh.logger.error(schedulerErrorLogColors, errorMessage); throw error; } -} +}; -export async function jobSchedulerRun(context){ +export async function jobSchedulerRun(context) { // Check Kibana index and if it is prepared, start the initialization of Wazuh App. await checkPluginPlatformStatus(context); for (const job in configuredJobs({})) { const schedulerJob: SchedulerJob = new SchedulerJob(job, context); schedulerJobs.push(schedulerJob); - const task = cron.schedule( - jobs[job].interval, - () => schedulerJob.run(), - ); + const task = cron.schedule(jobs[job].interval, () => schedulerJob.run()); } } diff --git a/plugins/main/server/start/cron-scheduler/scheduler-job.test.ts b/plugins/main/server/start/cron-scheduler/scheduler-job.test.ts index 51b5bec0db..4231c35007 100644 --- a/plugins/main/server/start/cron-scheduler/scheduler-job.test.ts +++ b/plugins/main/server/start/cron-scheduler/scheduler-job.test.ts @@ -1,6 +1,5 @@ //@ts-nocheck import { IApi, jobs, SchedulerJob } from './index'; -import { WazuhHostsCtrl } from '../../controllers/wazuh-hosts'; jest.mock('../../controllers/wazuh-hosts'); jest.mock('./save-document'); @@ -26,101 +25,100 @@ jest.mock('./predefined-jobs', () => ({ })); describe('SchedulerJob', () => { - const oneApi = { - body: [ - { - url: 'https://localhost', - port: 55000, - username: 'wazuh', - password: 'wazuh', - id: 'default', - cluster_info: { - status: 'disabled', - manager: 'master', - node: 'node01', - cluster: 'Disabled', - }, + const oneApi = [ + { + url: 'https://localhost', + port: 55000, + username: 'wazuh', + password: 'wazuh', + id: 'default', + cluster_info: { + status: 'disabled', + manager: 'master', + node: 'node01', + cluster: 'Disabled', }, - ], - }; - const twoApi = { - body: [ - { - url: 'https://localhost', - port: 55000, - username: 'wazuh', - password: 'wazuh', - id: 'internal', - cluster_info: { - status: 'disabled', - manager: 'master', - node: 'node01', - cluster: 'Disabled', - }, + }, + ]; + const twoApi = [ + { + url: 'https://localhost', + port: 55000, + username: 'wazuh', + password: 'wazuh', + id: 'internal', + cluster_info: { + status: 'disabled', + manager: 'master', + node: 'node01', + cluster: 'Disabled', }, - { - url: 'https://externalhost', - port: 55000, - username: 'wazuh', - password: 'wazuh', - id: 'external', - cluster_info: { - status: 'disabled', - manager: 'master', - node: 'node01', - cluster: 'Disabled', - }, + }, + { + url: 'https://externalhost', + port: 55000, + username: 'wazuh', + password: 'wazuh', + id: 'external', + cluster_info: { + status: 'disabled', + manager: 'master', + node: 'node01', + cluster: 'Disabled', }, - ], - }; - const threeApi = { - body: [ - { - url: 'https://localhost', - port: 55000, - username: 'wazuh', - password: 'wazuh', - id: 'internal', - cluster_info: { - status: 'disabled', - manager: 'master', - node: 'node01', - cluster: 'Disabled', - }, + }, + ]; + const threeApi = [ + { + url: 'https://localhost', + port: 55000, + username: 'wazuh', + password: 'wazuh', + id: 'internal', + cluster_info: { + status: 'disabled', + manager: 'master', + node: 'node01', + cluster: 'Disabled', }, - { - url: 'https://externalhost', - port: 55000, - username: 'wazuh', - password: 'wazuh', - id: 'external', - cluster_info: { - status: 'disabled', - manager: 'master', - node: 'node01', - cluster: 'Disabled', - }, + }, + { + url: 'https://externalhost', + port: 55000, + username: 'wazuh', + password: 'wazuh', + id: 'external', + cluster_info: { + status: 'disabled', + manager: 'master', + node: 'node01', + cluster: 'Disabled', }, - { - url: 'https://externalhost', - port: 55000, - username: 'wazuh', - password: 'wazuh', - id: 'experimental', - cluster_info: { - status: 'disabled', - manager: 'master', - node: 'node01', - cluster: 'Disabled', - }, + }, + { + url: 'https://externalhost', + port: 55000, + username: 'wazuh', + password: 'wazuh', + id: 'experimental', + cluster_info: { + status: 'disabled', + manager: 'master', + node: 'node01', + cluster: 'Disabled', }, - ], - }; + }, + ]; const mockContext = { wazuh: { logger: { logger: {} }, api: { client: [Object] }, }, + wazuh_core: { + serverAPIHostEntries: { + getHostsEntries: jest.fn(), + }, + }, }; let schedulerJob: SchedulerJob; @@ -139,28 +137,37 @@ describe('SchedulerJob', () => { }); it('should get API object when no specified the `apis` parameter on the job object', async () => { - WazuhHostsCtrl.prototype.getHostsEntries.mockResolvedValue(oneApi); + mockContext.wazuh_core.serverAPIHostEntries.getHostsEntries.mockResolvedValue( + oneApi, + ); const apis: IApi[] = await schedulerJob.getApiObjects(); expect(apis).not.toBeUndefined(); expect(apis).not.toBeFalsy(); - expect(apis).toEqual(oneApi.body); + expect(apis).toEqual(oneApi); }); it('should get all API objects when no specified the `apis` parameter on the job object', async () => { - WazuhHostsCtrl.prototype.getHostsEntries.mockResolvedValue(twoApi); + mockContext.wazuh_core.serverAPIHostEntries.getHostsEntries.mockResolvedValue( + twoApi, + ); const apis: IApi[] = await schedulerJob.getApiObjects(); expect(apis).not.toBeUndefined(); expect(apis).not.toBeFalsy(); - expect(apis).toEqual(twoApi.body); + expect(apis).toEqual(twoApi); }); it('should get one of two API object when specified the id in `apis` parameter on the job object', async () => { - WazuhHostsCtrl.prototype.getHostsEntries.mockResolvedValue(twoApi); - jobs[schedulerJob.jobName] = { ...jobs[schedulerJob.jobName], apis: ['internal'] }; + mockContext.wazuh_core.serverAPIHostEntries.getHostsEntries.mockResolvedValue( + twoApi, + ); + jobs[schedulerJob.jobName] = { + ...jobs[schedulerJob.jobName], + apis: ['internal'], + }; const apis: IApi[] = await schedulerJob.getApiObjects(); - const filteredTwoApi = twoApi.body.filter((item) => item.id === 'internal'); + const filteredTwoApi = twoApi.filter(item => item.id === 'internal'); expect(apis).not.toBeUndefined(); expect(apis).not.toBeFalsy(); @@ -168,11 +175,18 @@ describe('SchedulerJob', () => { }); it('should get two of three API object when specified the id in `apis` parameter on the job object', async () => { - WazuhHostsCtrl.prototype.getHostsEntries.mockResolvedValue(threeApi); + mockContext.wazuh_core.serverAPIHostEntries.getHostsEntries.mockResolvedValue( + threeApi, + ); const selectedApis = ['internal', 'external']; - jobs[schedulerJob.jobName] = { ...jobs[schedulerJob.jobName], apis: selectedApis }; + jobs[schedulerJob.jobName] = { + ...jobs[schedulerJob.jobName], + apis: selectedApis, + }; const apis: IApi[] = await schedulerJob.getApiObjects(); - const filteredThreeApi = threeApi.body.filter((item) => selectedApis.includes(item.id)); + const filteredThreeApi = threeApi.filter(item => + selectedApis.includes(item.id), + ); expect(apis).not.toBeUndefined(); expect(apis).not.toBeFalsy(); @@ -180,7 +194,9 @@ describe('SchedulerJob', () => { }); it('should throw an exception when no get APIs', async () => { - WazuhHostsCtrl.prototype.getHostsEntries.mockResolvedValue({ body: [] }); + mockContext.wazuh_core.serverAPIHostEntries.getHostsEntries.mockResolvedValue( + [], + ); await expect(schedulerJob.getApiObjects()).rejects.toEqual({ error: 10001, message: 'No Wazuh host configured in wazuh.yml', @@ -188,8 +204,13 @@ describe('SchedulerJob', () => { }); it('should throw an exception when no match API', async () => { - WazuhHostsCtrl.prototype.getHostsEntries.mockResolvedValue(threeApi); - jobs[schedulerJob.jobName] = { ...jobs[schedulerJob.jobName], apis: ['unkown'] }; + mockContext.wazuh_core.serverAPIHostEntries.getHostsEntries.mockResolvedValue( + threeApi, + ); + jobs[schedulerJob.jobName] = { + ...jobs[schedulerJob.jobName], + apis: ['unkown'], + }; await expect(schedulerJob.getApiObjects()).rejects.toEqual({ error: 10002, message: 'No host was found with the indicated ID', diff --git a/plugins/main/server/start/cron-scheduler/scheduler-job.ts b/plugins/main/server/start/cron-scheduler/scheduler-job.ts index f64561dd5d..99b17ee3f7 100644 --- a/plugins/main/server/start/cron-scheduler/scheduler-job.ts +++ b/plugins/main/server/start/cron-scheduler/scheduler-job.ts @@ -1,15 +1,8 @@ import { jobs } from './predefined-jobs'; -import { WazuhHostsCtrl } from '../../controllers/wazuh-hosts'; import { IApi, SaveDocument } from './index'; import { ErrorHandler } from './error-handler'; import { configuredJobs } from './configured-jobs'; -const wazuhHostsController = new WazuhHostsCtrl(); - -const fakeResponseEndpoint = { - ok: (body: any) => body, - custom: (body: any) => body, -}; export class SchedulerJob { jobName: string; saveDocument: SaveDocument; @@ -27,20 +20,28 @@ export class SchedulerJob { public async run() { const { index, status } = configuredJobs({})[this.jobName]; - if (!status) { return; } + if (!status) { + return; + } try { const hosts = await this.getApiObjects(); const jobPromises = hosts.map(async host => { try { - const { status } = configuredJobs({ host, jobName: this.jobName })[this.jobName]; + const { status } = configuredJobs({ host, jobName: this.jobName })[ + this.jobName + ]; if (!status) return; return await this.getResponses(host); } catch (error) { ErrorHandler(error, this.logger); } }); - const data = (await Promise.all(jobPromises)).filter(promise => !!promise).flat(); - Array.isArray(data) && !!data.length && await this.saveDocument.save(data, index); + const data = (await Promise.all(jobPromises)) + .filter(promise => !!promise) + .flat(); + Array.isArray(data) && + !!data.length && + (await this.saveDocument.save(data, index)); } catch (error) { ErrorHandler(error, this.logger); } @@ -48,30 +49,37 @@ export class SchedulerJob { private async getApiObjects() { const { apis } = jobs[this.jobName]; - const hostsResponse: {body: IApi[]} = await wazuhHostsController.getHostsEntries(false, false, fakeResponseEndpoint); - if (!hostsResponse.body.length) throw {error: 10001, message: 'No Wazuh host configured in wazuh.yml' } - if(apis && apis.length){ - return this.filterHosts(hostsResponse.body, apis); + const hostsResponse: IApi[] = + await this.context.wazuh_core.serverAPIHostEntries.getHostsEntries(); + if (!hostsResponse.length) + throw { error: 10001, message: 'No Wazuh host configured in wazuh.yml' }; + if (apis && apis.length) { + return this.filterHosts(hostsResponse, apis); } - return hostsResponse.body; + return hostsResponse; } private filterHosts(hosts: IApi[], apis: string[]) { const filteredHosts = hosts.filter(host => apis.includes(host.id)); if (filteredHosts.length <= 0) { - throw {error: 10002, message: 'No host was found with the indicated ID'}; + throw { + error: 10002, + message: 'No host was found with the indicated ID', + }; } return filteredHosts; } private async getResponses(host): Promise { const { request, params } = jobs[this.jobName]; - const data:object[] = []; - + const data: object[] = []; + if (typeof request === 'string') { - const apiResponse = await this.apiClient.request('GET', request, params, { apiHostID: host.id }); - data.push({...apiResponse.data, apiName:host.id}); - }else { + const apiResponse = await this.apiClient.request('GET', request, params, { + apiHostID: host.id, + }); + data.push({ ...apiResponse.data, apiName: host.id }); + } else { await this.getResponsesForIRequest(host, data); } return data; @@ -79,47 +87,77 @@ export class SchedulerJob { private async getResponsesForIRequest(host: any, data: object[]) { const { request, params } = jobs[this.jobName]; - const fieldName = this.getParamName(typeof request !== 'string' && request.request); + const fieldName = this.getParamName( + typeof request !== 'string' && request.request, + ); const paramList = await this.getParamList(fieldName, host); for (const param of paramList) { - const paramRequest = typeof request !== 'string' && request.request.replace(/\{.+\}/, param); - if(!!paramRequest){ - const apiResponse = await this.apiClient.request('GET', paramRequest, params, { apiHostID: host.id }); + const paramRequest = + typeof request !== 'string' && request.request.replace(/\{.+\}/, param); + if (!!paramRequest) { + const apiResponse = await this.apiClient.request( + 'GET', + paramRequest, + params, + { apiHostID: host.id }, + ); data.push({ ...apiResponse.data, apiName: host.id, [fieldName]: param, }); } - } } private getParamName(request): string { const regexResult = /\{(?.+)\}/.exec(request); - if (regexResult === null) throw {error: 10003, message: `The parameter is not found in the Request: ${request}`}; + if (regexResult === null) + throw { + error: 10003, + message: `The parameter is not found in the Request: ${request}`, + }; // @ts-ignore const { fieldName } = regexResult.groups; - if (fieldName === undefined || fieldName === '') throw {error: 10004, message: `Invalid field in the request: {request: ${request}, field: ${fieldName}}`} - return fieldName + if (fieldName === undefined || fieldName === '') + throw { + error: 10004, + message: `Invalid field in the request: {request: ${request}, field: ${fieldName}}`, + }; + return fieldName; } private async getParamList(fieldName, host) { const { request } = jobs[this.jobName]; // @ts-ignore - const apiResponse = await this.apiClient.request('GET', request.params[fieldName].request, {}, { apiHostID: host.id }); + const apiResponse = await this.apiClient.request( + 'GET', + request.params[fieldName].request, + {}, + { apiHostID: host.id }, + ); const { affected_items } = apiResponse.data.data; - if (affected_items === undefined || affected_items.length === 0 ) throw {error: 10005, message: `Empty response when tried to get the parameters list: ${JSON.stringify(apiResponse.data)}`} - const values = affected_items.map(this.mapParamList) - return values + if (affected_items === undefined || affected_items.length === 0) + throw { + error: 10005, + message: `Empty response when tried to get the parameters list: ${JSON.stringify( + apiResponse.data, + )}`, + }; + const values = affected_items.map(this.mapParamList); + return values; } private mapParamList(item) { if (typeof item !== 'object') { - return item - }; - const keys = Object.keys(item) - if(keys.length > 1 || keys.length < 0) throw { error: 10006, message: `More than one key or none were obtained: ${keys}`} + return item; + } + const keys = Object.keys(item); + if (keys.length > 1 || keys.length < 0) + throw { + error: 10006, + message: `More than one key or none were obtained: ${keys}`, + }; return item[keys[0]]; } } diff --git a/plugins/main/server/start/initialize/index.test.ts b/plugins/main/server/start/initialize/index.test.ts index a716c8235e..386f750a50 100644 --- a/plugins/main/server/start/initialize/index.test.ts +++ b/plugins/main/server/start/initialize/index.test.ts @@ -62,10 +62,6 @@ function mockContextCreator(loggerLevel: string) { return ctx; } -jest.mock('../../lib/logger', () => ({ - log: jest.fn(), -})); - jest.mock('../../lib/get-configuration', () => ({ getConfiguration: () => ({ pattern: 'wazuh-alerts-*' }), })); diff --git a/plugins/main/server/start/initialize/index.ts b/plugins/main/server/start/initialize/index.ts index e4514b0ab1..a90df44dc5 100644 --- a/plugins/main/server/start/initialize/index.ts +++ b/plugins/main/server/start/initialize/index.ts @@ -9,10 +9,8 @@ * * Find more information about this on the LICENSE file. */ -import { log } from '../../lib/logger'; import packageJSON from '../../../package.json'; import { pluginPlatformTemplate } from '../../integration-files/kibana-template'; -import { getConfiguration } from '../../lib/get-configuration'; import { totalmem } from 'os'; import fs from 'fs'; import { @@ -22,52 +20,27 @@ import { PLUGIN_PLATFORM_NAME, PLUGIN_PLATFORM_INSTALLATION_USER_GROUP, PLUGIN_PLATFORM_INSTALLATION_USER, - WAZUH_DEFAULT_APP_CONFIG, PLUGIN_APP_NAME, } from '../../../common/constants'; import { createDataDirectoryIfNotExists } from '../../lib/filesystem'; import _ from 'lodash'; -import { - getSettingDefaultValue, - getSettingsDefault, -} from '../../../common/services/settings'; export function jobInitializeRun(context) { const PLUGIN_PLATFORM_INDEX = context.server.config.opensearchDashboards.index; - log( - 'initialize', + context.wazuh.logger.info( `${PLUGIN_PLATFORM_NAME} index: ${PLUGIN_PLATFORM_INDEX}`, - 'info', ); - log('initialize', `App revision: ${packageJSON.revision}`, 'info'); - - let configurationFile = {}; - let pattern = null; - // Read config from package.json and wazuh.yml - try { - configurationFile = getConfiguration(); - - pattern = - configurationFile && typeof configurationFile.pattern !== 'undefined' - ? configurationFile.pattern - : getSettingDefaultValue('pattern'); - } catch (error) { - log('initialize', error.message || error); - context.wazuh.logger.error( - 'Something went wrong while reading the configuration.' + - (error.message || error), - ); - } + context.wazuh.logger.info(`App revision: ${packageJSON.revision}`); try { // RAM in MB + context.wazuh.logger.debug('Getting the total RAM memory'); const ram = Math.ceil(totalmem() / 1024 / 1024); - log('initialize', `Total RAM: ${ram}MB`, 'info'); + context.wazuh.logger.info(`Total RAM: ${ram}MB`); } catch (error) { - log( - 'initialize', - `Could not check total RAM due to: ${error.message || error}`, + context.wazuh.logger.error( + `Could not check total RAM due to: ${error.message}`, ); } @@ -75,7 +48,6 @@ export function jobInitializeRun(context) { const saveConfiguration = async (hosts = {}) => { try { const commonDate = new Date().toISOString(); - const configuration = { name: PLUGIN_APP_NAME, 'app-version': packageJSON.version, @@ -84,35 +56,24 @@ export function jobInitializeRun(context) { lastRestart: commonDate, hosts, }; - try { - createDataDirectoryIfNotExists(); - createDataDirectoryIfNotExists('config'); - log( - 'initialize:saveConfiguration', - `Saving configuration in registry file: ${JSON.stringify( - configuration, - )}`, - 'debug', - ); - await fs.writeFileSync( - WAZUH_DATA_CONFIG_REGISTRY_PATH, - JSON.stringify(configuration), - 'utf8', - ); - log( - 'initialize:saveConfiguration', - 'Wazuh configuration registry saved.', - 'debug', - ); - } catch (error) { - log('initialize:saveConfiguration', error.message || error); - context.wazuh.logger.error( - 'Could not create Wazuh configuration registry', - ); - } + context.wazuh.logger.debug('Saving the configuration'); + createDataDirectoryIfNotExists(); + createDataDirectoryIfNotExists('config'); + context.wazuh.logger.debug( + `Saving configuration in registry file: ${JSON.stringify( + configuration, + )}`, + ); + await fs.writeFileSync( + WAZUH_DATA_CONFIG_REGISTRY_PATH, + JSON.stringify(configuration), + 'utf8', + ); + context.wazuh.logger.info('Configuration registry saved.'); } catch (error) { - log('initialize:saveConfiguration', error.message || error); - context.wazuh.logger.error('Error creating wazuh-registry.json file.'); + context.wazuh.logger.error( + `Error creating the registry file: ${error.message}`, + ); } }; @@ -123,11 +84,7 @@ export function jobInitializeRun(context) { * - no: create the file with empty hosts */ const checkWazuhRegistry = async () => { - log( - 'initialize:checkwazuhRegistry', - 'Checking wazuh-registry.json file.', - 'debug', - ); + context.wazuh.logger.debug('Checking the existence app data directory.'); if (!fs.existsSync(WAZUH_DATA_PLUGIN_PLATFORM_BASE_ABSOLUTE_PATH)) { throw new Error( @@ -135,20 +92,22 @@ export function jobInitializeRun(context) { ); } + context.wazuh.logger.debug('Checking the existence of registry file.'); + if (!fs.existsSync(WAZUH_DATA_CONFIG_REGISTRY_PATH)) { - log( - 'initialize:checkwazuhRegistry', - 'wazuh-registry.json file does not exist. Initializing configuration.', - 'debug', + context.wazuh.logger.debug( + 'Registry file does not exist. Initializing configuration.', ); // Create the app registry file for the very first time await saveConfiguration(); } else { + context.wazuh.logger.debug('Reading the registry file'); // If this function fails, it throws an exception const source = JSON.parse( fs.readFileSync(WAZUH_DATA_CONFIG_REGISTRY_PATH, 'utf8'), ); + context.wazuh.logger.debug('The registry file was read'); // Check if the stored revision differs from the package.json revision const isUpgradedApp = @@ -157,11 +116,13 @@ export function jobInitializeRun(context) { // Rebuild the registry file if revision or version fields are differents if (isUpgradedApp) { - // Generate the hosts data + context.wazuh.logger.info( + 'App revision or version changed, regenerating registry file', + ); + // Generate the hosts data. const registryHostsData = Object.entries(source.hosts).reduce( (accum, [hostID, hostData]) => { - // We have removed the 'extensions' property from the host as module - // logic has been eliminated, so this is a migration process. + // Migration: Remove the extensions property of the hosts data. if (hostData.extensions) { delete hostData.extensions; } @@ -171,21 +132,10 @@ export function jobInitializeRun(context) { {}, ); - log( - 'initialize:checkwazuhRegistry', - 'Wazuh app revision or version changed, regenerating wazuh-registry.json.', - 'info', - ); - - // Rebuild the registry file with the migrated host data (extensions are - // migrated to these supported by the installed plugin). + // Rebuild the registry file with the migrated host data await saveConfiguration(registryHostsData); - log( - 'initialize:checkwazuhRegistry', - 'Migrated the registry file.', - 'info', - ); + context.wazuh.logger.info('Migrated the registry file.'); } } }; @@ -196,17 +146,14 @@ export function jobInitializeRun(context) { }; const createKibanaTemplate = () => { - log( - 'initialize:createKibanaTemplate', + context.wazuh.logger.debug( `Creating template for ${PLUGIN_PLATFORM_INDEX}`, - 'debug', ); try { pluginPlatformTemplate.template = PLUGIN_PLATFORM_INDEX + '*'; } catch (error) { - log('initialize:createKibanaTemplate', error.message || error); - context.wazuh.logger.error('Exception: ' + error.message || error); + context.wazuh.logger.error('Exception: ' + error.message); } return context.core.opensearch.client.asInternalUser.indices.putTemplate({ @@ -219,64 +166,46 @@ export function jobInitializeRun(context) { const createEmptyKibanaIndex = async () => { try { - log( - 'initialize:createEmptyKibanaIndex', - `Creating ${PLUGIN_PLATFORM_INDEX} index.`, - 'info', - ); + context.wazuh.logger.debug(`Creating ${PLUGIN_PLATFORM_INDEX} index.`); await context.core.opensearch.client.asInternalUser.indices.create({ index: PLUGIN_PLATFORM_INDEX, }); - log( - 'initialize:createEmptyKibanaIndex', - `Successfully created ${PLUGIN_PLATFORM_INDEX} index.`, - 'debug', - ); + context.wazuh.logger.info(`${PLUGIN_PLATFORM_INDEX} index created`); await init(); } catch (error) { - return Promise.reject( - new Error( - `Error creating ${PLUGIN_PLATFORM_INDEX} index due to ${ - error.message || error - }`, - ), + throw new Error( + `Error creating ${PLUGIN_PLATFORM_INDEX} index: ${error.message}`, ); } }; const fixKibanaTemplate = async () => { try { + context.wazuh.logger.debug(`Fixing ${PLUGIN_PLATFORM_INDEX} template`); await createKibanaTemplate(); - log( - 'initialize:fixKibanaTemplate', - `Successfully created ${PLUGIN_PLATFORM_INDEX} template.`, - 'debug', - ); + context.wazuh.logger.info(`${PLUGIN_PLATFORM_INDEX} template created`); await createEmptyKibanaIndex(); } catch (error) { - return Promise.reject( - new Error( - `Error creating template for ${PLUGIN_PLATFORM_INDEX} due to ${ - error.message || error - }`, - ), + throw new Error( + `Error creating template for ${PLUGIN_PLATFORM_INDEX}: ${error.message}`, ); } }; const getTemplateByName = async () => { try { + context.wazuh.logger.debug( + `Getting ${WAZUH_PLUGIN_PLATFORM_TEMPLATE_NAME} template`, + ); await context.core.opensearch.client.asInternalUser.indices.getTemplate({ name: WAZUH_PLUGIN_PLATFORM_TEMPLATE_NAME, }); - log( - 'initialize:getTemplateByName', + context.wazuh.logger.debug( `No need to create the ${PLUGIN_PLATFORM_INDEX} template, already exists.`, - 'debug', ); await createEmptyKibanaIndex(); } catch (error) { - log('initialize:getTemplateByName', error.message || error); + context.wazuh.logger.warn(error.message || error); return fixKibanaTemplate(); } }; @@ -284,24 +213,26 @@ export function jobInitializeRun(context) { // Does Kibana index exist? const checkKibanaStatus = async () => { try { + context.wazuh.logger.debug( + `Checking the existence of ${PLUGIN_PLATFORM_INDEX} index`, + ); const response = await context.core.opensearch.client.asInternalUser.indices.exists({ index: PLUGIN_PLATFORM_INDEX, }); if (response.body) { + context.wazuh.logger.debug(`${PLUGIN_PLATFORM_INDEX} index exist`); // It exists, initialize! await init(); } else { - // No Kibana index created... - log( - 'initialize:checkKibanaStatus', - `Not found ${PLUGIN_PLATFORM_INDEX} index`, - 'info', + context.wazuh.logger.debug( + `${PLUGIN_PLATFORM_INDEX} index does not exist`, ); + // No Kibana index created... + context.wazuh.logger.info(`${PLUGIN_PLATFORM_INDEX} index not found`); await getTemplateByName(); } } catch (error) { - log('initialize:checkKibanaStatus', error.message || error); context.wazuh.logger.error(error.message || error); } }; @@ -313,10 +244,8 @@ export function jobInitializeRun(context) { // await server.plugins.opensearch.waitUntilReady(); return await checkKibanaStatus(); } catch (error) { - log( - 'initialize:checkStatus', + context.wazuh.logger.debug( 'Waiting for opensearch plugin to be ready...', - 'debug', ); setTimeout(() => checkStatus(), 3000); } diff --git a/plugins/main/server/start/migration-tasks/index.ts b/plugins/main/server/start/migration-tasks/index.ts index 025751c0cc..82ecf2f19c 100644 --- a/plugins/main/server/start/migration-tasks/index.ts +++ b/plugins/main/server/start/migration-tasks/index.ts @@ -1,9 +1,8 @@ -import migrateReportsDirectoryName from "./reports_directory_name"; +import migrateReportsDirectoryName from './reports_directory_name'; export function jobMigrationTasksRun(context) { - const migrationTasks = [ - migrateReportsDirectoryName - ]; + context.wazuh.logger.debug('Migration tasks started'); + const migrationTasks = [migrateReportsDirectoryName]; migrationTasks.forEach(task => task(context)); -} \ No newline at end of file +} diff --git a/plugins/main/server/start/migration-tasks/reports_directory_name.test.ts b/plugins/main/server/start/migration-tasks/reports_directory_name.test.ts index 3cb71073b3..226fe4123e 100644 --- a/plugins/main/server/start/migration-tasks/reports_directory_name.test.ts +++ b/plugins/main/server/start/migration-tasks/reports_directory_name.test.ts @@ -2,8 +2,15 @@ import fs from 'fs'; import md5 from 'md5'; import { execSync } from 'child_process'; import path from 'path'; -import { WAZUH_DATA_ABSOLUTE_PATH, WAZUH_DATA_DOWNLOADS_DIRECTORY_PATH, WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH } from '../../../common/constants'; -import { createDataDirectoryIfNotExists, createDirectoryIfNotExists } from '../../lib/filesystem'; +import { + WAZUH_DATA_ABSOLUTE_PATH, + WAZUH_DATA_DOWNLOADS_DIRECTORY_PATH, + WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH, +} from '../../../common/constants'; +import { + createDataDirectoryIfNotExists, + createDirectoryIfNotExists, +} from '../../lib/filesystem'; import migrateReportsDirectoryName, { isMD5 } from './reports_directory_name'; function mockContextCreator(loggerLevel: string) { @@ -12,12 +19,14 @@ function mockContextCreator(loggerLevel: string) { function createLogger(level: string) { return jest.fn(function (message: string) { - const levelLogIncluded: number = levels.findIndex((level) => level === loggerLevel); - levelLogIncluded > -1 - && levels.slice(levelLogIncluded).includes(level) - && logs.push({ level, message }); + const levelLogIncluded: number = levels.findIndex( + level => level === loggerLevel, + ); + levelLogIncluded > -1 && + levels.slice(levelLogIncluded).includes(level) && + logs.push({ level, message }); }); - }; + } const ctx = { wazuh: { @@ -25,20 +34,16 @@ function mockContextCreator(loggerLevel: string) { info: createLogger('info'), warn: createLogger('warn'), error: createLogger('error'), - debug: createLogger('debug') - } + debug: createLogger('debug'), + }, }, /* Mocked logs getter. It is only for testing purpose.*/ _getLogs(logLevel: string) { return logLevel ? logs.filter(({ level }) => level === logLevel) : logs; - } - } + }, + }; return ctx; -}; - -jest.mock('../../lib/logger', () => ({ - log: jest.fn() -})); +} beforeAll(() => { // Create /data/wazuh directory. @@ -59,10 +64,21 @@ describe("[migration] `reports` directory doesn't exist", () => { // Migrate the directories migrateReportsDirectoryName(mockContext); // Logs that the task started and skipped. - expect(mockContext._getLogs('debug').filter(({ message }) => message.includes("Task started"))).toHaveLength(1); - expect(mockContext._getLogs('debug').filter(({ message }) => message.includes("Reports directory doesn't exist. The task is not required. Skip."))).toHaveLength(1); + expect( + mockContext + ._getLogs('debug') + .filter(({ message }) => message.includes('Task started')), + ).toHaveLength(1); + expect( + mockContext + ._getLogs('debug') + .filter(({ message }) => + message.includes( + "Reports directory doesn't exist. The task is not required. Skip.", + ), + ), + ).toHaveLength(1); }); - }); describe('[migration] Rename the subdirectories of `reports` directory', () => { @@ -91,72 +107,178 @@ describe('[migration] Rename the subdirectories of `reports` directory', () => { const userDirectoriesTest1 = []; const userDirectoriesTest2 = [userNameDirectory1, userNameDirectory2]; - const userDirectoriesTest3 = [userNameDirectory1, userNameDirectory2, userNameDirectory3, userNameDirectory4]; + const userDirectoriesTest3 = [ + userNameDirectory1, + userNameDirectory2, + userNameDirectory3, + userNameDirectory4, + ]; const userDirectoriesTest4 = [userNameDirectory1, userNameDirectory1MD5]; - const userDirectoriesTest5 = [{ ...userNameDirectory1, errorRenaming: true }, userNameDirectory1MD5WithFiles, userNameDirectory2]; - const userDirectoriesTest6 = [{ ...userNameDirectory1, errorRenaming: true }, userNameDirectory1MD5WithFiles, { ...userNameDirectory2, errorRenaming: true }, userNameDirectory2MD5WithFiles]; - const userDirectoriesTest7 = [userNameDirectory1WithFiles, userNameDirectory2WithFiles]; - const userDirectoriesTest8 = [userNameDirectory1MD5WithFiles, userNameDirectory2MD5WithFiles]; + const userDirectoriesTest5 = [ + { ...userNameDirectory1, errorRenaming: true }, + userNameDirectory1MD5WithFiles, + userNameDirectory2, + ]; + const userDirectoriesTest6 = [ + { ...userNameDirectory1, errorRenaming: true }, + userNameDirectory1MD5WithFiles, + { ...userNameDirectory2, errorRenaming: true }, + userNameDirectory2MD5WithFiles, + ]; + const userDirectoriesTest7 = [ + userNameDirectory1WithFiles, + userNameDirectory2WithFiles, + ]; + const userDirectoriesTest8 = [ + userNameDirectory1MD5WithFiles, + userNameDirectory2MD5WithFiles, + ]; function formatUserDirectoriesTest(inputs: any) { return inputs.length - ? inputs.map(input => `[${input.name}:${input.files}${input.errorRenaming ? ' (Error: renaming)' : ''}]`).join(', ') - : 'None' - }; + ? inputs + .map( + input => + `[${input.name}:${input.files}${ + input.errorRenaming ? ' (Error: renaming)' : '' + }]`, + ) + .join(', ') + : 'None'; + } it.each` - directories | foundRequireRenamingDirectories | renamedDirectories | title - ${userDirectoriesTest1} | ${0} | ${0} | ${formatUserDirectoriesTest(userDirectoriesTest1)} - ${userDirectoriesTest2} | ${2} | ${2} | ${formatUserDirectoriesTest(userDirectoriesTest2)} - ${userDirectoriesTest3} | ${4} | ${4} | ${formatUserDirectoriesTest(userDirectoriesTest3)} - ${userDirectoriesTest4} | ${1} | ${1} | ${formatUserDirectoriesTest(userDirectoriesTest4)} - ${userDirectoriesTest5} | ${2} | ${1} | ${formatUserDirectoriesTest(userDirectoriesTest5)} - ${userDirectoriesTest6} | ${2} | ${0} | ${formatUserDirectoriesTest(userDirectoriesTest6)} - ${userDirectoriesTest7} | ${2} | ${2} | ${formatUserDirectoriesTest(userDirectoriesTest7)} - ${userDirectoriesTest8} | ${0} | ${0} | ${formatUserDirectoriesTest(userDirectoriesTest8)} - `('Migrate Directories: $title - FoundRequireRenamingDirectories: $foundRequireRenamingDirectories - renamedDirectories: $renamedDirectories.', ({ directories, foundRequireRenamingDirectories, renamedDirectories }) => { - - const errorRenamingDirectoryMessages = foundRequireRenamingDirectories - renamedDirectories; - // Create directories and file/s within directory. - directories.forEach(({ name, files }) => { - createDirectoryIfNotExists(path.join(WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH, name)); - if (files) { - Array.from(Array(files).keys()).forEach(indexFile => { - fs.closeSync(fs.openSync(path.join(WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH, name, `report_${indexFile}.pdf`), 'w')); - }); - } - }); + directories | foundRequireRenamingDirectories | renamedDirectories | title + ${userDirectoriesTest1} | ${0} | ${0} | ${formatUserDirectoriesTest(userDirectoriesTest1)} + ${userDirectoriesTest2} | ${2} | ${2} | ${formatUserDirectoriesTest(userDirectoriesTest2)} + ${userDirectoriesTest3} | ${4} | ${4} | ${formatUserDirectoriesTest(userDirectoriesTest3)} + ${userDirectoriesTest4} | ${1} | ${1} | ${formatUserDirectoriesTest(userDirectoriesTest4)} + ${userDirectoriesTest5} | ${2} | ${1} | ${formatUserDirectoriesTest(userDirectoriesTest5)} + ${userDirectoriesTest6} | ${2} | ${0} | ${formatUserDirectoriesTest(userDirectoriesTest6)} + ${userDirectoriesTest7} | ${2} | ${2} | ${formatUserDirectoriesTest(userDirectoriesTest7)} + ${userDirectoriesTest8} | ${0} | ${0} | ${formatUserDirectoriesTest(userDirectoriesTest8)} + `( + 'Migrate Directories: $title - FoundRequireRenamingDirectories: $foundRequireRenamingDirectories - renamedDirectories: $renamedDirectories.', + ({ directories, foundRequireRenamingDirectories, renamedDirectories }) => { + const errorRenamingDirectoryMessages = + foundRequireRenamingDirectories - renamedDirectories; + // Create directories and file/s within directory. + directories.forEach(({ name, files }) => { + createDirectoryIfNotExists( + path.join(WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH, name), + ); + if (files) { + Array.from(Array(files).keys()).forEach(indexFile => { + fs.closeSync( + fs.openSync( + path.join( + WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH, + name, + `report_${indexFile}.pdf`, + ), + 'w', + ), + ); + }); + } + }); - // Migrate the directories. - migrateReportsDirectoryName(mockContext); + // Migrate the directories. + migrateReportsDirectoryName(mockContext); + + // Check the quantity of directories were found for renaming renaming. + expect( + mockContext + ._getLogs() + .filter(({ message }) => + message.includes('Found reports directory to migrate'), + ), + ).toHaveLength(foundRequireRenamingDirectories); + // Check the quantity of directories were renamed. + expect( + mockContext + ._getLogs() + .filter(({ message }) => message.includes('Renamed directory [')), + ).toHaveLength(renamedDirectories); + expect( + mockContext + ._getLogs('error') + .filter(({ message }) => + message.includes(`Error renaming directory [`), + ), + ).toHaveLength(errorRenamingDirectoryMessages); - // Check the quantity of directories were found for renaming renaming. - expect(mockContext._getLogs().filter(({ message }) => message.includes('Found reports directory to migrate'))).toHaveLength(foundRequireRenamingDirectories); - // Check the quantity of directories were renamed. - expect(mockContext._getLogs().filter(({ message }) => message.includes('Renamed directory ['))).toHaveLength(renamedDirectories); - expect(mockContext._getLogs('error').filter(({ message }) => message.includes(`Error renaming directory [`))).toHaveLength(errorRenamingDirectoryMessages); - - directories.forEach(({ name, ...rest }) => { - if (!rest.errorRenaming) { - if (isMD5(name)) { - // If directory name is a valid MD5, the directory should exist. - expect(fs.existsSync(path.join(WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH, name))).toBe(true); + directories.forEach(({ name, ...rest }) => { + if (!rest.errorRenaming) { + if (isMD5(name)) { + // If directory name is a valid MD5, the directory should exist. + expect( + fs.existsSync( + path.join(WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH, name), + ), + ).toBe(true); + } else { + // If directory name is not a valid MD5, the directory should be renamed. New directory exists and old directory doesn't exist. + expect( + mockContext + ._getLogs() + .filter(({ message }) => + message.includes(`Renamed directory [${name}`), + ), + ).toHaveLength(1); + expect( + mockContext + ._getLogs() + .filter(({ message }) => + message.includes( + `Found reports directory to migrate: [${name}`, + ), + ), + ).toHaveLength(1); + expect( + fs.existsSync( + path.join( + WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH, + md5(name), + ), + ), + ).toBe(true); + expect( + !fs.existsSync( + path.join(WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH, name), + ), + ).toBe(true); + } } else { - // If directory name is not a valid MD5, the directory should be renamed. New directory exists and old directory doesn't exist. - expect(mockContext._getLogs().filter(({ message }) => message.includes(`Renamed directory [${name}`))).toHaveLength(1); - expect(mockContext._getLogs().filter(({ message }) => message.includes(`Found reports directory to migrate: [${name}`))).toHaveLength(1); - expect(fs.existsSync(path.join(WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH, md5(name)))).toBe(true); - expect(!fs.existsSync(path.join(WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH, name))).toBe(true); - }; - } else { - // Check there was an error renaming the directory because of the directory exist and contains files. - expect(mockContext._getLogs().filter(({ message }) => message.includes(`Found reports directory to migrate: [${name}`))).toHaveLength(1); - expect( - mockContext._getLogs('error').some(({ message }) => message.includes(`Error renaming directory [${name}`)) - ).toBe(true); - expect(fs.existsSync(path.join(WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH, name))).toBe(true); - expect(fs.existsSync(path.join(WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH, md5(name)))).toBe(true); - } - }); - }); + // Check there was an error renaming the directory because of the directory exist and contains files. + expect( + mockContext + ._getLogs() + .filter(({ message }) => + message.includes( + `Found reports directory to migrate: [${name}`, + ), + ), + ).toHaveLength(1); + expect( + mockContext + ._getLogs('error') + .some(({ message }) => + message.includes(`Error renaming directory [${name}`), + ), + ).toBe(true); + expect( + fs.existsSync( + path.join(WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH, name), + ), + ).toBe(true); + expect( + fs.existsSync( + path.join(WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH, md5(name)), + ), + ).toBe(true); + } + }); + }, + ); }); diff --git a/plugins/main/server/start/migration-tasks/reports_directory_name.ts b/plugins/main/server/start/migration-tasks/reports_directory_name.ts index df81b8851e..288f8524f2 100644 --- a/plugins/main/server/start/migration-tasks/reports_directory_name.ts +++ b/plugins/main/server/start/migration-tasks/reports_directory_name.ts @@ -2,18 +2,15 @@ import fs from 'fs'; import md5 from 'md5'; import path from 'path'; import { WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH } from '../../../common/constants'; -import { log } from '../../lib/logger'; /** * This task renames the report user folder from username to hashed username. - * @param context - * @returns + * @param context + * @returns */ export default function migrateReportsDirectoryName(context) { - // Create a wrapper function that logs to plugin files and platform logging system - const createLog = (level: string) => (message) => { - log('migration:reportsDirectoryName', message, level); + const createLog = (level: string) => message => { context.wazuh.logger[level](`migration:reportsDirectoryName: ${message}`); }; @@ -26,38 +23,55 @@ export default function migrateReportsDirectoryName(context) { }; try { - logger.debug('Task started'); + logger.debug('Task started: Migrate reports directory name'); // Skip the task if the directory that stores the reports files doesn't exist in the file system if (!fs.existsSync(WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH)) { - logger.debug("Reports directory doesn't exist. The task is not required. Skip."); + logger.debug( + "Reports directory doesn't exist. The task is not required. Skip.", + ); return; - }; + } // Read the directories/files in the reports path - logger.debug(`Reading reports directory: ${WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH}`); - fs.readdirSync(WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH, { withFileTypes: true }) - .forEach((fileDirent) => { - // If it is a directory and has not a valid MD5 hash, continue the task. - if (fileDirent.isDirectory() && !isMD5(fileDirent.name)) { - // Generate the origin and target path and hash the name - const originDirectoryPath = path.join(WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH, fileDirent.name); - const targetDirectoryName = md5(fileDirent.name); - const targetDirectoryPath = path.join(WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH, targetDirectoryName); - try { - logger.info(`Found reports directory to migrate: [${fileDirent.name}]`); - // Rename the directory from origin to target path - fs.renameSync(originDirectoryPath, targetDirectoryPath); - logger.info(`Renamed directory [${fileDirent.name} (${originDirectoryPath})] to [${targetDirectoryName} (${targetDirectoryPath})]`); - } catch (error) { - logger.error(`Error renaming directory [${fileDirent.name} (${originDirectoryPath})] to [${targetDirectoryName} (${targetDirectoryPath})]: ${error.message}`); - } - }; - }); - logger.debug('Task finished'); + logger.debug( + `Reading reports directory: ${WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH}`, + ); + fs.readdirSync(WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH, { + withFileTypes: true, + }).forEach(fileDirent => { + // If it is a directory and has not a valid MD5 hash, continue the task. + if (fileDirent.isDirectory() && !isMD5(fileDirent.name)) { + // Generate the origin and target path and hash the name + const originDirectoryPath = path.join( + WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH, + fileDirent.name, + ); + const targetDirectoryName = md5(fileDirent.name); + const targetDirectoryPath = path.join( + WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH, + targetDirectoryName, + ); + try { + logger.info( + `Found reports directory to migrate: [${fileDirent.name}]`, + ); + // Rename the directory from origin to target path + fs.renameSync(originDirectoryPath, targetDirectoryPath); + logger.info( + `Renamed directory [${fileDirent.name} (${originDirectoryPath})] to [${targetDirectoryName} (${targetDirectoryPath})]`, + ); + } catch (error) { + logger.error( + `Error renaming directory [${fileDirent.name} (${originDirectoryPath})] to [${targetDirectoryName} (${targetDirectoryPath})]: ${error.message}`, + ); + } + } + }); + logger.debug('Task finished: Migrate reports directory name'); } catch (error) { logger.error(`Error: ${error.message}`); - }; + } } // Check that the text is a valid MD5 hash @@ -65,4 +79,4 @@ export default function migrateReportsDirectoryName(context) { export function isMD5(text: string) { const regexMD5 = /^[a-f0-9]{32}$/gi; return regexMD5.test(text); -} \ No newline at end of file +} diff --git a/plugins/main/server/start/monitoring/index.ts b/plugins/main/server/start/monitoring/index.ts index c17e844897..ca87b5121e 100644 --- a/plugins/main/server/start/monitoring/index.ts +++ b/plugins/main/server/start/monitoring/index.ts @@ -10,25 +10,25 @@ * Find more information about this on the LICENSE file. */ import cron from 'node-cron'; -import { log } from '../../lib/logger'; import { monitoringTemplate } from '../../integration-files/monitoring-template'; import { getConfiguration } from '../../lib/get-configuration'; import { parseCron } from '../../lib/parse-cron'; import { indexDate } from '../../lib/index-date'; import { buildIndexSettings } from '../../lib/build-index-settings'; -import { WazuhHostsCtrl } from '../../controllers/wazuh-hosts'; import { + WAZUH_MONITORING_DEFAULT_CRON_FREQ, WAZUH_MONITORING_TEMPLATE_NAME, } from '../../../common/constants'; import { tryCatchForIndexPermissionError } from '../tryCatchForIndexPermissionError'; import { delayAsPromise } from '../../../common/utils'; import { getSettingDefaultValue } from '../../../common/services/settings'; -const blueWazuh = '\u001b[34mwazuh\u001b[39m'; -const monitoringErrorLogColors = [blueWazuh, 'monitoring', 'error']; -const wazuhHostController = new WazuhHostsCtrl(); - -let MONITORING_ENABLED, MONITORING_FREQUENCY, MONITORING_CRON_FREQ, MONITORING_CREATION, MONITORING_INDEX_PATTERN, MONITORING_INDEX_PREFIX; +let MONITORING_ENABLED, + MONITORING_FREQUENCY, + MONITORING_CRON_FREQ, + MONITORING_CREATION, + MONITORING_INDEX_PATTERN, + MONITORING_INDEX_PREFIX; // Utils functions /** @@ -37,9 +37,15 @@ let MONITORING_ENABLED, MONITORING_FREQUENCY, MONITORING_CRON_FREQ, MONITORING_C * @param configuration * @param defaultValue */ -function getAppConfigurationSetting(setting: string, configuration: any, defaultValue: any) { - return typeof configuration[setting] !== 'undefined' ? configuration[setting] : defaultValue; -}; +function getAppConfigurationSetting( + setting: string, + configuration: any, + defaultValue: any, +) { + return typeof configuration[setting] !== 'undefined' + ? configuration[setting] + : defaultValue; +} /** * Set the monitoring variables @@ -47,48 +53,68 @@ function getAppConfigurationSetting(setting: string, configuration: any, default */ function initMonitoringConfiguration(context) { try { + context.wazuh.logger.debug('Reading configuration'); const appConfig = getConfiguration(); - MONITORING_ENABLED = appConfig && typeof appConfig['wazuh.monitoring.enabled'] !== 'undefined' - ? appConfig['wazuh.monitoring.enabled'] && - appConfig['wazuh.monitoring.enabled'] !== 'worker' - : getSettingDefaultValue('wazuh.monitoring.enabled'); - MONITORING_FREQUENCY = getAppConfigurationSetting('wazuh.monitoring.frequency', appConfig, getSettingDefaultValue('wazuh.monitoring.frequency')); - MONITORING_CRON_FREQ = parseCron(MONITORING_FREQUENCY); - MONITORING_CREATION = getAppConfigurationSetting('wazuh.monitoring.creation', appConfig, getSettingDefaultValue('wazuh.monitoring.creation')); - - MONITORING_INDEX_PATTERN = getAppConfigurationSetting('wazuh.monitoring.pattern', appConfig, getSettingDefaultValue('wazuh.monitoring.pattern')); - const lastCharIndexPattern = MONITORING_INDEX_PATTERN[MONITORING_INDEX_PATTERN.length - 1]; + MONITORING_ENABLED = + appConfig && typeof appConfig['wazuh.monitoring.enabled'] !== 'undefined' + ? appConfig['wazuh.monitoring.enabled'] && + appConfig['wazuh.monitoring.enabled'] !== 'worker' + : getSettingDefaultValue('wazuh.monitoring.enabled'); + MONITORING_FREQUENCY = getAppConfigurationSetting( + 'wazuh.monitoring.frequency', + appConfig, + getSettingDefaultValue('wazuh.monitoring.frequency'), + ); + try { + MONITORING_CRON_FREQ = parseCron(MONITORING_FREQUENCY); + } catch (error) { + context.wazuh.logger.warn( + `Using default value ${WAZUH_MONITORING_DEFAULT_CRON_FREQ} due to: ${ + error.message || error + }`, + ); + MONITORING_CRON_FREQ = WAZUH_MONITORING_DEFAULT_CRON_FREQ; + } + MONITORING_CREATION = getAppConfigurationSetting( + 'wazuh.monitoring.creation', + appConfig, + getSettingDefaultValue('wazuh.monitoring.creation'), + ); + + MONITORING_INDEX_PATTERN = getAppConfigurationSetting( + 'wazuh.monitoring.pattern', + appConfig, + getSettingDefaultValue('wazuh.monitoring.pattern'), + ); + const lastCharIndexPattern = + MONITORING_INDEX_PATTERN[MONITORING_INDEX_PATTERN.length - 1]; if (lastCharIndexPattern !== '*') { MONITORING_INDEX_PATTERN += '*'; - }; - MONITORING_INDEX_PREFIX = MONITORING_INDEX_PATTERN.slice(0, MONITORING_INDEX_PATTERN.length - 1); + } + MONITORING_INDEX_PREFIX = MONITORING_INDEX_PATTERN.slice( + 0, + MONITORING_INDEX_PATTERN.length - 1, + ); - log( - 'monitoring:initMonitoringConfiguration', + context.wazuh.logger.debug( `wazuh.monitoring.enabled: ${MONITORING_ENABLED}`, - 'debug' ); - log( - 'monitoring:initMonitoringConfiguration', + context.wazuh.logger.debug( `wazuh.monitoring.frequency: ${MONITORING_FREQUENCY} (${MONITORING_CRON_FREQ})`, - 'debug' ); - log( - 'monitoring:initMonitoringConfiguration', + context.wazuh.logger.debug( + `wazuh.monitoring.creation: ${MONITORING_CREATION}`, + ); + + context.wazuh.logger.debug( `wazuh.monitoring.pattern: ${MONITORING_INDEX_PATTERN} (index prefix: ${MONITORING_INDEX_PREFIX})`, - 'debug' ); } catch (error) { - const errorMessage = error.message || error; - log( - 'monitoring:initMonitoringConfiguration', - errorMessage - ); - context.wazuh.logger.error(errorMessage) + context.wazuh.logger.error(error.message); } -}; +} /** * Main. First execution when installing / loading App. @@ -98,10 +124,9 @@ async function init(context) { try { if (MONITORING_ENABLED) { await checkTemplate(context); - }; + } } catch (error) { const errorMessage = error.message || error; - log('monitoring:init', error.message || error); context.wazuh.logger.error(errorMessage); } } @@ -111,46 +136,48 @@ async function init(context) { */ async function checkTemplate(context) { try { - log( - 'monitoring:checkTemplate', - 'Updating the monitoring template', - 'debug' - ); - try { + context.wazuh.logger.debug( + `Getting the ${WAZUH_MONITORING_TEMPLATE_NAME} template`, + ); // Check if the template already exists - const currentTemplate = await context.core.opensearch.client.asInternalUser.indices.getTemplate({ - name: WAZUH_MONITORING_TEMPLATE_NAME - }); + const currentTemplate = + await context.core.opensearch.client.asInternalUser.indices.getTemplate( + { + name: WAZUH_MONITORING_TEMPLATE_NAME, + }, + ); // Copy already created index patterns - monitoringTemplate.index_patterns = currentTemplate.body[WAZUH_MONITORING_TEMPLATE_NAME].index_patterns; + monitoringTemplate.index_patterns = + currentTemplate.body[WAZUH_MONITORING_TEMPLATE_NAME].index_patterns; } catch (error) { // Init with the default index pattern - monitoringTemplate.index_patterns = [getSettingDefaultValue('wazuh.monitoring.pattern')]; + monitoringTemplate.index_patterns = [ + getSettingDefaultValue('wazuh.monitoring.pattern'), + ]; } // Check if the user is using a custom pattern and add it to the template if it does if (!monitoringTemplate.index_patterns.includes(MONITORING_INDEX_PATTERN)) { monitoringTemplate.index_patterns.push(MONITORING_INDEX_PATTERN); - }; + } // Update the monitoring template + context.wazuh.logger.debug( + `Updating the ${WAZUH_MONITORING_TEMPLATE_NAME} template`, + ); await context.core.opensearch.client.asInternalUser.indices.putTemplate({ name: WAZUH_MONITORING_TEMPLATE_NAME, - body: monitoringTemplate + body: monitoringTemplate, }); - log( - 'monitoring:checkTemplate', - 'Updated the monitoring template', - 'debug' + context.wazuh.logger.info( + `Updated the ${WAZUH_MONITORING_TEMPLATE_NAME} template`, ); } catch (error) { - const errorMessage = `Something went wrong updating the monitoring template ${error.message || error}`; - log( - 'monitoring:checkTemplate', - errorMessage - ); - context.wazuh.logger.error(monitoringErrorLogColors, errorMessage); + const errorMessage = `Something went wrong updating the ${WAZUH_MONITORING_TEMPLATE_NAME} template ${ + error.message || error + }`; + context.wazuh.logger.error(errorMessage); throw error; } } @@ -161,39 +188,57 @@ async function checkTemplate(context) { * @param {*} data */ async function insertMonitoringDataElasticsearch(context, data) { - const monitoringIndexName = MONITORING_INDEX_PREFIX + indexDate(MONITORING_CREATION); + const monitoringIndexName = + MONITORING_INDEX_PREFIX + indexDate(MONITORING_CREATION); if (!MONITORING_ENABLED) { return; - }; + } try { await tryCatchForIndexPermissionError(monitoringIndexName)(async () => { - const exists = await context.core.opensearch.client.asInternalUser.indices.exists({ index: monitoringIndexName }); + context.wazuh.logger.debug( + `Checking the existence of ${monitoringIndexName} index`, + ); + const exists = + await context.core.opensearch.client.asInternalUser.indices.exists({ + index: monitoringIndexName, + }); if (!exists.body) { + context.wazuh.logger.debug( + `The ${monitoringIndexName} index does not exist`, + ); await createIndex(context, monitoringIndexName); - }; + } else { + context.wazuh.logger.debug(`The ${monitoringIndexName} index exists`); + } // Update the index configuration const appConfig = getConfiguration(); const indexConfiguration = buildIndexSettings( appConfig, 'wazuh.monitoring', - getSettingDefaultValue('wazuh.monitoring.shards') + getSettingDefaultValue('wazuh.monitoring.shards'), ); // To update the index settings with this client is required close the index, update the settings and open it // Number of shards is not dynamic so delete that setting if it's given delete indexConfiguration.settings.index.number_of_shards; + context.wazuh.logger.debug( + `Adding settings to ${monitoringIndexName} index`, + ); await context.core.opensearch.client.asInternalUser.indices.putSettings({ index: monitoringIndexName, - body: indexConfiguration + body: indexConfiguration, }); + context.wazuh.logger.info( + `Settings added to ${monitoringIndexName} index`, + ); + // Insert data to the monitoring index await insertDataToIndex(context, monitoringIndexName, data); })(); } catch (error) { - log('monitoring:insertMonitoringDataElasticsearch', error.message || error); - context.wazuh.logger.error(error.message); + context.wazuh.logger.error(error.message || error); } } @@ -203,39 +248,45 @@ async function insertMonitoringDataElasticsearch(context, data) { * @param {String} indexName The name for the index (e.g. daily: wazuh-monitoring-YYYY.MM.DD) * @param {*} data */ -async function insertDataToIndex(context, indexName: string, data: { agents: any[], apiHost }) { +async function insertDataToIndex( + context, + indexName: string, + data: { agents: any[]; apiHost }, +) { const { agents, apiHost } = data; try { if (agents.length > 0) { - log( - 'monitoring:insertDataToIndex', + context.wazuh.logger.debug( `Bulk data to index ${indexName} for ${agents.length} agents`, - 'debug' ); - const bodyBulk = agents.map(agent => { - const agentInfo = { ...agent }; - agentInfo['timestamp'] = new Date(Date.now()).toISOString(); - agentInfo.host = agent.manager; - agentInfo.cluster = { name: apiHost.clusterName ? apiHost.clusterName : 'disabled' }; - return `{ "index": { "_index": "${indexName}" } }\n${JSON.stringify(agentInfo)}\n`; - }).join(''); + const bodyBulk = agents + .map(agent => { + const agentInfo = { ...agent }; + agentInfo['timestamp'] = new Date(Date.now()).toISOString(); + agentInfo.host = agent.manager; + agentInfo.cluster = { + name: apiHost.clusterName ? apiHost.clusterName : 'disabled', + }; + return `{ "index": { "_index": "${indexName}" } }\n${JSON.stringify( + agentInfo, + )}\n`; + }) + .join(''); await context.core.opensearch.client.asInternalUser.bulk({ index: indexName, - body: bodyBulk + body: bodyBulk, }); - log( - 'monitoring:insertDataToIndex', + context.wazuh.logger.info( `Bulk data to index ${indexName} for ${agents.length} agents completed`, - 'debug' ); } } catch (error) { - log( - 'monitoring:insertDataToIndex', - `Error inserting agent data into elasticsearch. Bulk request failed due to ${error.message || - error}` + context.wazuh.logger.error( + `Error inserting agent data into elasticsearch. Bulk request failed due to ${ + error.message || error + }`, ); } } @@ -253,67 +304,65 @@ async function createIndex(context, indexName: string) { const IndexConfiguration = { settings: { index: { - number_of_shards: getAppConfigurationSetting('wazuh.monitoring.shards', appConfig, getSettingDefaultValue('wazuh.monitoring.shards')), - number_of_replicas: getAppConfigurationSetting('wazuh.monitoring.replicas', appConfig, getSettingDefaultValue('wazuh.monitoring.replicas')) - } - } + number_of_shards: getAppConfigurationSetting( + 'wazuh.monitoring.shards', + appConfig, + getSettingDefaultValue('wazuh.monitoring.shards'), + ), + number_of_replicas: getAppConfigurationSetting( + 'wazuh.monitoring.replicas', + appConfig, + getSettingDefaultValue('wazuh.monitoring.replicas'), + ), + }, + }, }; + context.wazuh.logger.debug(`Creating ${indexName} index`); + await context.core.opensearch.client.asInternalUser.indices.create({ index: indexName, - body: IndexConfiguration + body: IndexConfiguration, }); - log( - 'monitoring:createIndex', - `Successfully created new index: ${indexName}`, - 'debug' - ); + context.wazuh.logger.info(`${indexName} index created`); } catch (error) { - const errorMessage = `Could not create ${indexName} index on elasticsearch due to ${error.message || error}`; - log( - 'monitoring:createIndex', - errorMessage + context.wazuh.logger.error( + `Could not create ${indexName} index: ${error.message || error}`, ); - context.wazuh.logger.error(errorMessage); } } /** -* Wait until Kibana server is ready -*/ + * Wait until Kibana server is ready + */ async function checkPluginPlatformStatus(context) { try { - log( - 'monitoring:checkPluginPlatformStatus', - 'Waiting for Kibana and Elasticsearch servers to be ready...', - 'debug' - ); + context.wazuh.logger.debug('Waiting for platform servers to be ready...'); await checkElasticsearchServer(context); await init(context); - return; } catch (error) { - log( - 'monitoring:checkPluginPlatformStatus', - error.mesage || error - ); + context.wazuh.logger.error(error.message || error); try { await delayAsPromise(3000); await checkPluginPlatformStatus(context); - } catch (error) { }; + } catch (error) {} } } - /** * Check Elasticsearch Server status and Kibana index presence */ async function checkElasticsearchServer(context) { try { - const data = await context.core.opensearch.client.asInternalUser.indices.exists({ - index: context.server.config.opensearchDashboards.index - }); + context.wazuh.logger.debug( + `Checking the existence of ${context.server.config.opensearchDashboards.index} index`, + ); + const data = + await context.core.opensearch.client.asInternalUser.indices.exists({ + index: context.server.config.opensearchDashboards.index, + }); return data.body; // TODO: check if Elasticsearch can receive requests @@ -323,51 +372,47 @@ async function checkElasticsearchServer(context) { // } return Promise.reject(data); } catch (error) { - log('monitoring:checkElasticsearchServer', error.message || error); + context.wazuh.logger.error(error.message || error); return Promise.reject(error); } } -const fakeResponseEndpoint = { - ok: (body: any) => body, - custom: (body: any) => body, -} /** * Get API configuration from elastic and callback to loadCredentials */ -async function getHostsConfiguration() { +async function getHostsConfiguration(context) { try { - const hosts = await wazuhHostController.getHostsEntries(false, false, fakeResponseEndpoint); - if (hosts.body.length) { - return hosts.body; - }; + const hosts = + await context.wazuh_core.serverAPIHostEntries.getHostsEntries(); + if (hosts.length) { + return hosts; + } - log( - 'monitoring:getConfig', - 'There are no Wazuh API entries yet', - 'debug' - ); + context.wazuh.logger.debug('There are no API host entries yet'); return Promise.reject({ error: 'no credentials', - error_code: 1 + error_code: 1, }); } catch (error) { - log('monitoring:getHostsConfiguration', error.message || error); + context.wazuh.logger.error(error.message || error); return Promise.reject({ - error: 'no wazuh hosts', - error_code: 2 + error: 'no API hosts', + error_code: 2, }); } } /** - * Task used by the cron job. - */ + * Task used by the cron job. + */ async function cronTask(context) { try { - const templateMonitoring = await context.core.opensearch.client.asInternalUser.indices.getTemplate({ name: WAZUH_MONITORING_TEMPLATE_NAME }); + const templateMonitoring = + await context.core.opensearch.client.asInternalUser.indices.getTemplate({ + name: WAZUH_MONITORING_TEMPLATE_NAME, + }); - const apiHosts = await getHostsConfiguration(); + const apiHosts = await getHostsConfiguration(context); const apiHostsUnique = (apiHosts || []).filter( (apiHost, index, self) => index === @@ -376,16 +421,17 @@ async function cronTask(context) { t.user === apiHost.user && t.password === apiHost.password && t.url === apiHost.url && - t.port === apiHost.port - ) + t.port === apiHost.port, + ), ); for (let apiHost of apiHostsUnique) { try { const { agents, apiHost: host } = await getApiInfo(context, apiHost); - await insertMonitoringDataElasticsearch(context, { agents, apiHost: host }); - } catch (error) { - - }; + await insertMonitoringDataElasticsearch(context, { + agents, + apiHost: host, + }); + } catch (error) {} } } catch (error) { // Retry to call itself again if Kibana index is not ready yet @@ -399,8 +445,6 @@ async function cronTask(context) { // return cronTask(context); // } // } catch (error) {} //eslint-disable-line - - log('monitoring:cronTask', error.message || error); context.wazuh.logger.error(error.message || error); } } @@ -412,20 +456,34 @@ async function cronTask(context) { */ async function getApiInfo(context, apiHost) { try { - log('monitoring:getApiInfo', `Getting API info for ${apiHost.id}`, 'debug'); - const responseIsCluster = await context.wazuh.api.client.asInternalUser.request('GET', '/cluster/status', {}, { apiHostID: apiHost.id }); - const isCluster = (((responseIsCluster || {}).data || {}).data || {}).enabled === 'yes'; + context.wazuh.logger.debug(`Getting API info for ${apiHost.id}`); + const responseIsCluster = + await context.wazuh.api.client.asInternalUser.request( + 'GET', + '/cluster/status', + {}, + { apiHostID: apiHost.id }, + ); + const isCluster = + (((responseIsCluster || {}).data || {}).data || {}).enabled === 'yes'; if (isCluster) { - const responseClusterInfo = await context.wazuh.api.client.asInternalUser.request('GET', `/cluster/local/info`, {}, { apiHostID: apiHost.id }); - apiHost.clusterName = responseClusterInfo.data.data.affected_items[0].cluster; - }; + const responseClusterInfo = + await context.wazuh.api.client.asInternalUser.request( + 'GET', + `/cluster/local/info`, + {}, + { apiHostID: apiHost.id }, + ); + apiHost.clusterName = + responseClusterInfo.data.data.affected_items[0].cluster; + } const agents = await fetchAllAgentsFromApiHost(context, apiHost); return { agents, apiHost }; } catch (error) { - log('monitoring:getApiInfo', error.message || error); + context.wazuh.logger.error(error.message || error); throw error; } -}; +} /** * Fetch all agents for the API provided @@ -435,25 +493,30 @@ async function getApiInfo(context, apiHost) { async function fetchAllAgentsFromApiHost(context, apiHost) { let agents = []; try { - log('monitoring:fetchAllAgentsFromApiHost', `Getting all agents from ApiID: ${apiHost.id}`, 'debug'); - const responseAgentsCount = await context.wazuh.api.client.asInternalUser.request( - 'GET', - '/agents', - { - params: { - offset: 0, - limit: 1, - q: 'id!=000' - } - }, { apiHostID: apiHost.id }); + context.wazuh.logger.debug(`Getting all agents from ApiID: ${apiHost.id}`); + const responseAgentsCount = + await context.wazuh.api.client.asInternalUser.request( + 'GET', + '/agents', + { + params: { + offset: 0, + limit: 1, + q: 'id!=000', + }, + }, + { apiHostID: apiHost.id }, + ); const agentsCount = responseAgentsCount.data.data.total_affected_items; - log('monitoring:fetchAllAgentsFromApiHost', `ApiID: ${apiHost.id}, Agent count: ${agentsCount}`, 'debug'); + context.wazuh.logger.debug( + `ApiID: ${apiHost.id}, Agent count: ${agentsCount}`, + ); let payload = { offset: 0, limit: 500, - q: 'id!=000' + q: 'id!=000', }; while (agents.length < agentsCount && payload.offset < agentsCount) { @@ -472,29 +535,37 @@ async function fetchAllAgentsFromApiHost(context, apiHost) { - increase the limit of results to retrieve (currently, the requests use the recommended value: 500). See the allowed values. This depends on the selected data because the response could fail if contains a lot of data */ - const responseAgents = await context.wazuh.api.client.asInternalUser.request( - 'GET', - `/agents`, - { params: payload }, - { apiHostID: apiHost.id } - ); + const responseAgents = + await context.wazuh.api.client.asInternalUser.request( + 'GET', + `/agents`, + { params: payload }, + { apiHostID: apiHost.id }, + ); agents = [...agents, ...responseAgents.data.data.affected_items]; payload.offset += payload.limit; } catch (error) { - log('monitoring:fetchAllAgentsFromApiHost', `ApiID: ${apiHost.id}, Error request with offset/limit ${payload.offset}/${payload.limit}: ${error.message || error}`); + context.wazuh.logger.error( + `ApiID: ${apiHost.id}, Error request with offset/limit ${ + payload.offset + }/${payload.limit}: ${error.message || error}`, + ); } } return agents; } catch (error) { - log('monitoring:fetchAllAgentsFromApiHost', `ApiID: ${apiHost.id}. Error: ${error.message || error}`); + context.wazuh.logger.error( + `ApiID: ${apiHost.id}. Error: ${error.message || error}`, + ); throw error; } -}; +} /** * Start the cron job */ export async function jobMonitoringRun(context) { + context.wazuh.logger.debug('Task:Monitoring initializing'); // Init the monitoring variables initMonitoringConfiguration(context); // Check Kibana index and if it is prepared, start the initialization of Wazuh App. @@ -505,4 +576,3 @@ export async function jobMonitoringRun(context) { cron.schedule(MONITORING_CRON_FREQ, () => cronTask(context)); } } - diff --git a/plugins/main/server/start/queue/index.ts b/plugins/main/server/start/queue/index.ts index 36228872c5..707ef2469a 100644 --- a/plugins/main/server/start/queue/index.ts +++ b/plugins/main/server/start/queue/index.ts @@ -10,69 +10,59 @@ * Find more information about this on the LICENSE file. */ import cron from 'node-cron'; -import { log } from '../../lib/logger'; import { WAZUH_QUEUE_CRON_FREQ } from '../../../common/constants'; export let queue = []; -export interface IQueueJob{ +export interface IQueueJob { /** Date object to start the job */ - startAt: Date + startAt: Date; /** Function to execute */ - run: () => void -}; + run: () => void; +} /** * Add a job to the queue. * @param job Job to add to queue */ export function addJobToQueue(job: IQueueJob) { - log('queue:addJob', `New job added`, 'debug'); queue.push(job); -}; +} -async function executePendingJobs() { +async function executePendingJobs(context: any) { try { if (!queue || !queue.length) return; const now: Date = new Date(); const pendingJobs: IQueueJob[] = queue.filter(item => item.startAt <= now); - log( - 'queue:executePendingJobs', - `Pending jobs: ${pendingJobs.length}`, - 'debug' - ); - if (!pendingJobs || !pendingJobs.length){ + context.wazuh.logger.debug(`Pending jobs: ${pendingJobs.length}`); + if (!pendingJobs || !pendingJobs.length) { return; - }; + } queue = queue.filter((item: IQueueJob) => item.startAt > now); for (const job of pendingJobs) { try { - await job.run(); + await job.run(context); } catch (error) { continue; - }; + } } } catch (error) { queue = []; - log('queue:executePendingJobs', error.message || error); return Promise.reject(error); } } /** * Run the job queue it plugin start. - * @param context + * @param context */ export function jobQueueRun(context) { - cron.schedule( - WAZUH_QUEUE_CRON_FREQ, - async () => { - try { - await executePendingJobs(); - } catch (error) { - log('queue:launchCronJob', error.message || error); - } + cron.schedule(WAZUH_QUEUE_CRON_FREQ, async () => { + try { + await executePendingJobs(context); + } catch (error) { + context.wazuh.logger.error(error.message || error); } - ); + }); } diff --git a/plugins/main/server/start/tryCatchForIndexPermissionError.ts b/plugins/main/server/start/tryCatchForIndexPermissionError.ts index 4ddde349a0..c0eee0ee65 100644 --- a/plugins/main/server/start/tryCatchForIndexPermissionError.ts +++ b/plugins/main/server/start/tryCatchForIndexPermissionError.ts @@ -9,27 +9,30 @@ * * Find more information about this on the LICENSE file. */ -import { log } from '../lib/logger'; -export const tryCatchForIndexPermissionError = (wazuhIndex: string) => (functionToTryCatch) => async () => { +export const tryCatchForIndexPermissionError = + (wazuhIndex: string) => functionToTryCatch => async () => { try { - await functionToTryCatch(); + await functionToTryCatch(); + } catch (error) { + enum errorTypes { + SECURITY_EXCEPTION = 'security_exception', + RESPONSE_ERROR = 'Response Error', + } + switch (error.message) { + case errorTypes.SECURITY_EXCEPTION: + error.message = + ( + ( + ((error.meta || error.message).body || error.message).error || + error.message + ).root_cause[0] || error.message + ).reason || error.message; + break; + case errorTypes.RESPONSE_ERROR: + error.message = `Could not check if the index ${wazuhIndex} exists due to no permissions for create, delete or check`; + break; + } + return Promise.reject(error); } - catch (error) { - enum errorTypes{ - SECURITY_EXCEPTION = 'security_exception', - RESPONSE_ERROR = 'Response Error', - } - switch(error.message){ - case errorTypes.SECURITY_EXCEPTION: - error.message = (((((error.meta || error.message).body || error.message).error || error.message).root_cause[0] || error.message).reason || error.message); - break; - case errorTypes.RESPONSE_ERROR: - error.message = `Could not check if the index ${ - wazuhIndex - } exists due to no permissions for create, delete or check`; - break; - } - return Promise.reject(error); - } -} \ No newline at end of file + }; diff --git a/plugins/main/server/types.ts b/plugins/main/server/types.ts index bc80cd26bf..2095695534 100644 --- a/plugins/main/server/types.ts +++ b/plugins/main/server/types.ts @@ -22,5 +22,6 @@ export interface WazuhPluginSetup {} export interface WazuhPluginStart {} export type PluginSetup = { - securityDashboards?: {}, // TODO: Add OpenSearch Dashboards Security interface -} + securityDashboards?: {}; // TODO: Add OpenSearch Dashboards Security interface + wazuhCore: {}; +}; diff --git a/plugins/main/test/cypress/cypress/integration/step-definitions/settings/wazuh-logs/reload-logs.when.js b/plugins/main/test/cypress/cypress/integration/step-definitions/settings/wazuh-logs/reload-logs.when.js deleted file mode 100644 index 19c0f47dc6..0000000000 --- a/plugins/main/test/cypress/cypress/integration/step-definitions/settings/wazuh-logs/reload-logs.when.js +++ /dev/null @@ -1,10 +0,0 @@ -import { clickElement, interceptAs, getSelector } from '../../../utils/driver'; - -import { LOGS_PAGE as pageName} from '../../../utils/pages-constants'; -const reloadLogsLink = getSelector('reloadLogsLink', pageName); - -When('The user reloads the logs', () => { - interceptAs('GET', '/utils/logs', 'apiCheck'); - clickElement(reloadLogsLink); - cy.wait(500); -}); diff --git a/plugins/main/test/server/wazuh-api.js b/plugins/main/test/server/wazuh-api.js index 3c63ae84be..8e48ae980f 100644 --- a/plugins/main/test/server/wazuh-api.js +++ b/plugins/main/test/server/wazuh-api.js @@ -5,7 +5,10 @@ const { PLUGIN_PLATFORM_REQUEST_HEADERS } = require('../../common/constants'); chai.should(); const headers = { - headers: { ...PLUGIN_PLATFORM_REQUEST_HEADERS, 'content-type': 'application/json' } + headers: { + ...PLUGIN_PLATFORM_REQUEST_HEADERS, + 'content-type': 'application/json', + }, }; let API_ID = null; @@ -35,7 +38,7 @@ describe('wazuh-api', () => { 'post', `localhost:5601/api/csv`, { path: '/agents', id: API_ID }, - headers + headers, ); res.body.should.be.a('string'); }); @@ -45,7 +48,7 @@ describe('wazuh-api', () => { 'post', `localhost:5601/api/check-api`, { username: API_USERNAME, url: API_URL, port: API_PORT, id: API_ID }, - headers + headers, ); res.body.should.be.a('object'); res.body.manager.should.be.a('string'); @@ -58,7 +61,7 @@ describe('wazuh-api', () => { 'post', `localhost:5601/api/check-stored-api`, API_ID, - headers + headers, ); res.body.should.be.a('object'); res.body.statusCode.should.be.eql(200); @@ -76,7 +79,7 @@ describe('wazuh-api', () => { 'post', `localhost:5601/api/request`, { method: 'GET', path: '/agents/000', body: {}, id: API_ID }, - headers + headers, ); res.body.should.be.a('object'); res.body.error.should.be.eql(0); @@ -89,7 +92,7 @@ describe('wazuh-api', () => { const res = await needle('get', `localhost:5601/api/pci/all`, {}, {}); res.body.should.be.a('object'); res.body['1.1.1'].should.be.eql( - 'A formal process for approving and testing all network connections and changes to the firewall and router configurations' + 'A formal process for approving and testing all network connections and changes to the firewall and router configurations', ); }); @@ -97,7 +100,7 @@ describe('wazuh-api', () => { const res = await needle('get', `localhost:5601/api/gdpr/all`, {}, {}); res.body.should.be.a('object'); res.body['II_5.1.f'].should.be.eql( - 'Ensure the ongoing confidentiality, integrity, availability and resilience of processing systems and services, verifying its modifications, accesses, locations and guarantee the safety of them.
File sharing protection and file sharing technologies that meet the requirements of data protection.' + 'Ensure the ongoing confidentiality, integrity, availability and resilience of processing systems and services, verifying its modifications, accesses, locations and guarantee the safety of them.
File sharing protection and file sharing technologies that meet the requirements of data protection.', ); }); @@ -106,7 +109,7 @@ describe('wazuh-api', () => { 'get', `localhost:5601/utils/configuration`, {}, - {} + {}, ); res.body.should.be.a('object'); res.body.error.should.be.eql(0); @@ -120,11 +123,4 @@ describe('wazuh-api', () => { res.body.error.should.be.eql(0); res.body.ram.should.be.gt(1); }); - - it('GET /utils/logs', async () => { - const res = await needle('get', `localhost:5601/utils/logs`, {}, {}); - res.body.should.be.a('object'); - res.body.lastLogs.should.be.a('array'); - res.body.error.should.be.eql(0); - }); }); diff --git a/plugins/main/yarn.lock b/plugins/main/yarn.lock index b04bca1416..527f2d2879 100644 --- a/plugins/main/yarn.lock +++ b/plugins/main/yarn.lock @@ -15,20 +15,6 @@ core-js-pure "^3.30.2" regenerator-runtime "^0.13.11" -"@colors/colors@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" - integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== - -"@dabh/diagnostics@^2.0.2": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.3.tgz#7f7e97ee9a725dffc7808d93668cc984e1dc477a" - integrity sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA== - dependencies: - colorspace "1.1.x" - enabled "2.0.x" - kuler "^2.0.0" - "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -509,11 +495,6 @@ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.0.tgz#591c1ce3a702c45ee15f47a42ade72c2fd78978a" integrity sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw== -"@types/triple-beam@^1.3.2": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.2.tgz#38ecb64f01aa0d02b7c8f4222d7c38af6316fef8" - integrity sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g== - "@types/tz-offset@*": version "0.0.0" resolved "https://registry.yarnpkg.com/@types/tz-offset/-/tz-offset-0.0.0.tgz#d58f1cebd794148d245420f8f0660305d320e565" @@ -803,11 +784,6 @@ ast-types@^0.7.0: resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.7.8.tgz#902d2e0d60d071bdcd46dc115e1809ed11c138a9" integrity sha512-RIOpVnVlltB6PcBJ5BMLx+H+6JJ/zjDGU0t7f0L6c2M1dqcK92VQopLBlPQ9R80AVXelfqYgjcPLtHtDbNFg0Q== -async@^3.2.3: - version "3.2.4" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" - integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== - asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -989,13 +965,6 @@ codemirror@^5.18.2: resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.65.14.tgz#e75fbc7247453f1baa71463c33b52adba7e41b2a" integrity sha512-VSNugIBDGt0OU9gDjeVr6fNkoFQznrWEUdAApMlXQNbfE8gGO19776D6MwSqF/V/w/sDwonsQ0z7KmmI9guScg== -color-convert@^1.9.3: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - color-convert@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" @@ -1003,40 +972,11 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== - -color-name@^1.0.0, color-name@~1.1.4: +color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-string@^1.6.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" - integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== - dependencies: - color-name "^1.0.0" - simple-swizzle "^0.2.2" - -color@^3.1.3: - version "3.2.1" - resolved "https://registry.yarnpkg.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164" - integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA== - dependencies: - color-convert "^1.9.3" - color-string "^1.6.0" - -colorspace@1.1.x: - version "1.1.4" - resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.4.tgz#8d442d1186152f60453bf8070cd66eb364e59243" - integrity sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w== - dependencies: - color "^3.1.3" - text-hex "1.0.x" - combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -1263,11 +1203,6 @@ duplexer2@~0.1.4: dependencies: readable-stream "^2.0.2" -enabled@2.0.x: - version "2.0.0" - resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" - integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== - end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" @@ -1754,11 +1689,6 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" -fecha@^4.2.0: - version "4.2.3" - resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" - integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw== - file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" @@ -1794,11 +1724,6 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== -fn.name@1.x.x: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" - integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== - follow-redirects@^1.15.0: version "1.15.2" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" @@ -2125,11 +2050,6 @@ is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: get-intrinsic "^1.2.0" is-typed-array "^1.1.10" -is-arrayish@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" - integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== - is-bigint@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" @@ -2365,11 +2285,6 @@ jwt-decode@^3.1.2: resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59" integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A== -kuler@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" - integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== - levn@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" @@ -2423,18 +2338,6 @@ lodash@^4.15.0, lodash@^4.17.21: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -logform@^2.3.2, logform@^2.4.0: - version "2.5.1" - resolved "https://registry.yarnpkg.com/logform/-/logform-2.5.1.tgz#44c77c34becd71b3a42a3970c77929e52c6ed48b" - integrity sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg== - dependencies: - "@colors/colors" "1.5.0" - "@types/triple-beam" "^1.3.2" - fecha "^4.2.0" - ms "^2.1.1" - safe-stable-stringify "^2.3.1" - triple-beam "^1.3.0" - loglevel@^1.7.1: version "1.8.1" resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.1.tgz#5c621f83d5b48c54ae93b6156353f555963377b4" @@ -2739,13 +2642,6 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -one-time@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/one-time/-/one-time-1.0.0.tgz#e06bc174aed214ed58edede573b433bbf827cb45" - integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g== - dependencies: - fn.name "1.x.x" - onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" @@ -3054,7 +2950,7 @@ readable-stream@^2.0.2, readable-stream@^2.2.2, readable-stream@~2.3.3, readable string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: +readable-stream@^3.1.1, readable-stream@^3.4.0: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== @@ -3181,11 +3077,6 @@ safe-regex-test@^1.0.0: get-intrinsic "^1.1.3" is-regex "^1.1.4" -safe-stable-stringify@^2.3.1: - version "2.4.3" - resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" - integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== - "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -3271,13 +3162,6 @@ simple-get@^4.0.0: once "^1.3.1" simple-concat "^1.0.0" -simple-swizzle@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" - integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== - dependencies: - is-arrayish "^0.3.1" - slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -3310,11 +3194,6 @@ sourcemap-codec@^1.4.1: resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== -stack-trace@0.0.x: - version "0.0.10" - resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" - integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg== - stampit@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/stampit/-/stampit-4.3.2.tgz#cfd3f607dd628a161ce6305621597994b4d56573" @@ -3503,11 +3382,6 @@ tar-stream@^2.1.4: inherits "^2.0.3" readable-stream "^3.1.1" -text-hex@1.0.x: - version "1.0.0" - resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" - integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== - text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -3594,11 +3468,6 @@ tree-sitter@=0.20.4: nan "^2.17.0" prebuild-install "^7.1.1" -triple-beam@^1.3.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.4.1.tgz#6fde70271dc6e5d73ca0c3b24e2d92afb7441984" - integrity sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg== - ts-api-utils@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.1.tgz#8144e811d44c749cd65b2da305a032510774452d" @@ -3832,32 +3701,6 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -winston-transport@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.5.0.tgz#6e7b0dd04d393171ed5e4e4905db265f7ab384fa" - integrity sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q== - dependencies: - logform "^2.3.2" - readable-stream "^3.6.0" - triple-beam "^1.3.0" - -winston@3.9.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/winston/-/winston-3.9.0.tgz#2bbdeb8167a75fac6d9a0c6d002890cd908016c2" - integrity sha512-jW51iW/X95BCW6MMtZWr2jKQBP4hV5bIDq9QrIjfDk6Q9QuxvTKEAlpUNAzP+HYHFFCeENhph16s0zEunu4uuQ== - dependencies: - "@colors/colors" "1.5.0" - "@dabh/diagnostics" "^2.0.2" - async "^3.2.3" - is-stream "^2.0.0" - logform "^2.4.0" - one-time "^1.0.0" - readable-stream "^3.4.0" - safe-stable-stringify "^2.3.1" - stack-trace "0.0.x" - triple-beam "^1.3.0" - winston-transport "^4.5.0" - word-wrap@~1.2.3: version "1.2.5" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" diff --git a/plugins/wazuh-check-updates/package.json b/plugins/wazuh-check-updates/package.json index 1f3a34506a..4804025d53 100644 --- a/plugins/wazuh-check-updates/package.json +++ b/plugins/wazuh-check-updates/package.json @@ -20,8 +20,7 @@ "dependencies": { "axios": "^1.6.1", "md5": "^2.3.0", - "node-cron": "^3.0.2", - "winston": "^3.10.0" + "node-cron": "^3.0.2" }, "devDependencies": { "@testing-library/user-event": "^14.5.0", diff --git a/plugins/wazuh-check-updates/server/plugin-services.ts b/plugins/wazuh-check-updates/server/plugin-services.ts index 1d5e14b699..016c19a7a6 100644 --- a/plugins/wazuh-check-updates/server/plugin-services.ts +++ b/plugins/wazuh-check-updates/server/plugin-services.ts @@ -1,10 +1,14 @@ -import { CoreStart, ISavedObjectsRepository } from 'opensearch-dashboards/server'; +import { + CoreStart, + ISavedObjectsRepository, +} from 'opensearch-dashboards/server'; import { createGetterSetter } from '../../../src/plugins/opensearch_dashboards_utils/common'; import { WazuhCorePluginStart } from '../../wazuh-core/server'; -export const [getInternalSavedObjectsClient, setInternalSavedObjectsClient] = createGetterSetter< - ISavedObjectsRepository ->('SavedObjectsRepository'); +export const [getInternalSavedObjectsClient, setInternalSavedObjectsClient] = + createGetterSetter('SavedObjectsRepository'); export const [getCore, setCore] = createGetterSetter('Core'); export const [getWazuhCore, setWazuhCore] = - createGetterSetter('WazuhCore'); \ No newline at end of file + createGetterSetter('WazuhCore'); +export const [getWazuhCheckUpdatesServices, setWazuhCheckUpdatesServices] = + createGetterSetter('WazuhCheckUpdatesServices'); diff --git a/plugins/wazuh-check-updates/server/plugin.ts b/plugins/wazuh-check-updates/server/plugin.ts index 105e29eee7..4b69b26171 100644 --- a/plugins/wazuh-check-updates/server/plugin.ts +++ b/plugins/wazuh-check-updates/server/plugin.ts @@ -13,8 +13,16 @@ import { AppPluginStartDependencies, } from './types'; import { defineRoutes } from './routes'; -import { availableUpdatesObject, userPreferencesObject } from './services/saved-object/types'; -import { setCore, setWazuhCore, setInternalSavedObjectsClient } from './plugin-services'; +import { + availableUpdatesObject, + userPreferencesObject, +} from './services/saved-object/types'; +import { + setCore, + setWazuhCore, + setInternalSavedObjectsClient, + setWazuhCheckUpdatesServices, +} from './plugin-services'; import { ISecurityFactory } from '../../wazuh-core/server/services/security-factory'; declare module 'opensearch-dashboards/server' { @@ -27,7 +35,8 @@ declare module 'opensearch-dashboards/server' { } export class WazuhCheckUpdatesPlugin - implements Plugin { + implements Plugin +{ private readonly logger: Logger; constructor(initializerContext: PluginInitializerContext) { @@ -37,10 +46,13 @@ export class WazuhCheckUpdatesPlugin public async setup(core: CoreSetup, plugins: PluginSetup) { this.logger.debug('wazuh_check_updates: Setup'); + setWazuhCore(plugins.wazuhCore); + setWazuhCheckUpdatesServices({ logger: this.logger }); + core.http.registerRouteHandlerContext('wazuh_check_updates', () => { return { logger: this.logger, - security: plugins.wazuhCore.wazuhSecurity, + security: plugins.wazuhCore.dashboardSecurity, }; }); @@ -56,12 +68,16 @@ export class WazuhCheckUpdatesPlugin return {}; } - public start(core: CoreStart, plugins: AppPluginStartDependencies): WazuhCheckUpdatesPluginStart { + public start( + core: CoreStart, + plugins: AppPluginStartDependencies, + ): WazuhCheckUpdatesPluginStart { this.logger.debug('wazuhCheckUpdates: Started'); - const internalSavedObjectsClient = core.savedObjects.createInternalRepository(); + const internalSavedObjectsClient = + core.savedObjects.createInternalRepository(); setCore(core); - setWazuhCore(plugins.wazuhCore); + setInternalSavedObjectsClient(internalSavedObjectsClient); return {}; diff --git a/plugins/wazuh-check-updates/server/services/saved-object/get-saved-object.test.ts b/plugins/wazuh-check-updates/server/services/saved-object/get-saved-object.test.ts index bc12157705..58d771e7f8 100644 --- a/plugins/wazuh-check-updates/server/services/saved-object/get-saved-object.test.ts +++ b/plugins/wazuh-check-updates/server/services/saved-object/get-saved-object.test.ts @@ -1,8 +1,14 @@ -import { getInternalSavedObjectsClient, getWazuhCore } from '../../plugin-services'; +import { + getInternalSavedObjectsClient, + getWazuhCore, + getWazuhCheckUpdatesServices, +} from '../../plugin-services'; import { getSavedObject } from './get-saved-object'; -const mockedGetInternalObjectsClient = getInternalSavedObjectsClient as jest.Mock; -const mockedGetWazuhCore = getWazuhCore as jest.Mock; +const mockedGetInternalObjectsClient = + getInternalSavedObjectsClient as jest.Mock; +const mockedGetWazuhCheckUpdatesServices = + getWazuhCheckUpdatesServices as jest.Mock; jest.mock('../../plugin-services'); describe('getSavedObject function', () => { @@ -24,8 +30,13 @@ describe('getSavedObject function', () => { mockedGetInternalObjectsClient.mockImplementation(() => ({ get: jest.fn().mockRejectedValue({ output: { statusCode: 404 } }), })); - mockedGetWazuhCore.mockImplementation(() => ({ - services: { log: jest.fn().mockImplementation(() => {}) }, + mockedGetWazuhCheckUpdatesServices.mockImplementation(() => ({ + logger: { + debug: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + }, })); const response = await getSavedObject('type'); @@ -37,8 +48,13 @@ describe('getSavedObject function', () => { mockedGetInternalObjectsClient.mockImplementation(() => ({ get: jest.fn().mockRejectedValue(new Error('getSavedObject error')), })); - mockedGetWazuhCore.mockImplementation(() => ({ - services: { log: jest.fn().mockImplementation(() => {}) }, + mockedGetWazuhCheckUpdatesServices.mockImplementation(() => ({ + logger: { + debug: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + }, })); const promise = getSavedObject('type'); diff --git a/plugins/wazuh-check-updates/server/services/saved-object/get-saved-object.ts b/plugins/wazuh-check-updates/server/services/saved-object/get-saved-object.ts index e7184cfd3b..fec5c3a548 100644 --- a/plugins/wazuh-check-updates/server/services/saved-object/get-saved-object.ts +++ b/plugins/wazuh-check-updates/server/services/saved-object/get-saved-object.ts @@ -1,7 +1,13 @@ -import { getInternalSavedObjectsClient, getWazuhCore } from '../../plugin-services'; +import { + getInternalSavedObjectsClient, + getWazuhCheckUpdatesServices, +} from '../../plugin-services'; import { savedObjectType } from '../../../common/types'; -export const getSavedObject = async (type: string, id?: string): Promise => { +export const getSavedObject = async ( + type: string, + id?: string, +): Promise => { try { const client = getInternalSavedObjectsClient(); @@ -20,11 +26,9 @@ export const getSavedObject = async (type: string, id?: string): Promise { @@ -14,11 +20,19 @@ describe('setSavedObject function', () => { mockedGetInternalObjectsClient.mockImplementation(() => ({ create: () => ({ attributes: { hide_update_notifications: true } }), })); + mockedGetWazuhCheckUpdatesServices.mockImplementation(() => ({ + logger: { + debug: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + }, + })); const response = await setSavedObject( 'wazuh-check-updates-user-preferences', { hide_update_notifications: true }, - 'admin' + 'admin', ); expect(response).toEqual({ hide_update_notifications: true }); @@ -28,14 +42,19 @@ describe('setSavedObject function', () => { mockedGetInternalObjectsClient.mockImplementation(() => ({ create: jest.fn().mockRejectedValue(new Error('setSavedObject error')), })); - mockedGetWazuhCore.mockImplementation(() => ({ - services: { log: jest.fn().mockImplementation(() => {}) }, + mockedGetWazuhCheckUpdatesServices.mockImplementation(() => ({ + logger: { + debug: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + }, })); const promise = setSavedObject( 'wazuh-check-updates-user-preferences', { hide_update_notifications: true }, - 'admin' + 'admin', ); await expect(promise).rejects.toThrow('setSavedObject error'); diff --git a/plugins/wazuh-check-updates/server/services/saved-object/set-saved-object.ts b/plugins/wazuh-check-updates/server/services/saved-object/set-saved-object.ts index 64820cd54e..5e45bda413 100644 --- a/plugins/wazuh-check-updates/server/services/saved-object/set-saved-object.ts +++ b/plugins/wazuh-check-updates/server/services/saved-object/set-saved-object.ts @@ -1,10 +1,13 @@ import { savedObjectType } from '../../../common/types'; -import { getInternalSavedObjectsClient, getWazuhCore } from '../../plugin-services'; +import { + getInternalSavedObjectsClient, + getWazuhCheckUpdatesServices, +} from '../../plugin-services'; export const setSavedObject = async ( type: string, value: savedObjectType, - id?: string + id?: string, ): Promise => { try { const client = getInternalSavedObjectsClient(); @@ -24,11 +27,9 @@ export const setSavedObject = async ( ? error : 'Error trying to update saved object'; - const { - services: { log }, - } = getWazuhCore(); + const { logger } = getWazuhCheckUpdatesServices(); - log('wazuh-check-updates:setSavedObject', message); + logger.error(message); return Promise.reject(error); } }; diff --git a/plugins/wazuh-check-updates/server/services/updates/get-updates.test.ts b/plugins/wazuh-check-updates/server/services/updates/get-updates.test.ts index 390e10436b..cc51533f2a 100644 --- a/plugins/wazuh-check-updates/server/services/updates/get-updates.test.ts +++ b/plugins/wazuh-check-updates/server/services/updates/get-updates.test.ts @@ -1,6 +1,9 @@ import { getSavedObject } from '../saved-object/get-saved-object'; import { setSavedObject } from '../saved-object/set-saved-object'; -import { getWazuhCore } from '../../plugin-services'; +import { + getWazuhCheckUpdatesServices, + getWazuhCore, +} from '../../plugin-services'; import { API_UPDATES_STATUS } from '../../../common/types'; import { getUpdates } from './get-updates'; import { SAVED_OBJECT_UPDATES } from '../../../common/constants'; @@ -12,6 +15,8 @@ const mockedSetSavedObject = setSavedObject as jest.Mock; jest.mock('../saved-object/set-saved-object'); const mockedGetWazuhCore = getWazuhCore as jest.Mock; +const mockedGetWazuhCheckUpdatesServices = + getWazuhCheckUpdatesServices as jest.Mock; jest.mock('../../plugin-services'); describe('getUpdates function', () => { @@ -43,6 +48,21 @@ describe('getUpdates function', () => { ], })); + mockedGetWazuhCore.mockImplementation(() => ({ + serverAPIHostEntries: { + getHostsEntries: jest.fn(() => []), + }, + })); + + mockedGetWazuhCheckUpdatesServices.mockImplementation(() => ({ + logger: { + debug: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + }, + })); + const updates = await getUpdates(); expect(getSavedObject).toHaveBeenCalledTimes(1); @@ -73,6 +93,7 @@ describe('getUpdates function', () => { }); test('should return available updates from api', async () => { + mockedSetSavedObject.mockImplementation(() => ({})); mockedGetWazuhCore.mockImplementation(() => ({ controllers: { WazuhHostsCtrl: jest.fn().mockImplementation(() => ({ @@ -81,36 +102,44 @@ describe('getUpdates function', () => { .mockImplementation(() => [{ id: 'api id' }]), })), }, - services: { - wazuhApiClient: { - client: { - asInternalUser: { - request: jest.fn().mockImplementation(() => ({ + api: { + client: { + asInternalUser: { + request: jest.fn().mockImplementation(() => ({ + data: { data: { - data: { - uuid: '7f828fd6-ef68-4656-b363-247b5861b84c', - current_version: '4.3.1', - last_available_patch: { - description: - '## Manager\r\n\r\n### Fixed\r\n\r\n- Fixed a crash when overwrite rules are triggered...', - published_date: '2022-05-18T10:12:43Z', - semver: { - major: 4, - minor: 3, - patch: 8, - }, - tag: 'v4.3.8', - title: 'Wazuh v4.3.8', + uuid: '7f828fd6-ef68-4656-b363-247b5861b84c', + current_version: '4.3.1', + last_available_patch: { + description: + '## Manager\r\n\r\n### Fixed\r\n\r\n- Fixed a crash when overwrite rules are triggered...', + published_date: '2022-05-18T10:12:43Z', + semver: { + major: 4, + minor: 3, + patch: 8, }, + tag: 'v4.3.8', + title: 'Wazuh v4.3.8', }, }, - })), - }, + }, + })), }, }, }, + serverAPIHostEntries: { + getHostsEntries: jest.fn(() => [{ id: 'api id' }]), + }, + })); + mockedGetWazuhCheckUpdatesServices.mockImplementation(() => ({ + logger: { + debug: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + }, })); - mockedSetSavedObject.mockImplementation(() => ({})); const updates = await getUpdates(true); diff --git a/plugins/wazuh-check-updates/server/services/updates/get-updates.ts b/plugins/wazuh-check-updates/server/services/updates/get-updates.ts index 7f1cf39312..59fc819c5d 100644 --- a/plugins/wazuh-check-updates/server/services/updates/get-updates.ts +++ b/plugins/wazuh-check-updates/server/services/updates/get-updates.ts @@ -5,26 +5,30 @@ import { } from '../../../common/types'; import { SAVED_OBJECT_UPDATES } from '../../../common/constants'; import { getSavedObject, setSavedObject } from '../saved-object'; -import { getWazuhCore } from '../../plugin-services'; +import { + getWazuhCheckUpdatesServices, + getWazuhCore, +} from '../../plugin-services'; -export const getUpdates = async (checkAvailableUpdates?: boolean): Promise => { +export const getUpdates = async ( + checkAvailableUpdates?: boolean, +): Promise => { try { if (!checkAvailableUpdates) { - const availableUpdates = (await getSavedObject(SAVED_OBJECT_UPDATES)) as AvailableUpdates; + const availableUpdates = (await getSavedObject( + SAVED_OBJECT_UPDATES, + )) as AvailableUpdates; return availableUpdates; } - const { - controllers: { WazuhHostsCtrl }, - services: { wazuhApiClient }, - } = getWazuhCore(); - const wazuhHostsController = new WazuhHostsCtrl(); + const { serverAPIHostEntries, api: wazuhApiClient } = getWazuhCore(); - const hosts: { id: string }[] = await wazuhHostsController.getHostsEntries(); + const hosts: { id: string }[] = + await serverAPIHostEntries.getHostsEntries(); const apisAvailableUpdates = await Promise.all( - hosts?.map(async (api) => { + hosts?.map(async api => { const data = {}; const method = 'GET'; const path = '/manager/version/check?force_query=true'; @@ -37,7 +41,7 @@ export const getUpdates = async (checkAvailableUpdates?: boolean): Promise { @@ -25,10 +30,22 @@ describe('getUserPreferences function', () => { hide_update_notifications: false, })); + mockedGetWazuhCheckUpdatesServices.mockImplementation(() => ({ + logger: { + debug: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + }, + })); + const response = await getUserPreferences('admin'); expect(getSavedObject).toHaveBeenCalledTimes(1); - expect(getSavedObject).toHaveBeenCalledWith(SAVED_OBJECT_USER_PREFERENCES, 'admin'); + expect(getSavedObject).toHaveBeenCalledWith( + SAVED_OBJECT_USER_PREFERENCES, + 'admin', + ); expect(response).toEqual({ last_dismissed_updates: [ @@ -44,10 +61,6 @@ describe('getUserPreferences function', () => { test('should return an error', async () => { mockedGetSavedObject.mockRejectedValue(new Error('getSavedObject error')); - mockedGetWazuhCore.mockImplementation(() => ({ - services: { log: jest.fn().mockImplementation(() => {}) }, - })); - const promise = getUserPreferences('admin'); expect(getSavedObject).toHaveBeenCalledTimes(1); diff --git a/plugins/wazuh-check-updates/server/services/user-preferences/get-user-preferences.ts b/plugins/wazuh-check-updates/server/services/user-preferences/get-user-preferences.ts index c5b7980985..07562b675c 100644 --- a/plugins/wazuh-check-updates/server/services/user-preferences/get-user-preferences.ts +++ b/plugins/wazuh-check-updates/server/services/user-preferences/get-user-preferences.ts @@ -2,13 +2,15 @@ import _ from 'lodash'; import { SAVED_OBJECT_USER_PREFERENCES } from '../../../common/constants'; import { UserPreferences } from '../../../common/types'; import { getSavedObject } from '../saved-object'; -import { getWazuhCore } from '../../plugin-services'; +import { getWazuhCheckUpdatesServices } from '../../plugin-services'; -export const getUserPreferences = async (username: string): Promise => { +export const getUserPreferences = async ( + username: string, +): Promise => { try { const userPreferences = (await getSavedObject( SAVED_OBJECT_USER_PREFERENCES, - username + username, )) as UserPreferences; const userPreferencesWithoutUsername = _.omit(userPreferences, 'username'); @@ -22,11 +24,9 @@ export const getUserPreferences = async (username: string): Promise { @@ -30,6 +35,14 @@ describe('updateUserPreferences function', () => { })); mockedSetSavedObject.mockImplementation(() => {}); + mockedGetWazuhCheckUpdatesServices.mockImplementation(() => ({ + logger: { + debug: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + }, + })); const response = await updateUserPreferences('admin', { last_dismissed_updates: [ @@ -42,7 +55,10 @@ describe('updateUserPreferences function', () => { }); expect(getSavedObject).toHaveBeenCalledTimes(1); - expect(getSavedObject).toHaveBeenCalledWith(SAVED_OBJECT_USER_PREFERENCES, 'admin'); + expect(getSavedObject).toHaveBeenCalledWith( + SAVED_OBJECT_USER_PREFERENCES, + 'admin', + ); expect(response).toEqual({ last_dismissed_updates: [ @@ -58,10 +74,6 @@ describe('updateUserPreferences function', () => { test('should return an error', async () => { mockedSetSavedObject.mockRejectedValue(new Error('getSavedObject error')); - mockedGetWazuhCore.mockImplementation(() => ({ - services: { log: jest.fn().mockImplementation(() => {}) }, - })); - const promise = updateUserPreferences('admin', { last_dismissed_updates: [ { diff --git a/plugins/wazuh-check-updates/server/services/user-preferences/update-user-preferences.ts b/plugins/wazuh-check-updates/server/services/user-preferences/update-user-preferences.ts index 3c004eccf1..a4c50b9992 100644 --- a/plugins/wazuh-check-updates/server/services/user-preferences/update-user-preferences.ts +++ b/plugins/wazuh-check-updates/server/services/user-preferences/update-user-preferences.ts @@ -1,19 +1,26 @@ import { SAVED_OBJECT_USER_PREFERENCES } from '../../../common/constants'; import { UserPreferences } from '../../../common/types'; -import { getWazuhCore } from '../../plugin-services'; +import { getWazuhCheckUpdatesServices } from '../../plugin-services'; import { getSavedObject, setSavedObject } from '../saved-object'; export const updateUserPreferences = async ( username: string, - preferences: UserPreferences + preferences: UserPreferences, ): Promise => { try { const userPreferences = - ((await getSavedObject(SAVED_OBJECT_USER_PREFERENCES, username)) as UserPreferences) || {}; + ((await getSavedObject( + SAVED_OBJECT_USER_PREFERENCES, + username, + )) as UserPreferences) || {}; const newUserPreferences = { ...userPreferences, ...preferences }; - await setSavedObject(SAVED_OBJECT_USER_PREFERENCES, newUserPreferences, username); + await setSavedObject( + SAVED_OBJECT_USER_PREFERENCES, + newUserPreferences, + username, + ); return newUserPreferences; } catch (error) { @@ -24,11 +31,9 @@ export const updateUserPreferences = async ( ? error : 'Error trying to update user preferences'; - const { - services: { log }, - } = getWazuhCore(); + const { logger } = getWazuhCheckUpdatesServices(); - log('wazuh-check-updates:getUserPreferences', message); + logger.error(message); return Promise.reject(error); } }; diff --git a/plugins/wazuh-check-updates/server/types.ts b/plugins/wazuh-check-updates/server/types.ts index 477d209c12..6a59f36b2b 100644 --- a/plugins/wazuh-check-updates/server/types.ts +++ b/plugins/wazuh-check-updates/server/types.ts @@ -10,7 +10,7 @@ export interface WazuhCheckUpdatesPluginStart {} export type PluginSetup = { securityDashboards?: {}; // TODO: Add OpenSearch Dashboards Security interface - wazuhCore: { wazuhSecurity: ISecurityFactory }; + wazuhCore: { dashboardSecurity: ISecurityFactory }; }; export interface AppPluginStartDependencies { diff --git a/plugins/wazuh-check-updates/yarn.lock b/plugins/wazuh-check-updates/yarn.lock index 79110b0f40..12a43703da 100644 --- a/plugins/wazuh-check-updates/yarn.lock +++ b/plugins/wazuh-check-updates/yarn.lock @@ -2,20 +2,6 @@ # yarn lockfile v1 -"@colors/colors@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" - integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== - -"@dabh/diagnostics@^2.0.2": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.3.tgz#7f7e97ee9a725dffc7808d93668cc984e1dc477a" - integrity sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA== - dependencies: - colorspace "1.1.x" - enabled "2.0.x" - kuler "^2.0.0" - "@testing-library/user-event@^14.5.0": version "14.5.0" resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.5.0.tgz#4036add379525b635a64bce4d727820d4ba516a7" @@ -35,16 +21,6 @@ resolved "https://registry.yarnpkg.com/@types/node-cron/-/node-cron-3.0.8.tgz#c4d774b86bf8250d1e9046e08b17875c21ae64eb" integrity sha512-+z5VrCvLwiJUohbRSgHdyZnHzAaLuD/E2bBANw+NQ1l05Crj8dIxb/kKK+OEqRitV2Wr/LYLuEBenGDsHZVV5Q== -"@types/triple-beam@^1.3.2": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.3.tgz#726ae98a5f6418c8f24f9b0f2a9f81a8664876ae" - integrity sha512-6tOUG+nVHn0cJbVp25JFayS5UE6+xlbcNF9Lo9mU7U0zk3zeUShZied4YEQZjy1JBF043FSkdXw8YkUJuVtB5g== - -async@^3.2.3: - version "3.2.4" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" - integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== - asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -64,47 +40,6 @@ charenc@0.0.2: resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== -color-convert@^1.9.3: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== - -color-name@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -color-string@^1.6.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" - integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== - dependencies: - color-name "^1.0.0" - simple-swizzle "^0.2.2" - -color@^3.1.3: - version "3.2.1" - resolved "https://registry.yarnpkg.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164" - integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA== - dependencies: - color-convert "^1.9.3" - color-string "^1.6.0" - -colorspace@1.1.x: - version "1.1.4" - resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.4.tgz#8d442d1186152f60453bf8070cd66eb364e59243" - integrity sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w== - dependencies: - color "^3.1.3" - text-hex "1.0.x" - combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -122,21 +57,6 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -enabled@2.0.x: - version "2.0.0" - resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" - integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== - -fecha@^4.2.0: - version "4.2.3" - resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" - integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw== - -fn.name@1.x.x: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" - integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== - follow-redirects@^1.15.0: version "1.15.3" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" @@ -151,43 +71,11 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" -inherits@^2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -is-arrayish@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" - integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== - is-buffer@~1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-stream@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" - integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== - -kuler@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" - integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== - -logform@^2.3.2, logform@^2.4.0: - version "2.5.1" - resolved "https://registry.yarnpkg.com/logform/-/logform-2.5.1.tgz#44c77c34becd71b3a42a3970c77929e52c6ed48b" - integrity sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg== - dependencies: - "@colors/colors" "1.5.0" - "@types/triple-beam" "^1.3.2" - fecha "^4.2.0" - ms "^2.1.1" - safe-stable-stringify "^2.3.1" - triple-beam "^1.3.0" - md5@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" @@ -209,11 +97,6 @@ mime-types@^2.1.12: dependencies: mime-db "1.52.0" -ms@^2.1.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - node-cron@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/node-cron/-/node-cron-3.0.2.tgz#bb0681342bd2dfb568f28e464031280e7f06bd01" @@ -221,98 +104,12 @@ node-cron@^3.0.2: dependencies: uuid "8.3.2" -one-time@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/one-time/-/one-time-1.0.0.tgz#e06bc174aed214ed58edede573b433bbf827cb45" - integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g== - dependencies: - fn.name "1.x.x" - proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== -readable-stream@^3.4.0, readable-stream@^3.6.0: - version "3.6.2" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" - integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safe-stable-stringify@^2.3.1: - version "2.4.3" - resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" - integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== - -simple-swizzle@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" - integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== - dependencies: - is-arrayish "^0.3.1" - -stack-trace@0.0.x: - version "0.0.10" - resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" - integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg== - -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -text-hex@1.0.x: - version "1.0.0" - resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" - integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== - -triple-beam@^1.3.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.4.1.tgz#6fde70271dc6e5d73ca0c3b24e2d92afb7441984" - integrity sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg== - -util-deprecate@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== - uuid@8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - -winston-transport@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.5.0.tgz#6e7b0dd04d393171ed5e4e4905db265f7ab384fa" - integrity sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q== - dependencies: - logform "^2.3.2" - readable-stream "^3.6.0" - triple-beam "^1.3.0" - -winston@^3.10.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/winston/-/winston-3.10.0.tgz#d033cb7bd3ced026fed13bf9d92c55b903116803" - integrity sha512-nT6SIDaE9B7ZRO0u3UvdrimG0HkB7dSTAgInQnNR2SOPJ4bvq5q79+pXLftKmP52lJGW15+H5MCK0nM9D3KB/g== - dependencies: - "@colors/colors" "1.5.0" - "@dabh/diagnostics" "^2.0.2" - async "^3.2.3" - is-stream "^2.0.0" - logform "^2.4.0" - one-time "^1.0.0" - readable-stream "^3.4.0" - safe-stable-stringify "^2.3.1" - stack-trace "0.0.x" - triple-beam "^1.3.0" - winston-transport "^4.5.0" diff --git a/plugins/wazuh-core/common/api-user-status-run-as.ts b/plugins/wazuh-core/common/api-user-status-run-as.ts new file mode 100644 index 0000000000..1aae9eef7e --- /dev/null +++ b/plugins/wazuh-core/common/api-user-status-run-as.ts @@ -0,0 +1,23 @@ +/** + * @example + * HOST = set in wazuh.yml config + * USER = set in user interface + * + * ALL_DISABLED + * binary 00 = decimal 0 ---> USER 0 y HOST 0 + * + * USER_NOT_ALLOWED + * binary 01 = decimal 1 ---> USER 0 y HOST 1 + * + * HOST_DISABLED + * binary 10 = decimal 2 ---> USER 1 y HOST 0 + * + * ENABLED + * binary 11 = decimal 3 ---> USER 1 y HOST 1 + */ +export enum API_USER_STATUS_RUN_AS { + ALL_DISABLED = 0, // Wazuh HOST and USER API user configured with run_as=false or undefined + USER_NOT_ALLOWED = 1, // Wazuh HOST API user configured with run_as = TRUE in wazuh.yml but it has not run_as in Wazuh API + HOST_DISABLED = 2, // Wazuh HOST API user configured with run_as=false in wazuh.yml but it has not run_as in Wazuh API + ENABLED = 3, // Wazuh API user configured with run_as=true and allow run_as +} diff --git a/plugins/wazuh-core/common/constants.ts b/plugins/wazuh-core/common/constants.ts index 08b4ac45c2..231a5a3a56 100644 --- a/plugins/wazuh-core/common/constants.ts +++ b/plugins/wazuh-core/common/constants.ts @@ -137,35 +137,6 @@ export const WAZUH_DATA_CONFIG_REGISTRY_PATH = path.join( 'wazuh-registry.json', ); -// Wazuh data path - logs -export const MAX_MB_LOG_FILES = 100; -export const WAZUH_DATA_LOGS_DIRECTORY_PATH = path.join( - WAZUH_DATA_ABSOLUTE_PATH, - 'logs', -); -export const WAZUH_DATA_LOGS_PLAIN_FILENAME = 'wazuhapp-plain.log'; -export const WAZUH_DATA_LOGS_PLAIN_PATH = path.join( - WAZUH_DATA_LOGS_DIRECTORY_PATH, - WAZUH_DATA_LOGS_PLAIN_FILENAME, -); -export const WAZUH_DATA_LOGS_RAW_FILENAME = 'wazuhapp.log'; -export const WAZUH_DATA_LOGS_RAW_PATH = path.join( - WAZUH_DATA_LOGS_DIRECTORY_PATH, - WAZUH_DATA_LOGS_RAW_FILENAME, -); - -// Wazuh data path - UI logs -export const WAZUH_UI_LOGS_PLAIN_FILENAME = 'wazuh-ui-plain.log'; -export const WAZUH_UI_LOGS_RAW_FILENAME = 'wazuh-ui.log'; -export const WAZUH_UI_LOGS_PLAIN_PATH = path.join( - WAZUH_DATA_LOGS_DIRECTORY_PATH, - WAZUH_UI_LOGS_PLAIN_FILENAME, -); -export const WAZUH_UI_LOGS_RAW_PATH = path.join( - WAZUH_DATA_LOGS_DIRECTORY_PATH, - WAZUH_UI_LOGS_RAW_FILENAME, -); - // Wazuh data path - downloads export const WAZUH_DATA_DOWNLOADS_DIRECTORY_PATH = path.join( WAZUH_DATA_ABSOLUTE_PATH, @@ -1503,38 +1474,6 @@ export const PLUGIN_SETTINGS: { [key: string]: TPluginSetting } = { return schema.boolean(); }, }, - 'logs.level': { - title: 'Log level', - description: 'Logging level of the App.', - category: SettingCategory.GENERAL, - type: EpluginSettingType.select, - options: { - select: [ - { - text: 'Info', - value: 'info', - }, - { - text: 'Debug', - value: 'debug', - }, - ], - }, - defaultValue: 'info', - isConfigurableFromFile: true, - isConfigurableFromUI: true, - requiresRestartingPluginPlatform: true, - validate: function (value) { - return SettingsValidator.literal( - this.options.select.map(({ value }) => value), - )(value); - }, - validateBackend: function (schema) { - return schema.oneOf( - this.options.select.map(({ value }) => schema.literal(value)), - ); - }, - }, pattern: { title: 'Index pattern', description: diff --git a/plugins/wazuh-core/docs/README.md b/plugins/wazuh-core/docs/README.md new file mode 100644 index 0000000000..7e51a684fd --- /dev/null +++ b/plugins/wazuh-core/docs/README.md @@ -0,0 +1,20 @@ +# Description + +The plugin creates and provides instances of the core functionalities services to be shared with other plugins. They are exposed +through the plugin lifecycle methods. + +# Backend + +This plugin provides some core services: + +- CacheAPIUserAllowRunAs: caches the status of API host internal user allows the run as option +- ManageHosts: manage the API host entries +- ServerAPIClient: communicates with the Wazuh server APIs +- ServerAPIHostEntries: gets information about the API host entries +- UpdateConfigurationFile: updates the configuration file +- UpdateRegistry: updates the registry file + +## Frontend + +- Utils +- Constants diff --git a/plugins/wazuh-core/package.json b/plugins/wazuh-core/package.json index 82522f6cba..1317a3b47c 100644 --- a/plugins/wazuh-core/package.json +++ b/plugins/wazuh-core/package.json @@ -22,8 +22,7 @@ "json2csv": "^4.1.2", "jwt-decode": "^3.1.2", "md5": "^2.3.0", - "node-cron": "^3.0.2", - "winston": "^3.10.0" + "node-cron": "^3.0.2" }, "devDependencies": { "@testing-library/user-event": "^14.5.0", diff --git a/plugins/wazuh-core/public/plugin.ts b/plugins/wazuh-core/public/plugin.ts index 8b3078adba..e6d9498318 100644 --- a/plugins/wazuh-core/public/plugin.ts +++ b/plugins/wazuh-core/public/plugin.ts @@ -2,12 +2,16 @@ import { CoreSetup, CoreStart, Plugin } from 'opensearch-dashboards/public'; import { WazuhCorePluginSetup, WazuhCorePluginStart } from './types'; import { setCore, setUiSettings } from './plugin-services'; import * as utils from './utils'; +import { API_USER_STATUS_RUN_AS } from '../common/api-user-status-run-as'; export class WazuhCorePlugin implements Plugin { public setup(core: CoreSetup): WazuhCorePluginSetup { - return {}; + return { + utils, + API_USER_STATUS_RUN_AS, + }; } public start(core: CoreStart): WazuhCorePluginStart { @@ -16,6 +20,7 @@ export class WazuhCorePlugin return { utils, + API_USER_STATUS_RUN_AS, }; } diff --git a/plugins/wazuh-core/public/types.ts b/plugins/wazuh-core/public/types.ts index 28803ab6df..62cb106877 100644 --- a/plugins/wazuh-core/public/types.ts +++ b/plugins/wazuh-core/public/types.ts @@ -1,7 +1,13 @@ -export interface WazuhCorePluginSetup {} +import { API_USER_STATUS_RUN_AS } from '../common/api-user-status-run-as'; + +export interface WazuhCorePluginSetup { + utils: { formatUIDate: (date: Date) => string }; + API_USER_STATUS_RUN_AS: API_USER_STATUS_RUN_AS; +} // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface WazuhCorePluginStart { utils: { formatUIDate: (date: Date) => string }; + API_USER_STATUS_RUN_AS: API_USER_STATUS_RUN_AS; } export interface AppPluginStartDependencies {} diff --git a/plugins/wazuh-core/server/controllers/index.ts b/plugins/wazuh-core/server/controllers/index.ts deleted file mode 100644 index 616611ede1..0000000000 --- a/plugins/wazuh-core/server/controllers/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Wazuh app - Module to export all the controllers - * Copyright (C) 2015-2023 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 { WazuhHostsCtrl } from './wazuh-hosts'; diff --git a/plugins/wazuh-core/server/plugin.ts b/plugins/wazuh-core/server/plugin.ts index 5cbb0b0bd6..b165b23519 100644 --- a/plugins/wazuh-core/server/plugin.ts +++ b/plugins/wazuh-core/server/plugin.ts @@ -6,26 +6,97 @@ import { Logger, } from 'opensearch-dashboards/server'; -import { PluginSetup, WazuhCorePluginSetup, WazuhCorePluginStart } from './types'; +import { + PluginSetup, + WazuhCorePluginSetup, + WazuhCorePluginStart, +} from './types'; import { setCore } from './plugin-services'; -import * as controllers from './controllers'; -import * as services from './services'; -import { SecurityObj } from './services/security-factory'; +import { + CacheAPIUserAllowRunAs, + ManageHosts, + createDashboardSecurity, + ServerAPIClient, + ServerAPIHostEntries, + UpdateConfigurationFile, + UpdateRegistry, +} from './services'; -export class WazuhCorePlugin implements Plugin { +export class WazuhCorePlugin + implements Plugin +{ private readonly logger: Logger; + private services: { [key: string]: any }; constructor(initializerContext: PluginInitializerContext) { this.logger = initializerContext.logger.get(); + this.services = {}; } - public async setup(core: CoreSetup, plugins: PluginSetup): Promise { + public async setup( + core: CoreSetup, + plugins: PluginSetup, + ): Promise { this.logger.debug('wazuh_core: Setup'); - const wazuhSecurity = await SecurityObj(plugins); + this.services.dashboardSecurity = createDashboardSecurity(plugins); + + this.services.updateRegistry = new UpdateRegistry( + this.logger.get('update-registry'), + ); + + this.services.manageHosts = new ManageHosts( + this.logger.get('manage-hosts'), + this.services.updateRegistry, + ); + + this.services.serverAPIClient = new ServerAPIClient( + this.logger.get('server-api-client'), + this.services.manageHosts, + this.services.dashboardSecurity, + ); + + this.services.cacheAPIUserAllowRunAs = new CacheAPIUserAllowRunAs( + this.logger.get('api-user-allow-run-as'), + this.services.manageHosts, + this.services.serverAPIClient, + ); + + this.services.serverAPIHostEntries = new ServerAPIHostEntries( + this.logger.get('server-api-host-entries'), + this.services.manageHosts, + this.services.updateRegistry, + this.services.cacheAPIUserAllowRunAs, + ); + + this.services.updateConfigurationFile = new UpdateConfigurationFile( + this.logger.get('update-configuration-file'), + ); + + // Register a property to the context parameter of the endpoint handlers + core.http.registerRouteHandlerContext('wazuh_core', (context, request) => { + return { + ...this.services, + api: { + client: { + asInternalUser: this.services.serverAPIClient.asInternalUser, + asCurrentUser: this.services.serverAPIClient.asScoped( + context, + request, + ), + }, + }, + }; + }); return { - wazuhSecurity, + ...this.services, + api: { + client: { + asInternalUser: this.services.serverAPIClient.asInternalUser, + asScoped: this.services.serverAPIClient.asScoped, + }, + }, }; } @@ -35,8 +106,13 @@ export class WazuhCorePlugin implements Plugin - await ApiInterceptor.authenticate(apiHostID), - request: async (method: string, path: string, data: any, options: APIInterceptorRequestOptionsInternalUser) => - await ApiInterceptor.requestAsInternalUser( - method, - path, - data, - options, - ), - }, - }, - }; \ No newline at end of file diff --git a/plugins/wazuh-core/server/services/api-interceptor.ts b/plugins/wazuh-core/server/services/api-interceptor.ts deleted file mode 100644 index 256eaede05..0000000000 --- a/plugins/wazuh-core/server/services/api-interceptor.ts +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Wazuh app - Interceptor API entries - * Copyright (C) 2015-2022 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 axios, { AxiosResponse } from 'axios'; -import { ManageHosts } from './manage-hosts'; -import https from 'https'; - -const httpsAgent = new https.Agent({ - rejectUnauthorized: false, -}); - -const _axios = axios.create({ httpsAgent }); - -interface APIHost{ - url: string - port: string - username: string - password: string -} - -export interface APIInterceptorRequestOptions{ - apiHostID: string - token: string - forceRefresh?: boolean -} - -export interface APIInterceptorRequestOptionsInternalUser{ - apiHostID: string - forceRefresh?: boolean -} - -const manageHosts = new ManageHosts(); - -// Cache to save the token for the internal user by API host ID -const CacheInternalUserAPIHostToken = new Map(); - -export const authenticate = async (apiHostID: string, authContext?: any): Promise => { - try{ - const api: APIHost = await manageHosts.getHostById(apiHostID); - const optionsRequest = { - method: 'POST', - headers: { - 'content-type': 'application/json', - }, - auth: { - username: api.username, - password: api.password, - }, - url: `${api.url}:${api.port}/security/user/authenticate${!!authContext ? '/run_as' : ''}`, - ...(!!authContext ? { data: authContext } : {}) - }; - - const response: AxiosResponse = await _axios(optionsRequest); - const token: string = (((response || {}).data || {}).data || {}).token; - if (!authContext) { - CacheInternalUserAPIHostToken.set(apiHostID, token); - }; - return token; - }catch(error){ - throw error; - } -}; - -const buildRequestOptions = async (method: string, path: string, data: any, { apiHostID, forceRefresh, token }: APIInterceptorRequestOptions) => { - const api = await manageHosts.getHostById(apiHostID); - const { body, params, headers, ...rest } = data; - return { - method: method, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer ' + token, - ...(headers ? headers : {}) - }, - data: body || rest || {}, - params: params || {}, - url: `${api.url}:${api.port}${path}`, - } -} - -export const requestAsInternalUser = async (method: string, path: string, data: any, options: APIInterceptorRequestOptionsInternalUser) => { - try{ - const token = CacheInternalUserAPIHostToken.has(options.apiHostID) && !options.forceRefresh - ? CacheInternalUserAPIHostToken.get(options.apiHostID) - : await authenticate(options.apiHostID); - return await request(method, path, data, {...options, token}); - }catch(error){ - if (error.response && error.response.status === 401) { - try{ - const token: string = await authenticate(options.apiHostID); - return await request(method, path, data, {...options, token}); - }catch(error){ - throw error; - } - } - throw error; - } -}; - -export const requestAsCurrentUser = async (method: string, path: string, data: any, options: APIInterceptorRequestOptions) => { - return await request(method, path, data, options) -}; - -const request = async (method: string, path: string, data: any, options: any): Promise => { - try{ - const optionsRequest = await buildRequestOptions(method, path, data, options); - const response: AxiosResponse = await _axios(optionsRequest); - return response; - }catch(error){ - throw error; - } -}; diff --git a/plugins/wazuh-core/server/services/base-logger.ts b/plugins/wazuh-core/server/services/base-logger.ts deleted file mode 100644 index 55f95e9f09..0000000000 --- a/plugins/wazuh-core/server/services/base-logger.ts +++ /dev/null @@ -1,245 +0,0 @@ -import winston, { LogEntry } from 'winston'; -import fs from 'fs'; -import path from 'path'; -import { getConfiguration } from './get-configuration'; -import { createDataDirectoryIfNotExists, createLogFileIfNotExists } from './filesystem'; - -import { WAZUH_DATA_LOGS_DIRECTORY_PATH, MAX_MB_LOG_FILES } from '../../common/constants'; - -export interface IUIPlainLoggerSettings { - level: string; - message?: string; - data?: any; -} - -export interface IUILoggerSettings extends IUIPlainLoggerSettings { - date: Date; - location: string; -} - -export class BaseLogger { - allowed: boolean = false; - wazuhLogger: winston.Logger | undefined = undefined; - wazuhPlainLogger: winston.Logger | undefined = undefined; - PLAIN_LOGS_PATH: string = ''; - PLAIN_LOGS_FILE_NAME: string = ''; - RAW_LOGS_PATH: string = ''; - RAW_LOGS_FILE_NAME: string = ''; - - constructor(plainLogsFile: string, rawLogsFile: string) { - this.PLAIN_LOGS_PATH = path.join(WAZUH_DATA_LOGS_DIRECTORY_PATH, plainLogsFile); - this.RAW_LOGS_PATH = path.join(WAZUH_DATA_LOGS_DIRECTORY_PATH, rawLogsFile); - this.PLAIN_LOGS_FILE_NAME = plainLogsFile; - this.RAW_LOGS_FILE_NAME = rawLogsFile; - } - - /** - * Initialize loggers, plain and raw logger - */ - private initLogger = () => { - const configurationFile = getConfiguration(); - const level = - typeof (configurationFile || {})['logs.level'] !== 'undefined' && - ['info', 'debug'].includes(configurationFile['logs.level']) - ? configurationFile['logs.level'] - : 'info'; - - // JSON logger - this.wazuhLogger = winston.createLogger({ - level, - format: winston.format.json(), - transports: [ - new winston.transports.File({ - filename: this.RAW_LOGS_PATH, - }), - ], - }); - - // Prevents from exit on error related to the logger. - this.wazuhLogger.exitOnError = false; - - // Plain text logger - this.wazuhPlainLogger = winston.createLogger({ - level, - format: winston.format.simple(), - transports: [ - new winston.transports.File({ - filename: this.PLAIN_LOGS_PATH, - }), - ], - }); - - // Prevents from exit on error related to the logger. - this.wazuhPlainLogger.exitOnError = false; - }; - - /** - * Checks if wazuh/logs exists. If it doesn't exist, it will be created. - */ - initDirectory = async () => { - try { - createDataDirectoryIfNotExists(); - createDataDirectoryIfNotExists('logs'); - if (typeof this.wazuhLogger === 'undefined' || typeof this.wazuhPlainLogger === 'undefined') { - this.initLogger(); - } - this.allowed = true; - return; - } catch (error) { - this.allowed = false; - return Promise.reject(error); - } - }; - - /** - * Returns given file size in MB, if the file doesn't exist returns 0 - * @param {*} filename Path to the file - */ - getFilesizeInMegaBytes = (filename: string) => { - if (this.allowed) { - if (fs.existsSync(filename)) { - const stats = fs.statSync(filename); - const fileSizeInMegaBytes = stats.size; - - return fileSizeInMegaBytes / 1000000.0; - } - } - return 0; - }; - - /** - * Check if file exist - * @param filename - * @returns boolean - */ - checkFileExist = (filename: string) => { - return fs.existsSync(filename); - }; - - rotateFiles = (file: string, pathFile: string, log?: string) => { - if (this.getFilesizeInMegaBytes(pathFile) >= MAX_MB_LOG_FILES) { - const fileExtension = path.extname(file); - const fileName = path.basename(file, fileExtension); - fs.renameSync( - pathFile, - `${WAZUH_DATA_LOGS_DIRECTORY_PATH}/${fileName}-${new Date().getTime()}${fileExtension}` - ); - if (log) { - fs.writeFileSync(pathFile, log + '\n'); - } - } - }; - - /** - * Checks if the wazuhapp.log file size is greater than 100MB, if so it rotates the file. - */ - private checkFiles = () => { - createLogFileIfNotExists(this.RAW_LOGS_PATH); - createLogFileIfNotExists(this.PLAIN_LOGS_PATH); - if (this.allowed) { - // check raw log file - this.rotateFiles( - this.RAW_LOGS_FILE_NAME, - this.RAW_LOGS_PATH, - JSON.stringify({ - date: new Date(), - level: 'info', - location: 'logger', - message: 'Rotated log file', - }) - ); - // check log file - this.rotateFiles(this.PLAIN_LOGS_FILE_NAME, this.PLAIN_LOGS_PATH); - } - }; - - /** - * Get Current Date - * @returns string - */ - private yyyymmdd = () => { - const now = new Date(); - const y = now.getFullYear(); - const m = now.getMonth() + 1; - const d = now.getDate(); - const seconds = now.getSeconds(); - const minutes = now.getMinutes(); - const hour = now.getHours(); - return `${y}/${m < 10 ? '0' : ''}${m}/${d < 10 ? '0' : ''}${d} ${hour}:${minutes}:${seconds}`; - }; - - /** - * This function filter some known interfaces to avoid log hug objects - * @param data string | object - * @returns the data parsed - */ - private parseData = (data: any) => { - let parsedData = - data instanceof Error - ? { - message: data.message, - stack: data.stack, - } - : data; - - // when error is AxiosError, it extends from Error - if (data.isAxiosError) { - const { config } = data; - parsedData = { - ...parsedData, - config: { - url: config.url, - method: config.method, - data: config.data, - params: config.params, - }, - }; - } - - if (typeof parsedData === 'object') parsedData.toString = () => JSON.stringify(parsedData); - - return parsedData; - }; - - /** - * Main function to add a new log - * @param {*} location File where the log is being thrown - * @param {*} data Message or object to log - * @param {*} level Optional, default is 'error' - */ - async log(location: string, data: any, level?: string) { - const parsedData = this.parseData(data); - return this.initDirectory() - .then(() => { - if (this.allowed) { - this.checkFiles(); - const plainLogData: IUIPlainLoggerSettings = { - level: level || 'error', - message: `${this.yyyymmdd()}: ${location || 'Unknown origin'}: ${ - parsedData.toString() || 'An error occurred' - }`, - }; - - this.wazuhPlainLogger?.log(plainLogData as LogEntry); - - const logData: IUILoggerSettings = { - date: new Date(), - level: level || 'error', - location: location || 'Unknown origin', - data: parsedData || 'An error occurred', - }; - - if (typeof data == 'string') { - logData.message = parsedData; - delete logData.data; - } - - this.wazuhLogger?.log(logData as LogEntry); - } - }) - .catch((error) => { - console.error(`Cannot create the logs directory due to:\n${error.message || error}`); - throw error; - }); - } -} diff --git a/plugins/wazuh-core/server/services/cache-api-user-has-run-as.ts b/plugins/wazuh-core/server/services/cache-api-user-has-run-as.ts index 725ec3771b..7ca8b1d19e 100644 --- a/plugins/wazuh-core/server/services/cache-api-user-has-run-as.ts +++ b/plugins/wazuh-core/server/services/cache-api-user-has-run-as.ts @@ -9,88 +9,90 @@ * * Find more information about this on the LICENSE file. */ -import * as ApiInterceptor from './api-interceptor'; +import { Logger } from 'opensearch-dashboards/server'; import { ManageHosts } from './manage-hosts'; -import { log } from './logger'; -// Private variable to save the cache -const _cache = {}; +import { ServerAPIClient } from './server-api-client'; +import { API_USER_STATUS_RUN_AS } from '../../common/api-user-status-run-as'; -// Export an interface which interacts with the private cache object -export const CacheInMemoryAPIUserAllowRunAs = { +// Object.freeze(API_USER_STATUS_RUN_AS); +/** + * This service caches the status of API host internal user allows the run as option. + */ +export class CacheAPIUserAllowRunAs { + readonly API_USER_STATUS_RUN_AS; + private _cache: any; + constructor( + private logger: Logger, + private manageHosts: ManageHosts, + private serverAPIClient: ServerAPIClient, + ) { + // TODO: create API Client and replace API Interceptor + // Private variable to save the cache + this._cache = {}; + this.API_USER_STATUS_RUN_AS = API_USER_STATUS_RUN_AS; + } // Set an entry with API ID, username and allow_run_as - set: (apiID: string, username: string, allow_run_as : number): void => { - if(!_cache[apiID]){ - _cache[apiID] = {}; // Create a API ID entry if it doesn't exist in cache object - }; - _cache[apiID][username] = allow_run_as; - }, + set(apiID: string, username: string, allow_run_as: number): void { + if (!this._cache[apiID]) { + this._cache[apiID] = {}; // Create a API ID entry if it doesn't exist in cache object + } + this._cache[apiID][username] = allow_run_as; + } // Get the value of an entry with API ID and username from cache - get: (apiID: string, username: string): number => _cache[apiID] && typeof _cache[apiID][username] !== 'undefined' ? _cache[apiID][username] : API_USER_STATUS_RUN_AS.ALL_DISABLED, + get(apiID: string, username: string): number { + return this._cache[apiID] && + typeof this._cache[apiID][username] !== 'undefined' + ? this._cache[apiID][username] + : API_USER_STATUS_RUN_AS.ALL_DISABLED; + } // Check if it exists the API ID and username in the cache - has: (apiID: string, username: string): boolean => _cache[apiID] && typeof _cache[apiID][username] !== 'undefined' ? true : false -}; - -const manageHosts = new ManageHosts(); - -export const APIUserAllowRunAs = { - async check(apiId: string): Promise{ - try{ - const api = await manageHosts.getHostById(apiId); - log('APIUserAllowRunAs:check', `Check if API user ${api.username} (${apiId}) has run_as`, 'debug'); + has(apiID: string, username: string): boolean { + return this._cache[apiID] && + typeof this._cache[apiID][username] !== 'undefined' + ? true + : false; + } + async check(apiId: string): Promise { + try { + const api = await this.manageHosts.getHostById(apiId); + this.logger.debug( + `Check if API user ${api.username} (${apiId}) has run_as`, + ); // Check if api.run_as is false or undefined, then it set to false in cache - if(!api.run_as){ - CacheInMemoryAPIUserAllowRunAs.set(apiId, api.username, API_USER_STATUS_RUN_AS.HOST_DISABLED); - }; + if (!api.run_as) { + this.set(apiId, api.username, API_USER_STATUS_RUN_AS.HOST_DISABLED); + } // Check if the API user is cached and returns it - if(CacheInMemoryAPIUserAllowRunAs.has(apiId, api.username)){ - return CacheInMemoryAPIUserAllowRunAs.get(apiId, api.username); - }; - const response = await ApiInterceptor.requestAsInternalUser( + if (this.has(apiId, api.username)) { + return this.get(apiId, api.username); + } + const response = await this.serverAPIClient.asInternalUser.request( 'get', '/security/users/me', {}, - { apiHostID: apiId } + { apiHostID: apiId }, ); - const statusUserAllowRunAs = response.data.data.affected_items[0].allow_run_as ? API_USER_STATUS_RUN_AS.ENABLED : API_USER_STATUS_RUN_AS.USER_NOT_ALLOWED; + const statusUserAllowRunAs = response.data.data.affected_items[0] + .allow_run_as + ? API_USER_STATUS_RUN_AS.ENABLED + : API_USER_STATUS_RUN_AS.USER_NOT_ALLOWED; // Cache the run_as for the API user - CacheInMemoryAPIUserAllowRunAs.set(apiId, api.username, statusUserAllowRunAs); + this.set(apiId, api.username, statusUserAllowRunAs); return statusUserAllowRunAs; - }catch(error){ - log('APIUserAllowRunAs:check', error.message || error); + } catch (error) { + this.logger.error(error.message || error); return API_USER_STATUS_RUN_AS.ALL_DISABLED; } - }, - async canUse(apiId: string): Promise{ - const ApiUserCanUseStatus = await APIUserAllowRunAs.check(apiId); - if(ApiUserCanUseStatus === API_USER_STATUS_RUN_AS.USER_NOT_ALLOWED){ - const api = await manageHosts.getHostById(apiId); - throw new Error(`API with host ID [${apiId}] misconfigured. The Wazuh API user [${api.username}] is not allowed to use [run_as]. Allow it in the user configuration or set [run_as] host setting with [false] value.`); + } + async canUse(apiId: string): Promise { + const ApiUserCanUseStatus = await this.check(apiId); + if (ApiUserCanUseStatus === API_USER_STATUS_RUN_AS.USER_NOT_ALLOWED) { + const api = await this.manageHosts.getHostById(apiId); + throw new Error( + `API with host ID [${apiId}] misconfigured. The Wazuh API user [${api.username}] is not allowed to use [run_as]. Allow it in the user configuration or set [run_as] host setting with [false] value.`, + ); } return ApiUserCanUseStatus; } -}; - -/** - * @example - * HOST = set in wazuh.yml config - * USER = set in user interface - * - * ALL_DISABLED - * binary 00 = decimal 0 ---> USER 0 y HOST 0 - * - * USER_NOT_ALLOWED - * binary 01 = decimal 1 ---> USER 0 y HOST 1 - * - * HOST_DISABLED - * binary 10 = decimal 2 ---> USER 1 y HOST 0 - * - * ENABLED - * binary 11 = decimal 3 ---> USER 1 y HOST 1 - */ -export enum API_USER_STATUS_RUN_AS{ - ALL_DISABLED = 0, // Wazuh HOST and USER API user configured with run_as=false or undefined - USER_NOT_ALLOWED = 1, // Wazuh HOST API user configured with run_as = TRUE in wazuh.yml but it has not run_as in Wazuh API - HOST_DISABLED = 2, // Wazuh HOST API user configured with run_as=false in wazuh.yml but it has not run_as in Wazuh API - ENABLED = 3 // Wazuh API user configured with run_as=true and allow run_as } diff --git a/plugins/main/server/lib/ui-logger.ts b/plugins/wazuh-core/server/services/cookie.ts similarity index 53% rename from plugins/main/server/lib/ui-logger.ts rename to plugins/wazuh-core/server/services/cookie.ts index d3ca58fd61..3d3beff12a 100644 --- a/plugins/main/server/lib/ui-logger.ts +++ b/plugins/wazuh-core/server/services/cookie.ts @@ -1,5 +1,5 @@ /* - * Wazuh app - Module for ui logging functions + * Wazuh app - Cookie util functions * Copyright (C) 2015-2022 Wazuh, Inc. * * This program is free software; you can redistribute it and/or modify @@ -9,10 +9,13 @@ * * Find more information about this on the LICENSE file. */ -import { BaseLogger } from './base-logger'; -import { - WAZUH_UI_LOGS_PLAIN_FILENAME, - WAZUH_UI_LOGS_RAW_FILENAME -} from '../../common/constants'; -export default new BaseLogger(WAZUH_UI_LOGS_PLAIN_FILENAME,WAZUH_UI_LOGS_RAW_FILENAME); +export const getCookieValueByName = ( + cookie: string, + name: string, +): string | undefined => { + if (!cookie) return; + const cookieRegExp = new RegExp(`.*${name}=([^;]+)`); + const [_, cookieNameValue] = cookie.match(cookieRegExp) || []; + return cookieNameValue; +}; diff --git a/plugins/wazuh-core/server/services/get-configuration.ts b/plugins/wazuh-core/server/services/get-configuration.ts index c24e633486..1ea855c75f 100644 --- a/plugins/wazuh-core/server/services/get-configuration.ts +++ b/plugins/wazuh-core/server/services/get-configuration.ts @@ -1,7 +1,9 @@ import fs from 'fs'; import yml from 'js-yaml'; -import { WAZUH_DATA_CONFIG_APP_PATH, WAZUH_CONFIGURATION_CACHE_TIME } from '../../common/constants'; -import { getSettingsDefault } from '../../common/services/settings'; +import { + WAZUH_DATA_CONFIG_APP_PATH, + WAZUH_CONFIGURATION_CACHE_TIME, +} from '../../common/constants'; let cachedConfiguration: any = null; let lastAssign: number = new Date().getTime(); @@ -15,16 +17,22 @@ export function getConfiguration(options: { force?: boolean } = {}) { try { const now = new Date().getTime(); const dateDiffer = now - lastAssign; - const defaultConfiguration = getSettingsDefault(); - if (!cachedConfiguration || dateDiffer >= WAZUH_CONFIGURATION_CACHE_TIME || options?.force) { + if ( + !cachedConfiguration || + dateDiffer >= WAZUH_CONFIGURATION_CACHE_TIME || + options?.force + ) { cachedConfiguration = obfuscateHostsConfiguration( readPluginConfigurationFile(WAZUH_DATA_CONFIG_APP_PATH), - ['password'] + ['password'], ); lastAssign = now; } - return { ...defaultConfiguration, ...cachedConfiguration }; + /* WARNING: This should only return the configuration defined in the configuration file. + Merging the default settings with the user settings could cause side effects in other services. + */ + return cachedConfiguration; } catch (error) { return false; } @@ -46,23 +54,31 @@ function readPluginConfigurationFile(filepath: string) { * @param obfuscateHostConfigurationKeys Keys to obfuscate its value in the hosts configuration. * @returns */ -function obfuscateHostsConfiguration(configuration: any, obfuscateHostConfigurationKeys: string[]) { +function obfuscateHostsConfiguration( + configuration: any, + obfuscateHostConfigurationKeys: string[], +) { if (configuration.hosts) { - configuration.hosts = configuration.hosts.map((host: { [hostID: string]: any }) => { - const hostID = Object.keys(host)[0]; - return { - [hostID]: { - ...host[hostID], - ...obfuscateHostConfigurationKeys.reduce( - (accumObfuscateHostConfigurationKeys, obfuscateHostConfigurationKey) => ({ - ...accumObfuscateHostConfigurationKeys, - [obfuscateHostConfigurationKey]: '*****', - }), - {} - ), - }, - }; - }); + configuration.hosts = configuration.hosts.map( + (host: { [hostID: string]: any }) => { + const hostID = Object.keys(host)[0]; + return { + [hostID]: { + ...host[hostID], + ...obfuscateHostConfigurationKeys.reduce( + ( + accumObfuscateHostConfigurationKeys, + obfuscateHostConfigurationKey, + ) => ({ + ...accumObfuscateHostConfigurationKeys, + [obfuscateHostConfigurationKey]: '*****', + }), + {}, + ), + }, + }; + }, + ); } return configuration; } diff --git a/plugins/wazuh-core/server/services/index.ts b/plugins/wazuh-core/server/services/index.ts index 3890cc4326..3f956dd192 100644 --- a/plugins/wazuh-core/server/services/index.ts +++ b/plugins/wazuh-core/server/services/index.ts @@ -10,6 +10,13 @@ * Find more information about this on the LICENSE file. */ -export { log } from './logger'; -export { wazuhApiClient } from './api-client'; -export * as securityFactory from './security-factory'; +export * from './cache-api-user-has-run-as'; +export * from './cookie'; +export * from './filesystem'; +export * from './get-configuration'; +export * from './manage-hosts'; +export * from './security-factory'; +export * from './server-api-client'; +export * from './server-api-host-entries'; +export * from './update-registry'; +export * from './update-configuration-file'; diff --git a/plugins/wazuh-core/server/services/logger.ts b/plugins/wazuh-core/server/services/logger.ts deleted file mode 100644 index 1f0ffb0856..0000000000 --- a/plugins/wazuh-core/server/services/logger.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { BaseLogger } from './base-logger'; -import { - WAZUH_DATA_LOGS_PLAIN_FILENAME, - WAZUH_DATA_LOGS_RAW_FILENAME, -} from '../../common/constants'; - -const logger = new BaseLogger(WAZUH_DATA_LOGS_PLAIN_FILENAME, WAZUH_DATA_LOGS_RAW_FILENAME); - -export const log = (location: string, message: string, level?: string) => { - logger.log(location, message, level); -}; diff --git a/plugins/wazuh-core/server/services/manage-hosts.ts b/plugins/wazuh-core/server/services/manage-hosts.ts index c9450eb813..982a8d7192 100644 --- a/plugins/wazuh-core/server/services/manage-hosts.ts +++ b/plugins/wazuh-core/server/services/manage-hosts.ts @@ -11,21 +11,22 @@ */ import fs from 'fs'; import yml from 'js-yaml'; -import { log } from './logger'; import { UpdateRegistry } from './update-registry'; import { initialWazuhConfig } from './initial-wazuh-config'; import { WAZUH_DATA_CONFIG_APP_PATH } from '../../common/constants'; import { createDataDirectoryIfNotExists } from './filesystem'; +import { Logger } from 'opensearch-dashboards/server'; +/** + * This services manages the API host entries + */ export class ManageHosts { busy: boolean; file: string; - updateRegistry: UpdateRegistry; initialConfig: string; - constructor() { + constructor(private logger: Logger, private updateRegistry: UpdateRegistry) { this.busy = false; this.file = WAZUH_DATA_CONFIG_APP_PATH; - this.updateRegistry = new UpdateRegistry(); this.initialConfig = initialWazuhConfig; } @@ -36,14 +37,14 @@ export class ManageHosts { */ composeHost(host, id) { try { - log('manage-hosts:composeHost', 'Composing host', 'debug'); + this.logger.debug('Composing host'); return ` - ${!id ? new Date().getTime() : id}: url: ${host.url} port: ${host.port} username: ${host.username || host.user} password: ${host.password}`; } catch (error) { - log('manage-hosts:composeHost', error.message || error); + this.logger.error(error.message || error); throw error; } } @@ -56,10 +57,10 @@ export class ManageHosts { try { const hostId = Object.keys(host)[0]; const reg = `\\s*-\\s*${hostId}\\s*:\\s*\\n*\\s*url\\s*:\\s*\\S*\\s*\\n*\\s*port\\s*:\\s*\\S*\\s*\\n*\\s*username\\s*:\\s*\\S*\\s*\\n*\\s*password\\s*:\\s*\\S*`; - log('manage-hosts:composeRegex', 'Composing regex', 'debug'); + this.logger.debug('Composing regex'); return new RegExp(`${reg}`, 'gm'); } catch (error) { - log('manage-hosts:composeRegex', error.message || error); + this.logger.error(error.message || error); throw error; } } @@ -82,12 +83,12 @@ export class ManageHosts { const raw = fs.readFileSync(this.file, { encoding: 'utf-8' }); this.busy = false; const content = yml.load(raw); - log('manage-hosts:getHosts', 'Getting hosts', 'debug'); + this.logger.debug('Getting hosts'); const entries = (content || {})['hosts'] || []; return entries; } catch (error) { this.busy = false; - log('manage-hosts:getHosts', error.message || error); + this.logger.error(error.message || error); return Promise.reject(error); } } @@ -97,15 +98,15 @@ export class ManageHosts { */ async checkIfHostsKeyExists() { try { - log('manage-hosts:checkIfHostsKeyExists', 'Checking hosts key', 'debug'); + this.logger.debug('Checking hosts key'); this.busy = true; const raw = fs.readFileSync(this.file, { encoding: 'utf-8' }); this.busy = false; const content = yml.load(raw); return Object.keys(content || {}).includes('hosts'); } catch (error) { - log('manage-hosts:checkIfHostsKeyExists', error.message || error); this.busy = false; + this.logger.error(error.message || error); return Promise.reject(error); } } @@ -119,10 +120,10 @@ export class ManageHosts { const ids = hosts.map(h => { return Object.keys(h)[0]; }); - log('manage-hosts:getCurrentHostsIds', 'Getting hosts ids', 'debug'); + this.logger.debug('Getting hosts ids'); return ids; } catch (error) { - log('manage-hosts:getCurrentHostsIds', error.message || error); + this.logger.error(error.message || error); return Promise.reject(error); } } @@ -133,7 +134,7 @@ export class ManageHosts { */ async getHostById(id) { try { - log('manage-hosts:getHostById', `Getting host ${id}`, 'debug'); + this.logger.debug(`Getting host ${id}`); const hosts = await this.getHosts(); const host = hosts.filter(h => { return Object.keys(h)[0] == id; @@ -145,7 +146,7 @@ export class ManageHosts { const result = Object.assign(host[0][key], { id: key }) || {}; return result; } catch (error) { - log('manage-hosts:getHostById', error.message || error); + this.logger.error(error.message || error); return Promise.reject(error); } } @@ -179,13 +180,9 @@ export class ManageHosts { }; entries.push(api); }); - log( - 'manage-hosts:transformIndexedApis', - 'Transforming index API schedule to wazuh.yml', - 'debug', - ); + this.logger.debug('Transforming index API schedule to wazuh.yml'); } catch (error) { - log('manage-hosts:transformIndexedApis', error.message || error); + this.logger.error(error.message || error); throw error; } return entries; @@ -200,7 +197,7 @@ export class ManageHosts { const apis = this.transformIndexedApis(apiEntries); return await this.addSeveralHosts(apis); } catch (error) { - log('manage-hosts:migrateFromIndex', error.message || error); + this.logger.error(error.message || error); return Promise.reject(error); } } @@ -215,14 +212,10 @@ export class ManageHosts { const cleanHosts = hosts.filter(h => { return !currentHosts.includes(h.id); }); - log( - 'manage-hosts:cleanExistingHosts', - 'Preventing add existings hosts', - 'debug', - ); + this.logger.debug('Preventing add existings hosts'); return cleanHosts; } catch (error) { - log('manage-hosts:cleanExistingHosts', error.message || error); + this.logger.error(error.message || error); return Promise.reject(error); } } @@ -241,7 +234,7 @@ export class ManageHosts { */ async addSeveralHosts(hosts) { try { - log('manage-hosts:addSeveralHosts', 'Adding several', 'debug'); + this.logger.debug('Adding several'); const hostsToAdd = await this.cleanExistingHosts(hosts); if (!hostsToAdd.length) return 'There are not APIs entries to migrate'; for (let idx in hostsToAdd) { @@ -250,7 +243,7 @@ export class ManageHosts { } return 'All APIs entries were migrated to the wazuh.yml'; } catch (error) { - log('manage-hosts:addSeveralHosts', error.message || error); + this.logger.error(error.message || error); return Promise.reject(error); } } @@ -294,11 +287,11 @@ export class ManageHosts { host.cluster_info, host.extensions, ); - log('manage-hosts:addHost', `Host ${id} was properly added`, 'debug'); + this.logger.debug(`Host ${id} was properly added`); return id; } catch (error) { this.busy = false; - log('manage-hosts:addHost', error.message || error); + this.logger.error(error.message || error); return Promise.reject(error); } } @@ -336,15 +329,11 @@ export class ManageHosts { } } this.busy = false; - log( - 'manage-hosts:deleteHost', - `Host ${req.params.id} was properly deleted`, - 'debug', - ); + this.logger.debug(`Host ${req.params.id} was properly deleted`); return true; } catch (error) { this.busy = false; - log('manage-hosts:deleteHost', error.message || error); + this.logger.error(error.message || error); return Promise.reject(error); } } @@ -374,15 +363,11 @@ export class ManageHosts { await fs.writeFileSync(this.file, result, 'utf8'); } this.busy = false; - log( - 'manage-hosts:updateHost', - `Host ${id} was properly updated`, - 'debug', - ); + this.logger.debug(`Host ${id} was properly updated`); return true; } catch (error) { this.busy = false; - log('manage-hosts:updateHost', error.message || error); + this.logger.error(error.message || error); return Promise.reject(error); } } diff --git a/plugins/wazuh-core/server/services/security-factory/factories/opensearch-dashboards-security-factory.ts b/plugins/wazuh-core/server/services/security-factory/factories/opensearch-dashboards-security-factory.ts index ba290fbba3..4c16eda892 100644 --- a/plugins/wazuh-core/server/services/security-factory/factories/opensearch-dashboards-security-factory.ts +++ b/plugins/wazuh-core/server/services/security-factory/factories/opensearch-dashboards-security-factory.ts @@ -1,21 +1,28 @@ import { ISecurityFactory } from '..'; -import { OpenSearchDashboardsRequest, RequestHandlerContext } from 'opensearch-dashboards/server'; +import { + OpenSearchDashboardsRequest, + RequestHandlerContext, +} from 'opensearch-dashboards/server'; import md5 from 'md5'; import { WAZUH_SECURITY_PLUGIN_OPENSEARCH_DASHBOARDS_SECURITY } from '../../../../common/constants'; export class OpenSearchDashboardsSecurityFactory implements ISecurityFactory { platform: string = WAZUH_SECURITY_PLUGIN_OPENSEARCH_DASHBOARDS_SECURITY; - async getCurrentUser(request: OpenSearchDashboardsRequest, context: RequestHandlerContext) { + async getCurrentUser( + request: OpenSearchDashboardsRequest, + context: RequestHandlerContext, + ) { try { const params = { path: `/_opendistro/_security/api/account`, method: 'GET', }; - const { - body: authContext, - } = await context.core.opensearch.client.asCurrentUser.transport.request(params); + const { body: authContext } = + await context.core.opensearch.client.asCurrentUser.transport.request( + params, + ); const username = this.getUserName(authContext); return { username, authContext, hashUsername: md5(username) }; } catch (error) { diff --git a/plugins/wazuh-core/server/services/security-factory/index.ts b/plugins/wazuh-core/server/services/security-factory/index.ts index 629d004a60..1f800fdb7f 100644 --- a/plugins/wazuh-core/server/services/security-factory/index.ts +++ b/plugins/wazuh-core/server/services/security-factory/index.ts @@ -1 +1 @@ -export { ISecurityFactory, SecurityObj} from './security-factory'; \ No newline at end of file +export * from './security-factory'; diff --git a/plugins/wazuh-core/server/services/security-factory/security-factory.ts b/plugins/wazuh-core/server/services/security-factory/security-factory.ts index f3168bb1c4..6d6e1b3f6a 100644 --- a/plugins/wazuh-core/server/services/security-factory/security-factory.ts +++ b/plugins/wazuh-core/server/services/security-factory/security-factory.ts @@ -1,5 +1,11 @@ -import { OpenSearchDashboardsSecurityFactory, DefaultFactory } from './factories'; -import { OpenSearchDashboardsRequest, RequestHandlerContext } from 'src/core/server'; +import { + OpenSearchDashboardsSecurityFactory, + DefaultFactory, +} from './factories'; +import { + OpenSearchDashboardsRequest, + RequestHandlerContext, +} from 'src/core/server'; import { PluginSetup } from '../../types'; type CurrentUser = { @@ -11,10 +17,14 @@ export interface ISecurityFactory { platform?: string; getCurrentUser( request: OpenSearchDashboardsRequest, - context?: RequestHandlerContext + context?: RequestHandlerContext, ): Promise; } -export async function SecurityObj({ securityDashboards }: PluginSetup): Promise { - return !!securityDashboards ? new OpenSearchDashboardsSecurityFactory() : new DefaultFactory(); +export function createDashboardSecurity({ + securityDashboards, +}: PluginSetup): Promise { + return !!securityDashboards + ? new OpenSearchDashboardsSecurityFactory() + : new DefaultFactory(); } diff --git a/plugins/wazuh-core/server/services/server-api-client.ts b/plugins/wazuh-core/server/services/server-api-client.ts new file mode 100644 index 0000000000..4dff19825d --- /dev/null +++ b/plugins/wazuh-core/server/services/server-api-client.ts @@ -0,0 +1,249 @@ +/* + * Wazuh app - Interceptor API entries + * Copyright (C) 2015-2022 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 axios, { AxiosInstance, AxiosResponse } from 'axios'; +import https from 'https'; +import { Logger } from 'opensearch-dashboards/server'; +import { getCookieValueByName } from './cookie'; +import { ManageHosts } from './manage-hosts'; +import { ISecurityFactory } from './security-factory'; + +interface APIHost { + url: string; + port: string; + username: string; + password: string; +} + +type RequestHTTPMethod = 'DELETE' | 'GET' | 'PATCH' | 'POST' | 'PUT'; +type RequestPath = string; + +export interface APIInterceptorRequestOptions { + apiHostID: string; + token: string; + forceRefresh?: boolean; +} + +export interface APIInterceptorRequestOptionsInternalUser { + apiHostID: string; + forceRefresh?: boolean; +} + +export interface APIInterceptorRequestOptionsScopedUser { + apiHostID: string; + forceRefresh?: boolean; + token: string; +} + +export interface ServerAPIInternalUserClient { + authenticate: (apiHostID: string) => Promise; + request: ( + method: RequestHTTPMethod, + path: RequestPath, + data: any, + options: APIInterceptorRequestOptionsInternalUser, + ) => Promise>; +} + +export interface ServerAPIScopedUserClient { + authenticate: (apiHostID: string) => Promise; + request: ( + method: RequestHTTPMethod, + path: RequestPath, + data: any, + options: APIInterceptorRequestOptionsScopedUser, + ) => Promise>; +} + +/** + * This service communicates with the Wazuh server APIs + */ +export class ServerAPIClient { + private _CacheInternalUserAPIHostToken: Map; + private _axios: typeof axios; + private asInternalUser: ServerAPIInternalUserClient; + private _axios: AxiosInstance; + constructor( + private logger: Logger, // TODO: add logger as needed + private manageHosts: ManageHosts, + private dashboardSecurity: ISecurityFactory, + ) { + const httpsAgent = new https.Agent({ + rejectUnauthorized: false, + }); + this._axios = axios.create({ httpsAgent }); + // Cache to save the token for the internal user by API host ID + this._CacheInternalUserAPIHostToken = new Map(); + + // Create internal user client + this.asInternalUser = { + authenticate: async apiHostID => await this._authenticate(apiHostID), + request: async ( + method: RequestHTTPMethod, + path: RequestPath, + data: any, + options, + ) => await this._requestAsInternalUser(method, path, data, options), + }; + } + + /** + * Internal method to execute the request + * @param method HTTP verb + * @param path Server API endpoint + * @param data Request data + * @param options Options. Data about the Server API ID and the token + * @returns + */ + private async _request( + method: RequestHTTPMethod, + path: RequestPath, + data: any, + options: + | APIInterceptorRequestOptionsInternalUser + | APIInterceptorRequestOptionsScopedUser, + ): Promise { + const optionsRequest = await this._buildRequestOptions( + method, + path, + data, + options, + ); + return await this._axios(optionsRequest); + } + + /** + * Build the options for the request + * @param method HTTP verb + * @param path Server API endpoint + * @param data Request data + * @param options Options. Data about the Server API ID and the token + * @returns + */ + private async _buildRequestOptions( + method: RequestHTTPMethod, + path: RequestPath, + data: any, + { apiHostID, token }: APIInterceptorRequestOptions, + ) { + const api = await this.manageHosts.getHostById(apiHostID); + const { body, params, headers, ...rest } = data; + return { + method: method, + headers: { + 'content-type': 'application/json', + Authorization: 'Bearer ' + token, + ...(headers ? headers : {}), + }, + data: body || rest || {}, + params: params || {}, + url: `${api.url}:${api.port}${path}`, + }; + } + + /** + * Get the authentication token + * @param apiHostID Server API ID + * @param authContext Authentication context to get the token + * @returns + */ + private async _authenticate( + apiHostID: string, + authContext?: any, + ): Promise { + const api: APIHost = await this.manageHosts.getHostById(apiHostID); + const optionsRequest = { + method: 'POST', + headers: { + 'content-type': 'application/json', + }, + auth: { + username: api.username, + password: api.password, + }, + url: `${api.url}:${api.port}/security/user/authenticate${ + !!authContext ? '/run_as' : '' + }`, + ...(!!authContext ? { data: authContext } : {}), + }; + + const response: AxiosResponse = await this._axios(optionsRequest); + const token: string = (((response || {}).data || {}).data || {}).token; + if (!authContext) { + this._CacheInternalUserAPIHostToken.set(apiHostID, token); + } + return token; + } + + /** + * Create a client from the context and request + * @param context + * @param request + * @returns + */ + asScoped(context: any, request: any): ServerAPIScopedUserClient { + return { + authenticate: async (apiHostID: string) => + await this._authenticate( + apiHostID, + ( + await this.dashboardSecurity.getCurrentUser(request, context) + ).authContext, + ), + request: async ( + method: RequestHTTPMethod, + path: string, + data: any, + options: APIInterceptorRequestOptionsScopedUser, + ) => { + return await this._request(method, path, data, { + ...options, + token: getCookieValueByName(request.headers.cookie, 'wz-token'), + }); + }, + }; + } + + /** + * Request as internal user + * @param method HTTP verb + * @param path Server API endpoint + * @param data Request data + * @param options Options. Data about the Server API ID and the token + * @returns + */ + private async _requestAsInternalUser( + method: RequestHTTPMethod, + path: RequestPath, + data: any, + options: APIInterceptorRequestOptionsInternalUser, + ) { + try { + const token = + this._CacheInternalUserAPIHostToken.has(options.apiHostID) && + !options.forceRefresh + ? this._CacheInternalUserAPIHostToken.get(options.apiHostID) + : await this._authenticate(options.apiHostID); + return await this._request(method, path, data, { ...options, token }); + } catch (error) { + if (error.response && error.response.status === 401) { + try { + const token: string = await this._authenticate(options.apiHostID); + return await this._request(method, path, data, { ...options, token }); + } catch (error) { + throw error; + } + } + throw error; + } + } +} diff --git a/plugins/wazuh-core/server/controllers/wazuh-hosts.ts b/plugins/wazuh-core/server/services/server-api-host-entries.ts similarity index 81% rename from plugins/wazuh-core/server/controllers/wazuh-hosts.ts rename to plugins/wazuh-core/server/services/server-api-host-entries.ts index 864caa15cd..5d2b6f13bf 100644 --- a/plugins/wazuh-core/server/controllers/wazuh-hosts.ts +++ b/plugins/wazuh-core/server/services/server-api-host-entries.ts @@ -16,18 +16,21 @@ import { PLUGIN_PLATFORM_NAME, WAZUH_DATA_PLUGIN_PLATFORM_BASE_ABSOLUTE_PATH, } from '../../common/constants'; -import { APIUserAllowRunAs } from '../services/cache-api-user-has-run-as'; -import { log } from '../services/logger'; -import { ManageHosts } from '../services/manage-hosts'; -import { UpdateRegistry } from '../services/update-registry'; +import { CacheAPIUserAllowRunAs } from './cache-api-user-has-run-as'; +import { ManageHosts } from './manage-hosts'; +import { UpdateRegistry } from './update-registry'; +import { Logger } from 'opensearch-dashboards/server'; -export class WazuhHostsCtrl { - manageHosts: ManageHosts; - updateRegistry: UpdateRegistry; - constructor() { - this.manageHosts = new ManageHosts(); - this.updateRegistry = new UpdateRegistry(); - } +/** + * This service gets information about the API host entries + */ +export class ServerAPIHostEntries { + constructor( + private logger: Logger, + private manageHosts: ManageHosts, + private updateRegistry: UpdateRegistry, + private cacheAPIUserAllowRunAs: CacheAPIUserAllowRunAs, + ) {} /** * This get all hosts entries in the wazuh.yml and the related info in the wazuh-registry.json @@ -59,7 +62,7 @@ export class WazuhHostsCtrl { throw new Error(`Error getting the hosts entries: The \'${WAZUH_DATA_PLUGIN_PLATFORM_BASE_ABSOLUTE_PATH}\' directory could not exist in your ${PLUGIN_PLATFORM_NAME} installation. If this doesn't exist, create it and give the permissions 'sudo mkdir ${WAZUH_DATA_PLUGIN_PLATFORM_BASE_ABSOLUTE_PATH};sudo chown -R ${PLUGIN_PLATFORM_INSTALLATION_USER}:${PLUGIN_PLATFORM_INSTALLATION_USER_GROUP} ${WAZUH_DATA_PLUGIN_PLATFORM_BASE_ABSOLUTE_PATH}'. After, restart the ${PLUGIN_PLATFORM_NAME} service.`); } - log('wazuh-hosts:getHostsEntries', error.message || error); + this.logger.error(error); throw new Error(error); } } @@ -70,7 +73,7 @@ export class WazuhHostsCtrl { * @param {Object} registry * @param {Boolean} removePassword */ - async joinHostRegistry( + private async joinHostRegistry( hosts: any, registry: any, removePassword: boolean = true, @@ -86,7 +89,7 @@ export class WazuhHostsCtrl { const api = Object.assign(h[id], { id: id }); const host = Object.assign(api, registry[id]); // Add to run_as from API user. Use the cached value or get it doing a request - host.allow_run_as = await APIUserAllowRunAs.check(id); + host.allow_run_as = await this.cacheAPIUserAllowRunAs.check(id); if (removePassword) { delete host.password; delete host.token; diff --git a/plugins/main/server/lib/update-configuration.ts b/plugins/wazuh-core/server/services/update-configuration-file.ts similarity index 69% rename from plugins/main/server/lib/update-configuration.ts rename to plugins/wazuh-core/server/services/update-configuration-file.ts index 0c9adb84d2..2477e9e85e 100644 --- a/plugins/main/server/lib/update-configuration.ts +++ b/plugins/wazuh-core/server/services/update-configuration-file.ts @@ -10,13 +10,16 @@ * Find more information about this on the LICENSE file. */ import fs from 'fs'; -import { log } from './logger'; import { getConfiguration } from './get-configuration'; import { WAZUH_DATA_CONFIG_APP_PATH } from '../../common/constants'; import { formatSettingValueToFile } from '../../common/services/settings'; +import { Logger } from 'opensearch-dashboards/server'; +/** + * This service updates the configuration file + */ export class UpdateConfigurationFile { - constructor() { + constructor(private logger: Logger) { this.busy = false; this.file = WAZUH_DATA_CONFIG_APP_PATH; } @@ -29,17 +32,18 @@ export class UpdateConfigurationFile { */ updateLine(key, value, exists = false) { try { + this.logger.debug(`Updating setting: ${key} with value ${value}`); const data = fs.readFileSync(this.file, { encoding: 'utf-8' }); const re = new RegExp(`^${key}\\s{0,}:\\s{1,}.*`, 'gm'); const formatedValue = formatSettingValueToFile(value); const result = exists ? data.replace(re, `${key}: ${formatedValue}`) : `${data}\n${key}: ${formatedValue}`; + this.logger.info(`updateLine: ${result}`); fs.writeFileSync(this.file, result, 'utf8'); - log('update-configuration:updateLine', 'Updating line', 'debug'); return true; } catch (error) { - log('update-configuration:updateLine', error.message || error); + this.logger.error(error.message || error); throw error; } } @@ -55,24 +59,28 @@ export class UpdateConfigurationFile { } this.busy = true; - const pluginConfiguration = getConfiguration({force: true}) || {}; + const pluginConfiguration = getConfiguration({ force: true }) || {}; - for(const pluginSettingKey in updatedConfiguration){ + for (const pluginSettingKey in updatedConfiguration) { // Store the configuration in the configuration file. const value = updatedConfiguration[pluginSettingKey]; - this.updateLine(pluginSettingKey, value, typeof pluginConfiguration[pluginSettingKey] !== 'undefined'); + this.updateLine( + pluginSettingKey, + value, + /* WARNING: This is trusting the result of getConfiguration service only returns the + settings defined in the configuration file. The updateLine function could be enhanced to + identify if the setting is defined or not itself. + */ + typeof pluginConfiguration[pluginSettingKey] !== 'undefined', + ); // Update the app configuration server-cached setting in memory with the new value. pluginConfiguration[pluginSettingKey] = value; - }; + } this.busy = false; - log( - 'update-configuration:updateConfiguration', - 'Updating configuration', - 'debug' - ); + this.logger.debug('Updating configuration'); } catch (error) { - log('update-configuration:updateConfiguration', error.message || error); + this.logger.error(error.message || error); this.busy = false; throw error; } diff --git a/plugins/wazuh-core/server/services/update-registry.ts b/plugins/wazuh-core/server/services/update-registry.ts index 71e76ca627..df9c5270d5 100644 --- a/plugins/wazuh-core/server/services/update-registry.ts +++ b/plugins/wazuh-core/server/services/update-registry.ts @@ -10,13 +10,16 @@ * Find more information about this on the LICENSE file. */ import fs from 'fs'; -import { log } from './logger'; import { WAZUH_DATA_CONFIG_REGISTRY_PATH } from '../../common/constants'; +import { Logger } from 'opensearch-dashboards/server'; +/** + * This service updates the registry file + */ export class UpdateRegistry { busy: boolean; file: string; - constructor() { + constructor(private logger: Logger) { this.busy = false; this.file = WAZUH_DATA_CONFIG_REGISTRY_PATH; } @@ -26,11 +29,11 @@ export class UpdateRegistry { */ async readContent() { try { - log('update-registry:readContent', 'Reading wazuh-registry.json content', 'debug'); + this.logger.debug('Reading registry file'); const content = await fs.readFileSync(this.file, { encoding: 'utf-8' }); return JSON.parse(content); } catch (error) { - log('update-registry:readContent', error.message || error); + this.logger.error(error.message || error); return Promise.reject(error); } } @@ -40,11 +43,11 @@ export class UpdateRegistry { */ async getHosts() { try { - log('update-registry:getHosts', 'Getting hosts from registry', 'debug'); + this.logger.debug('Getting hosts from registry', 'debug'); const content = await this.readContent(); return content.hosts || {}; } catch (error) { - log('update-registry:getHosts', error.message || error); + this.logger.error(error.message || error); return Promise.reject(error); } } @@ -59,7 +62,7 @@ export class UpdateRegistry { const hosts = await this.getHosts(); return hosts.id || {}; } catch (error) { - log('update-registry:getClusterInfoByAPI', error.message || error); + this.logger.error(error.message || error); return Promise.reject(error); } } @@ -70,7 +73,7 @@ export class UpdateRegistry { */ async writeContent(content) { try { - log('update-registry:writeContent', 'Writting wazuh-registry.json content', 'debug'); + this.logger.debug('Writting wazuh-registry.json content'); if (this.busy) { throw new Error('Another process is updating the registry file'); } @@ -78,7 +81,7 @@ export class UpdateRegistry { await fs.writeFileSync(this.file, JSON.stringify(content)); this.busy = false; } catch (error) { - log('update-registry:writeContent', error.message || error); + this.logger.error(error.message || error); return Promise.reject(error); } } @@ -92,7 +95,7 @@ export class UpdateRegistry { try { return Object.keys(hosts).includes(id); } catch (error) { - log('update-registry:checkHost', error.message || error); + this.logger.error(error.message || error); return Promise.reject(error); } } @@ -106,14 +109,15 @@ export class UpdateRegistry { async migrateToRegistry(id, clusterInfo, clusterExtensions) { try { const content = await this.readContent(); - if (!Object.keys(content).includes('hosts')) Object.assign(content, { hosts: {} }); + if (!Object.keys(content).includes('hosts')) + Object.assign(content, { hosts: {} }); const info = { cluster_info: clusterInfo, extensions: clusterExtensions }; content.hosts[id] = info; await this.writeContent(content); - log('update-registry:migrateToRegistry', `API ${id} was properly migrated`, 'debug'); + this.logger.info(`API ${id} was properly migrated`); return info; } catch (error) { - log('update-registry:migrateToRegistry', error.message || error); + this.logger.error(error.message || error); return Promise.reject(error); } } @@ -130,14 +134,10 @@ export class UpdateRegistry { if (!content.hosts[id]) content.hosts[id] = {}; content.hosts[id].cluster_info = clusterInfo; await this.writeContent(content); - log( - 'update-registry:updateClusterInfo', - `API ${id} information was properly updated`, - 'debug' - ); + this.logger.debug(`API ${id} information was properly updated`); return id; } catch (error) { - log('update-registry:updateClusterInfo', error.message || error); + this.logger.error(error.message || error); return Promise.reject(error); } } @@ -150,16 +150,12 @@ export class UpdateRegistry { async updateAPIExtensions(id, extensions) { try { const content = await this.readContent(); - if(content.hosts[id]) content.hosts[id].extensions = extensions; + if (content.hosts[id]) content.hosts[id].extensions = extensions; await this.writeContent(content); - log( - 'update-registry:updateAPIExtensions', - `API ${id} extensions were properly updated`, - 'debug' - ); + this.logger.info(`API ${id} extensions were properly updated`); return id; } catch (error) { - log('update-registry:updateAPIHostname', error.message || error); + this.logger.error(error.message || error); return Promise.reject(error); } } @@ -170,12 +166,12 @@ export class UpdateRegistry { */ async removeHostEntries(ids) { try { - log('update-registry:removeHostEntry', 'Removing entry', 'debug'); + this.logger.debug('Removing entry'); const content = await this.readContent(); ids.forEach(id => delete content.hosts[id]); await this.writeContent(content); } catch (error) { - log('update-registry:removeHostEntry', error.message || error); + this.logger.error(error.message || error); return Promise.reject(error); } } @@ -186,7 +182,7 @@ export class UpdateRegistry { */ async removeOrphanEntries(hosts) { try { - log('update-registry:removeOrphanEntries', 'Checking orphan registry entries', 'debug'); + this.logger.debug('Checking orphan registry entries'); const entries = await this.getHosts(); const hostsKeys = hosts.map(h => { return h.id; @@ -197,7 +193,7 @@ export class UpdateRegistry { }); await this.removeHostEntries(diff); } catch (error) { - log('update-registry:removeOrphanEntries', error.message || error); + this.logger.error(error.message || error); return Promise.reject(error); } } @@ -212,7 +208,7 @@ export class UpdateRegistry { const hosts = await this.getHosts(); return hosts[id] ? hosts[id].token || null : null; } catch (error) { - log('update-registry:getTokenById', error.message || error); + this.logger.error(error.message || error); return Promise.reject(error); } } @@ -229,10 +225,10 @@ export class UpdateRegistry { if (!content.hosts[id]) content.hosts[id] = {}; content.hosts[id].token = token; await this.writeContent(content); - log('update-registry:updateToken', `API ${id} information was properly updated`, 'debug'); + this.logger.info(`API ${id} information was properly updated`); return id; } catch (error) { - log('update-registry:updateToken', error.message || error); + this.logger.debug(error.message || error); return Promise.reject(error); } } diff --git a/plugins/wazuh-core/server/types.ts b/plugins/wazuh-core/server/types.ts index 8d5cedb7f4..0e74a96ce0 100644 --- a/plugins/wazuh-core/server/types.ts +++ b/plugins/wazuh-core/server/types.ts @@ -1,31 +1,44 @@ -import { AxiosResponse } from 'axios'; -import { APIInterceptorRequestOptionsInternalUser } from './services/api-interceptor'; -import { WazuhHostsCtrl } from './controllers'; -import { ISecurityFactory } from './services/security-factory'; +import { + CacheAPIUserAllowRunAs, + ISecurityFactory, + ManageHosts, + ServerAPIClient, + ServerAPIHostEntries, + ServerAPIInternalUserClient, + ServerAPIScopedUserClient, + UpdateConfigurationFile, + UpdateRegistry, +} from './services'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface WazuhCorePluginSetup { - wazuhSecurity: ISecurityFactory; + dashboardSecurity: ISecurityFactory; + cacheAPIUserAllowRunAs: CacheAPIUserAllowRunAs; + manageHosts: ManageHosts; + serverAPIClient: ServerAPIClient; + serverAPIHostEntries: ServerAPIHostEntries; + updateRegistry: UpdateRegistry; + updateConfigurationFile: UpdateConfigurationFile; + api: { + client: { + asInternalUser: ServerAPIInternalUserClient; + asScoped: (context: any, request: any) => ServerAPIScopedUserClient; + }; + }; } // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface WazuhCorePluginStart { - controllers: { - WazuhHostsCtrl: typeof WazuhHostsCtrl; - }; - services: { - log: (location: string, message: string, level?: string) => void; - wazuhApiClient: { - client: { - asInternalUser: { - authenticate: (apiHostID: string) => Promise; - request: ( - method: string, - path: string, - data: any, - options: APIInterceptorRequestOptionsInternalUser - ) => Promise>; - }; - }; + dashboardSecurity: ISecurityFactory; + cacheAPIUserAllowRunAs: CacheAPIUserAllowRunAs; + manageHosts: ManageHosts; + serverAPIClient: ServerAPIClient; + serverAPIHostEntries: ServerAPIHostEntries; + updateRegistry: UpdateRegistry; + updateConfigurationFile: UpdateConfigurationFile; + api: { + client: { + asInternalUser: ServerAPIInternalUserClient; + asScoped: (context: any, request: any) => ServerAPIScopedUserClient; }; }; } diff --git a/plugins/wazuh-core/yarn.lock b/plugins/wazuh-core/yarn.lock index 10107cf612..1fca6c2895 100644 --- a/plugins/wazuh-core/yarn.lock +++ b/plugins/wazuh-core/yarn.lock @@ -2,20 +2,6 @@ # yarn lockfile v1 -"@colors/colors@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" - integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== - -"@dabh/diagnostics@^2.0.2": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.3.tgz#7f7e97ee9a725dffc7808d93668cc984e1dc477a" - integrity sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA== - dependencies: - colorspace "1.1.x" - enabled "2.0.x" - kuler "^2.0.0" - "@testing-library/user-event@^14.5.0": version "14.5.0" resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.5.0.tgz#4036add379525b635a64bce4d727820d4ba516a7" @@ -30,16 +16,6 @@ resolved "https://registry.yarnpkg.com/@types/md5/-/md5-2.3.2.tgz#529bb3f8a7e9e9f621094eb76a443f585d882528" integrity sha512-v+JFDu96+UYJ3/UWzB0mEglIS//MZXgRaJ4ubUPwOM0gvLc/kcQ3TWNYwENEK7/EcXGQVrW8h/XqednSjBd/Og== -"@types/triple-beam@^1.3.2": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.3.tgz#726ae98a5f6418c8f24f9b0f2a9f81a8664876ae" - integrity sha512-6tOUG+nVHn0cJbVp25JFayS5UE6+xlbcNF9Lo9mU7U0zk3zeUShZied4YEQZjy1JBF043FSkdXw8YkUJuVtB5g== - -async@^3.2.3: - version "3.2.4" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" - integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== - asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -59,47 +35,6 @@ charenc@0.0.2: resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== -color-convert@^1.9.3: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== - -color-name@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -color-string@^1.6.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" - integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== - dependencies: - color-name "^1.0.0" - simple-swizzle "^0.2.2" - -color@^3.1.3: - version "3.2.1" - resolved "https://registry.yarnpkg.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164" - integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA== - dependencies: - color-convert "^1.9.3" - color-string "^1.6.0" - -colorspace@1.1.x: - version "1.1.4" - resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.4.tgz#8d442d1186152f60453bf8070cd66eb364e59243" - integrity sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w== - dependencies: - color "^3.1.3" - text-hex "1.0.x" - combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -122,21 +57,6 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -enabled@2.0.x: - version "2.0.0" - resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" - integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== - -fecha@^4.2.0: - version "4.2.3" - resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" - integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw== - -fn.name@1.x.x: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" - integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== - follow-redirects@^1.15.0: version "1.15.3" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" @@ -151,26 +71,11 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" -inherits@^2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -is-arrayish@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" - integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== - is-buffer@~1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-stream@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" - integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== - json2csv@^4.1.2: version "4.5.4" resolved "https://registry.yarnpkg.com/json2csv/-/json2csv-4.5.4.tgz#2b59c2869a137ec48cd2e243e0180466155f773f" @@ -190,28 +95,11 @@ jwt-decode@^3.1.2: resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59" integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A== -kuler@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" - integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== - lodash.get@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== -logform@^2.3.2, logform@^2.4.0: - version "2.5.1" - resolved "https://registry.yarnpkg.com/logform/-/logform-2.5.1.tgz#44c77c34becd71b3a42a3970c77929e52c6ed48b" - integrity sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg== - dependencies: - "@colors/colors" "1.5.0" - "@types/triple-beam" "^1.3.2" - fecha "^4.2.0" - ms "^2.1.1" - safe-stable-stringify "^2.3.1" - triple-beam "^1.3.0" - md5@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" @@ -233,11 +121,6 @@ mime-types@^2.1.12: dependencies: mime-db "1.52.0" -ms@^2.1.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - node-cron@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/node-cron/-/node-cron-3.0.2.tgz#bb0681342bd2dfb568f28e464031280e7f06bd01" @@ -245,98 +128,12 @@ node-cron@^3.0.2: dependencies: uuid "8.3.2" -one-time@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/one-time/-/one-time-1.0.0.tgz#e06bc174aed214ed58edede573b433bbf827cb45" - integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g== - dependencies: - fn.name "1.x.x" - proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== -readable-stream@^3.4.0, readable-stream@^3.6.0: - version "3.6.2" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" - integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safe-stable-stringify@^2.3.1: - version "2.4.3" - resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" - integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== - -simple-swizzle@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" - integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== - dependencies: - is-arrayish "^0.3.1" - -stack-trace@0.0.x: - version "0.0.10" - resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" - integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg== - -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -text-hex@1.0.x: - version "1.0.0" - resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" - integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== - -triple-beam@^1.3.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.4.1.tgz#6fde70271dc6e5d73ca0c3b24e2d92afb7441984" - integrity sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg== - -util-deprecate@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== - uuid@8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - -winston-transport@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.5.0.tgz#6e7b0dd04d393171ed5e4e4905db265f7ab384fa" - integrity sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q== - dependencies: - logform "^2.3.2" - readable-stream "^3.6.0" - triple-beam "^1.3.0" - -winston@^3.10.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/winston/-/winston-3.10.0.tgz#d033cb7bd3ced026fed13bf9d92c55b903116803" - integrity sha512-nT6SIDaE9B7ZRO0u3UvdrimG0HkB7dSTAgInQnNR2SOPJ4bvq5q79+pXLftKmP52lJGW15+H5MCK0nM9D3KB/g== - dependencies: - "@colors/colors" "1.5.0" - "@dabh/diagnostics" "^2.0.2" - async "^3.2.3" - is-stream "^2.0.0" - logform "^2.4.0" - one-time "^1.0.0" - readable-stream "^3.4.0" - safe-stable-stringify "^2.3.1" - stack-trace "0.0.x" - triple-beam "^1.3.0" - winston-transport "^4.5.0"