diff --git a/CHANGELOG.md b/CHANGELOG.md index 21cd28249e..a7a31bda1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,7 @@ All notable changes to the Wazuh app project will be documented in this file. - Fixed filter by value in document details in safari [#7151](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7151) - Fixed error message to prevent pass no strings to the wazuh logger [#7167](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7167) - Fixed the rendering of the `data.vunerability.reference` in the table and flyout [#7177](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7177) +- Fixed incorrect or empty Wazuh API version displayed after upgrade [#440](https://github.com/wazuh/wazuh-dashboard/issues/440) ### Removed diff --git a/plugins/wazuh-check-updates/server/plugin-services.ts b/plugins/wazuh-check-updates/server/plugin-services.ts index 016c19a7a6..98686bd879 100644 --- a/plugins/wazuh-check-updates/server/plugin-services.ts +++ b/plugins/wazuh-check-updates/server/plugin-services.ts @@ -1,6 +1,7 @@ import { CoreStart, ISavedObjectsRepository, + Logger, } from 'opensearch-dashboards/server'; import { createGetterSetter } from '../../../src/plugins/opensearch_dashboards_utils/common'; import { WazuhCorePluginStart } from '../../wazuh-core/server'; @@ -11,4 +12,4 @@ export const [getCore, setCore] = createGetterSetter('Core'); export const [getWazuhCore, setWazuhCore] = createGetterSetter('WazuhCore'); export const [getWazuhCheckUpdatesServices, setWazuhCheckUpdatesServices] = - createGetterSetter('WazuhCheckUpdatesServices'); + createGetterSetter<{ logger: Logger }>('WazuhCheckUpdatesServices'); 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 3277b9dd63..a44cc16f61 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,22 +1,54 @@ -import { getSavedObject } from '../saved-object/get-saved-object'; -import { setSavedObject } from '../saved-object/set-saved-object'; +import { SAVED_OBJECT_UPDATES } from '../../../common/constants'; +import { API_UPDATES_STATUS } from '../../../common/types'; import { getWazuhCheckUpdatesServices, getWazuhCore, } from '../../plugin-services'; -import { API_UPDATES_STATUS } from '../../../common/types'; +import { getSavedObject } from '../saved-object/get-saved-object'; +import { setSavedObject } from '../saved-object/set-saved-object'; import { getUpdates } from './get-updates'; -import { SAVED_OBJECT_UPDATES } from '../../../common/constants'; -const mockedGetSavedObject = getSavedObject as jest.Mock; +const API_ID = 'api id'; + +const mockGetSavedObject = getSavedObject as jest.Mock; jest.mock('../saved-object/get-saved-object'); -const mockedSetSavedObject = setSavedObject as jest.Mock; +const mockSetSavedObject = setSavedObject as jest.Mock; +mockSetSavedObject.mockImplementation(() => ({})); jest.mock('../saved-object/set-saved-object'); -const mockedGetWazuhCore = getWazuhCore as jest.Mock; +const mockGetWazuhCore = getWazuhCore as jest.Mock; +const mockRequest = jest.fn(); +const mockManageHosts = { + get: jest.fn(() => [{ id: API_ID }]), +}; +const mockGetHostsEntries = jest.fn(() => []); +mockGetWazuhCore.mockImplementation(() => { + return { + api: { + client: { + asInternalUser: { + request: mockRequest, + }, + }, + }, + manageHosts: mockManageHosts, + serverAPIHostEntries: { + getHostsEntries: mockGetHostsEntries, + }, + }; +}); + const mockedGetWazuhCheckUpdatesServices = getWazuhCheckUpdatesServices as jest.Mock; +mockedGetWazuhCheckUpdatesServices.mockImplementation(() => ({ + logger: { + debug: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + }, +})); jest.mock('../../plugin-services'); describe('getUpdates function', () => { @@ -24,115 +56,222 @@ describe('getUpdates function', () => { jest.clearAllMocks(); }); - test('should return available updates from saved object', async () => { - mockedGetSavedObject.mockImplementation(() => ({ - last_check_date: '2023-09-30T14:00:00.000Z', + it('should return available updates from saved object', async () => { + const semver = { + major: 4, + minor: 3, + patch: 1, + }; + const version = `${semver.major}.${semver.minor}.${semver.patch}`; + const 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, + tag: `v${version}`, + title: `Wazuh v${version}`, + }; + const last_check_date = '2023-09-30T14:00:00.000Z'; + const savedObject = { + last_check_date, apis_available_updates: [ { - api_id: 'api id', - current_version: '4.3.1', + api_id: API_ID, + current_version: `v${version}`, status: API_UPDATES_STATUS.UP_TO_DATE, - 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', - }, + last_available_patch, }, ], - })); - - mockedGetWazuhCore.mockImplementation(() => ({ - serverAPIHostEntries: { - getHostsEntries: jest.fn(() => []), - }, - })); - - mockedGetWazuhCheckUpdatesServices.mockImplementation(() => ({ - logger: { - debug: jest.fn(), - info: jest.fn(), - warn: jest.fn(), - error: jest.fn(), - }, - })); + }; + mockGetSavedObject.mockImplementation(() => savedObject); const updates = await getUpdates(); expect(getSavedObject).toHaveBeenCalledTimes(1); expect(getSavedObject).toHaveBeenCalledWith(SAVED_OBJECT_UPDATES); + expect(updates).toEqual(savedObject); + }); + + it('should return available updates from api when both requests succeed', async () => { + const semver = { + major: 4, + minor: 3, + patch: 1, + }; + const version = `${semver.major}.${semver.minor}.${semver.patch}`; + const 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, + tag: `v${version}`, + title: `Wazuh v${version}`, + }; + mockRequest + .mockImplementationOnce(() => ({ + data: { + data: { + api_version: version, + }, + }, + })) + .mockImplementationOnce(() => ({ + data: { + data: { + uuid: '7f828fd6-ef68-4656-b363-247b5861b84c', + current_version: `v${version}`, + update_check: undefined, + last_available_major: undefined, + last_available_minor: undefined, + last_available_patch, + last_check_date: undefined, + }, + }, + })); + + const updates = await getUpdates(true); + expect(updates).toEqual({ - last_check_date: '2023-09-30T14:00:00.000Z', + last_check_date: expect.any(Date), apis_available_updates: [ { - api_id: 'api id', - current_version: '4.3.1', - status: API_UPDATES_STATUS.UP_TO_DATE, - 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', + api_id: API_ID, + current_version: `v${version}`, + status: API_UPDATES_STATUS.AVAILABLE_UPDATES, + update_check: undefined, + last_available_major: undefined, + last_available_minor: undefined, + last_available_patch, + last_check_date: undefined, + }, + ], + }); + }); + + it('should return updates when api version undefined on first request', async () => { + const semver = { + major: 4, + minor: 3, + patch: 1, + }; + const version = `${semver.major}.${semver.minor}.${semver.patch}`; + const 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, + tag: `v${version}`, + title: `Wazuh v${version}`, + }; + mockRequest + .mockImplementationOnce(() => ({ + data: { + data: { + api_version: undefined, + }, + }, + })) + .mockImplementationOnce(() => ({ + data: { + data: { + uuid: '7f828fd6-ef68-4656-b363-247b5861b84c', + current_version: `v${version}`, + update_check: undefined, + last_available_major: undefined, + last_available_minor: undefined, + last_available_patch, + last_check_date: undefined, }, }, + })); + + const updates = await getUpdates(true); + + expect(updates).toEqual({ + last_check_date: expect.any(Date), + apis_available_updates: [ + { + api_id: API_ID, + current_version: `v${version}`, + status: API_UPDATES_STATUS.AVAILABLE_UPDATES, + update_check: undefined, + last_available_major: undefined, + last_available_minor: undefined, + last_available_patch, + last_check_date: undefined, + }, ], }); }); + it('should return updates when api version undefined on first request and second request fails', async () => { + const semver = { + major: 4, + minor: 3, + patch: 1, + }; + const version = `${semver.major}.${semver.minor}.${semver.patch}`; + mockRequest + .mockImplementationOnce(() => ({ + data: { + data: { + api_version: undefined, + }, + }, + })) + .mockImplementationOnce(() => { + throw new Error('Error'); + }); + + const updates = await getUpdates(true); - test('should return available updates from api', async () => { - mockedSetSavedObject.mockImplementation(() => ({})); - mockedGetWazuhCore.mockImplementation(() => ({ - api: { - client: { - asInternalUser: { - request: jest.fn().mockImplementation(() => ({ - 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', - }, - }, - }, - })), + expect(updates).toEqual({ + last_check_date: expect.any(Date), + apis_available_updates: [ + { + api_id: API_ID, + current_version: undefined, + status: API_UPDATES_STATUS.ERROR, + error: { + detail: 'Error', + title: 'Error', }, }, - }, - manageHosts: { - get: jest.fn(() => [{ id: 'api id' }]), - }, - })); - mockedGetWazuhCheckUpdatesServices.mockImplementation(() => ({ - logger: { - debug: jest.fn(), - info: jest.fn(), - warn: jest.fn(), - error: jest.fn(), - }, - })); + ], + }); + }); + it('should return updates when first request fails', async () => { + const semver = { + major: 4, + minor: 3, + patch: 1, + }; + const version = `${semver.major}.${semver.minor}.${semver.patch}`; + const 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, + tag: `v${version}`, + title: `Wazuh v${version}`, + }; + mockRequest + .mockImplementationOnce(() => { + throw new Error('Error'); + }) + .mockImplementationOnce(() => ({ + data: { + data: { + uuid: '7f828fd6-ef68-4656-b363-247b5861b84c', + current_version: `v${version}`, + update_check: undefined, + last_available_major: undefined, + last_available_minor: undefined, + last_available_patch, + last_check_date: undefined, + }, + }, + })); const updates = await getUpdates(true); @@ -140,20 +279,75 @@ describe('getUpdates function', () => { last_check_date: expect.any(Date), apis_available_updates: [ { - api_id: 'api id', - current_version: '4.3.1', + api_id: API_ID, + current_version: `v${version}`, status: API_UPDATES_STATUS.AVAILABLE_UPDATES, - 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', + update_check: undefined, + last_available_major: undefined, + last_available_minor: undefined, + last_available_patch, + last_check_date: undefined, + }, + ], + }); + }); + it('should return updates when second request fails', async () => { + const semver = { + major: 4, + minor: 3, + patch: 1, + }; + const version = `${semver.major}.${semver.minor}.${semver.patch}`; + mockRequest + .mockImplementationOnce(() => ({ + data: { + data: { + api_version: version, + }, + }, + })) + .mockImplementationOnce(() => { + throw new Error('Error'); + }); + + const updates = await getUpdates(true); + + expect(updates).toEqual({ + last_check_date: expect.any(Date), + apis_available_updates: [ + { + api_id: API_ID, + current_version: `v${version}`, + status: API_UPDATES_STATUS.ERROR, + error: { + detail: 'Error', + title: 'Error', + }, + }, + ], + }); + }); + it('should return error when both requests fail', async () => { + mockRequest + .mockImplementationOnce(() => { + throw new Error('Error'); + }) + .mockImplementationOnce(() => { + throw new Error('Error'); + }); + + const updates = await getUpdates(true); + + expect(updates).toEqual({ + last_check_date: expect.any(Date), + apis_available_updates: [ + { + api_id: API_ID, + current_version: undefined, + status: API_UPDATES_STATUS.ERROR, + error: { + detail: 'Error', + title: 'Error', }, }, ], 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 2b8d50df05..b87da612bc 100644 --- a/plugins/wazuh-check-updates/server/services/updates/get-updates.ts +++ b/plugins/wazuh-check-updates/server/services/updates/get-updates.ts @@ -9,11 +9,14 @@ import { getWazuhCheckUpdatesServices, getWazuhCore, } from '../../plugin-services'; +import { IAPIHost } from '../../../../wazuh-core/server/services'; export const getUpdates = async ( queryApi = false, forceQuery = false, ): Promise => { + const { logger } = getWazuhCheckUpdatesServices(); + try { if (!queryApi) { const availableUpdates = (await getSavedObject( @@ -23,9 +26,30 @@ export const getUpdates = async ( return availableUpdates; } + const getStatus = ({ + update_check, + last_available_major, + last_available_minor, + last_available_patch, + }: ResponseApiAvailableUpdates) => { + if (update_check === false) { + return API_UPDATES_STATUS.DISABLED; + } + + if ( + last_available_major?.tag || + last_available_minor?.tag || + last_available_patch?.tag + ) { + return API_UPDATES_STATUS.AVAILABLE_UPDATES; + } + + return API_UPDATES_STATUS.UP_TO_DATE; + }; + const { manageHosts, api: wazuhApiClient } = getWazuhCore(); - const hosts: { id: string }[] = await manageHosts.get(); + const hosts = (await manageHosts.get()) as IAPIHost[]; const apisAvailableUpdates = await Promise.all( hosts?.map(async api => { @@ -36,6 +60,27 @@ export const getUpdates = async ( apiHostID: api.id, forceRefresh: true, }; + let currentVersion: string | undefined = undefined; + let availableUpdates: ResponseApiAvailableUpdates = {}; + + try { + const { + data: { + data: { api_version }, + }, + } = await wazuhApiClient.client.asInternalUser.request( + 'GET', + '/', + {}, + options, + ); + if (api_version !== undefined) { + currentVersion = `v${api_version}`; + } + } catch { + logger.debug('[ERROR]: Cannot get the API version'); + } + try { const response = await wazuhApiClient.client.asInternalUser.request( method, @@ -44,53 +89,43 @@ export const getUpdates = async ( options, ); - const update = response.data.data as ResponseApiAvailableUpdates; + availableUpdates = response.data.data as ResponseApiAvailableUpdates; const { - current_version, update_check, last_available_major, last_available_minor, last_available_patch, last_check_date, - } = update; - - const getStatus = () => { - if (update_check === false) { - return API_UPDATES_STATUS.DISABLED; - } - - if ( - last_available_major?.tag || - last_available_minor?.tag || - last_available_patch?.tag - ) { - return API_UPDATES_STATUS.AVAILABLE_UPDATES; - } - - return API_UPDATES_STATUS.UP_TO_DATE; - }; + } = availableUpdates; + + // If for some reason, the previous request fails + if (currentVersion === undefined) { + currentVersion = availableUpdates.current_version; + } return { - current_version, + current_version: currentVersion, update_check, last_available_major, last_available_minor, last_available_patch, last_check_date: last_check_date || undefined, api_id: api.id, - status: getStatus(), + status: getStatus(availableUpdates), }; - } catch (e: any) { - const error = { - title: e.response?.data?.title, - detail: e.response?.data?.detail ?? e.message, + } catch (error: any) { + logger.debug('[ERROR]: Cannot get the API status'); + const errorResponse = { + title: error.response?.data?.title ?? error.message, + detail: error.response?.data?.detail ?? error.message, }; return { api_id: api.id, status: API_UPDATES_STATUS.ERROR, - error, + current_version: currentVersion, + error: errorResponse, }; } }), @@ -112,8 +147,6 @@ export const getUpdates = async ( ? error : 'Error trying to get available updates'; - const { logger } = getWazuhCheckUpdatesServices(); - logger.error(message); return Promise.reject(error); } diff --git a/plugins/wazuh-core/server/services/manage-hosts.ts b/plugins/wazuh-core/server/services/manage-hosts.ts index 84165d95e2..317096abd4 100644 --- a/plugins/wazuh-core/server/services/manage-hosts.ts +++ b/plugins/wazuh-core/server/services/manage-hosts.ts @@ -15,7 +15,7 @@ import { ServerAPIClient } from './server-api-client'; import { API_USER_STATUS_RUN_AS } from '../../common/api-user-status-run-as'; import { HTTP_STATUS_CODES } from '../../common/constants'; -interface IAPIHost { +export interface IAPIHost { id: string; url: string; username: string;