From c289b6bac148ec2b4b009c5bea96ab78bae5214c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20David=20Guti=C3=A9rrez?= Date: Mon, 30 Sep 2024 15:55:58 +0200 Subject: [PATCH 01/28] feat(state): create state core service - Create state service based on AppState from main plugin - Create state containers - server_host - server_host_cluster_info - data_source_alerts - Add documentation for state service --- plugins/wazuh-core/package.json | 3 +- plugins/wazuh-core/public/plugin.ts | 45 ++++++++- .../public/services/state/README.md | 75 ++++++++++++++ .../state/containers/data-source-alerts.ts | 97 +++++++++++++++++++ .../containers/server-host-cluster-info.ts | 82 ++++++++++++++++ .../services/state/containers/server-host.ts | 67 +++++++++++++ .../public/services/state/hocs/creator.tsx | 20 ++++ .../public/services/state/hooks/creator.ts | 21 ++++ .../wazuh-core/public/services/state/index.ts | 2 + .../wazuh-core/public/services/state/state.ts | 54 +++++++++++ .../wazuh-core/public/services/state/types.ts | 32 ++++++ plugins/wazuh-core/public/services/types.ts | 12 +++ plugins/wazuh-core/public/types.ts | 3 + plugins/wazuh-core/yarn.lock | 62 +++++++++++- 14 files changed, 571 insertions(+), 4 deletions(-) create mode 100644 plugins/wazuh-core/public/services/state/README.md create mode 100644 plugins/wazuh-core/public/services/state/containers/data-source-alerts.ts create mode 100644 plugins/wazuh-core/public/services/state/containers/server-host-cluster-info.ts create mode 100644 plugins/wazuh-core/public/services/state/containers/server-host.ts create mode 100644 plugins/wazuh-core/public/services/state/hocs/creator.tsx create mode 100644 plugins/wazuh-core/public/services/state/hooks/creator.ts create mode 100644 plugins/wazuh-core/public/services/state/index.ts create mode 100644 plugins/wazuh-core/public/services/state/state.ts create mode 100644 plugins/wazuh-core/public/services/state/types.ts create mode 100644 plugins/wazuh-core/public/services/types.ts diff --git a/plugins/wazuh-core/package.json b/plugins/wazuh-core/package.json index 430be8bf75..f70b91a570 100644 --- a/plugins/wazuh-core/package.json +++ b/plugins/wazuh-core/package.json @@ -24,7 +24,8 @@ "json2csv": "^4.1.2", "jwt-decode": "^3.1.2", "md5": "^2.3.0", - "node-cron": "^3.0.2" + "node-cron": "^3.0.2", + "react-cookie": "^4.0.3" }, "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 ef08e41595..8137ff0e5a 100644 --- a/plugins/wazuh-core/public/plugin.ts +++ b/plugins/wazuh-core/public/plugin.ts @@ -11,12 +11,24 @@ import { } from '../common/constants'; import { DashboardSecurity } from './utils/dashboard-security'; import * as hooks from './hooks'; +import { CoreState, State } from './services/state'; +import { ServerHostClusterInfoStateContainer } from './services/state/containers/server-host-cluster-info'; +import { Cookies } from 'react-cookie'; +import { ServerHostStateContainer } from './services/state/containers/server-host'; +import { DataSourceAlertsStateContainer } from './services/state/containers/data-source-alerts'; export class WazuhCorePlugin implements Plugin { + runtime: { [key: string]: any } = { + state: {}, + }; _internal: { [key: string]: any } = {}; - services: { [key: string]: any } = {}; + services: { + [key: string]: any; + dashboardSecurity?: DashboardSecurity; + state?: State; + } = {}; public async setup(core: CoreSetup): Promise { const noop = () => {}; const logger = { @@ -46,12 +58,37 @@ export class WazuhCorePlugin this.services.dashboardSecurity = new DashboardSecurity(logger, core.http); + this.services.state = new CoreState(logger); + + this.runtime.state.setup = this.services.state.setup(); + const cookiesStore = new Cookies(); + this.services.state.register( + 'server_host_cluster_info', + new ServerHostClusterInfoStateContainer(logger, { store: cookiesStore }), + ); + this.services.state.register( + 'server_host', + new ServerHostStateContainer(logger, { store: cookiesStore }), + ); + this.services.state.subscribe('server_host', value => { + // TODO: refresh the auth when the server_host data changed + }); + this.services.state.register( + 'data_source_alerts', + new DataSourceAlertsStateContainer(logger, { store: cookiesStore }), + ); + + console.log(this.services.state.get('server_host')); + await this.services.dashboardSecurity.setup(); return { ...this.services, utils, API_USER_STATUS_RUN_AS, + hooks: { + ...this.runtime.state.setup.hooks, + }, }; } @@ -66,7 +103,11 @@ export class WazuhCorePlugin ...this.services, utils, API_USER_STATUS_RUN_AS, - hooks, + // hooks, + hooks: { + ...hooks, + ...this.runtime.state.setup.hooks, + }, }; } diff --git a/plugins/wazuh-core/public/services/state/README.md b/plugins/wazuh-core/public/services/state/README.md new file mode 100644 index 0000000000..2ca9274d1a --- /dev/null +++ b/plugins/wazuh-core/public/services/state/README.md @@ -0,0 +1,75 @@ +# State + +The `state` service manages the shared state of the Wazuh plugins and it is a HUB of state containers. Features: + +- Extensible +- Register state containers +- Ability to get, set, remove data and subscribe to changes of state containers + +The state containers provides a mechanism to manage a specific state. For example, some data is stored in cookies, others could be managed in-memory, local storage, session storage. + +Others plugins can register new state containers. + +The service creates hooks and HOCs that are exposed through the plugin lifecycle. + +## Usage + +### Register a state container + +```ts +state.register('my_state', new MyStateContainer(logger, deps)); +``` + +### Get data of a state container + +```ts +state.get('my_state'); +``` + +### Set data of a state container + +```ts +state.set('my_state', newMyState); +``` + +### Remove data of a state container + +```ts +state.remove('my_state', newMyState); +``` + +### Subscribe to a state container + +```ts +state.subscribe('my_state', data => { + // Do something with the data +}); +``` + +### Hooks + +#### useStateContainer + +Use the state container. + +```ts +const [value, { set: setValue, remove: removeValue }] = + useStateContainter('my_state_container'); +``` + +### HOCs + +#### withStateContainer + +Use the state container. + +```tsx +const MyComponent = withStateContainer('my_state_container')(props => { + // access to state container value + props['stateContainer:my_state_container'].value; + // set a new value + props['stateContainer:my_state_container'].set(newValue); + // remove the value + props['stateContainer:my_state_container'].remove(); +}); +``` diff --git a/plugins/wazuh-core/public/services/state/containers/data-source-alerts.ts b/plugins/wazuh-core/public/services/state/containers/data-source-alerts.ts new file mode 100644 index 0000000000..497cdb84fe --- /dev/null +++ b/plugins/wazuh-core/public/services/state/containers/data-source-alerts.ts @@ -0,0 +1,97 @@ +import { ILogger } from '../../../../common/services/configuration'; +import { StateContainer } from '../types'; +import { BehaviorSubject } from 'rxjs'; + +export class DataSourceAlertsStateContainer implements StateContainer { + private store: any; + private storeKey: string = 'currentPattern'; + updater$: BehaviorSubject; + constructor(private logger: ILogger, { store }) { + this.store = store; + this.updater$ = new BehaviorSubject(this.get()); + } + get() { + try { + this.logger.debug('Getting data'); + const rawData = this.store.get(this.storeKey); + + let result = ''; + if (rawData) { + this.logger.debug('Getting decoded data'); + let decodedData = decodeURI(rawData); + this.logger.debug(`Decoded data: ${decodedData}`); + + /* I assume in previous versions, the selected index pattern could be wrapped with " characters. + This could probably removed if we confirm it is unused anymore. I will leave for historical reasons. + */ + if ( + decodedData && + decodedData[0] === '"' && + decodedData[decodedData.length - 1] === '"' + ) { + const newPattern = decodedData.substring(1, decodedData.length - 1); + this.set(newPattern); + decodedData = decodeURI(this.store.get('currentPattern')); + } + + result = decodedData; + } else { + this.logger.debug('No raw data'); + result = ''; + } + this.logger.debug(`Data: ${result}`); + return result; + } catch (error) { + this.logger.error(error.message); + throw error; + } + } + set(data) { + try { + this.logger.debug(`Setting data: ${data}`); + const encodedData = encodeURI(data); + this.logger.debug(`Setting encoded data: ${encodedData}`); + const exp = new Date(); + exp.setDate(exp.getDate() + 365); + if (data) { + this.store.set(this.storeKey, encodedData, { + expires: exp, + }); + this.updater$.next(encodedData); + this.logger.debug(`Encoded data was set: ${encodedData}`); + } + } catch (error) { + this.logger.error(error.message); + // TODO: implement + // const options = { + // context: `${AppState.name}.setClusterInfo`, + // level: UI_LOGGER_LEVELS.ERROR, + // severity: UI_ERROR_SEVERITIES.UI, + // error: { + // error: error, + // message: error.message || error, + // title: `${error.name}: Error set cluster info`, + // }, + // }; + // getErrorOrchestrator().handleError(options); + throw error; + } + } + remove() { + try { + this.logger.debug('Removing'); + const result = this.store.remove(this.storeKey); + this.logger.debug('Removed'); + return result; + } catch (error) { + this.logger.error(error.message); + throw error; + } + } + subscribe(callback) { + this.logger.debug('Subscribing'); + const subscription = this.updater$.subscribe(callback); + this.logger.debug('Subscribed'); + return subscription; + } +} diff --git a/plugins/wazuh-core/public/services/state/containers/server-host-cluster-info.ts b/plugins/wazuh-core/public/services/state/containers/server-host-cluster-info.ts new file mode 100644 index 0000000000..923303049e --- /dev/null +++ b/plugins/wazuh-core/public/services/state/containers/server-host-cluster-info.ts @@ -0,0 +1,82 @@ +import { ILogger } from '../../../../common/services/configuration'; +import { StateContainer } from '../types'; +import { BehaviorSubject } from 'rxjs'; + +export class ServerHostClusterInfoStateContainer implements StateContainer { + private store: any; + private storeKey: string = 'clusterInfo'; + updater$: BehaviorSubject; + constructor(private logger: ILogger, { store }) { + this.store = store; + this.updater$ = new BehaviorSubject(this.get()); + } + get() { + try { + this.logger.debug('Getting data'); + const rawData = this.store.get(this.storeKey); + let result = {}; + if (rawData) { + this.logger.debug('Getting decoded data'); + const decodedData = decodeURI(this.store.get(this.storeKey)); + this.logger.debug(`Decoded data: ${decodedData}`); + result = JSON.parse(decodedData); + } else { + this.logger.debug('No raw data'); + result = {}; + } + this.logger.debug(`Data: ${result}`); + return result; + } catch (error) { + // TODO: implement + // const options = { + // context: `${AppState.name}.getClusterInfo`, + // level: UI_LOGGER_LEVELS.ERROR, + // severity: UI_ERROR_SEVERITIES.UI, + // error: { + // error: error, + // message: error.message || error, + // title: `${error.name}: Error get cluster info`, + // }, + // }; + // getErrorOrchestrator().handleError(options); + throw error; + } + } + set(data) { + try { + this.logger.debug(`Setting data: ${data}`); + const encodedData = encodeURI(JSON.stringify(data)); + this.logger.debug(`Setting encoded data: ${encodedData}`); + const exp = new Date(); + exp.setDate(exp.getDate() + 365); + if (data) { + this.store.set(this.storeKey, encodedData, { + expires: exp, + }); + this.updater$.next(encodedData); + this.logger.debug(`Encoded data was set: ${encodedData}`); + } + } catch (error) { + // TODO: implement + // const options = { + // context: `${AppState.name}.setClusterInfo`, + // level: UI_LOGGER_LEVELS.ERROR, + // severity: UI_ERROR_SEVERITIES.UI, + // error: { + // error: error, + // message: error.message || error, + // title: `${error.name}: Error set cluster info`, + // }, + // }; + // getErrorOrchestrator().handleError(options); + throw error; + } + } + remove() {} + subscribe(callback) { + this.logger.debug('Subscribing'); + const subscription = this.updater$.subscribe(callback); + this.logger.debug('Subscribed'); + return subscription; + } +} diff --git a/plugins/wazuh-core/public/services/state/containers/server-host.ts b/plugins/wazuh-core/public/services/state/containers/server-host.ts new file mode 100644 index 0000000000..b20ccabe84 --- /dev/null +++ b/plugins/wazuh-core/public/services/state/containers/server-host.ts @@ -0,0 +1,67 @@ +import { ILogger } from '../../../../common/services/configuration'; +import { StateContainer } from '../types'; +import { BehaviorSubject } from 'rxjs'; + +export class ServerHostStateContainer implements StateContainer { + private store: any; + private storeKey: string = 'currentApi'; + updater$: BehaviorSubject; + constructor(private logger: ILogger, { store }) { + this.store = store; + this.updater$ = new BehaviorSubject(this.get()); + } + get() { + this.logger.debug('Getting data'); + const currentAPI = this.store.get(this.storeKey); + this.logger.debug(`Raw data: ${currentAPI}`); + if (currentAPI) { + this.logger.debug('Decoding data'); + const decodedData = decodeURI(currentAPI); + this.logger.debug(`Decoded data: ${decodedData}`); + return decodedData; + } + return false; + } + set(data) { + try { + this.logger.debug(`Setting data: ${data}`); + const encodedData = encodeURI(data); + this.logger.debug(`Setting encoded data: ${encodedData}`); + const exp = new Date(); + exp.setDate(exp.getDate() + 365); + if (data) { + this.store.set(this.storeKey, encodedData, { + expires: exp, + }); + this.updater$.next(encodedData); + this.logger.debug(`Encoded data was set: ${encodedData}`); + } + } catch (error) { + // TODO: implement + // const options = { + // context: `${AppState.name}.setCurrentAPI`, + // level: UI_LOGGER_LEVELS.ERROR, + // severity: UI_ERROR_SEVERITIES.UI, + // error: { + // error: error, + // message: error.message || error, + // title: `${error.name}: Error set current API`, + // }, + // }; + // getErrorOrchestrator().handleError(options); + throw error; + } + } + remove() { + this.logger.debug('Removing'); + const result = this.store.remove(this.storeKey); + this.logger.debug('Removed'); + return result; + } + subscribe(callback) { + this.logger.debug('Subscribing'); + const subscription = this.updater$.subscribe(callback); + this.logger.debug('Subscribed'); + return subscription; + } +} diff --git a/plugins/wazuh-core/public/services/state/hocs/creator.tsx b/plugins/wazuh-core/public/services/state/hocs/creator.tsx new file mode 100644 index 0000000000..ead4a69f07 --- /dev/null +++ b/plugins/wazuh-core/public/services/state/hocs/creator.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +export const createHOCs = ({ useStateContainer }) => { + return { + withStateContainer: (name: string) => WrappedComponent => props => { + const [state, setState, removeValue] = useStateContainer(name); + return ( + + ); + }, + }; +}; diff --git a/plugins/wazuh-core/public/services/state/hooks/creator.ts b/plugins/wazuh-core/public/services/state/hooks/creator.ts new file mode 100644 index 0000000000..9902c6c1a1 --- /dev/null +++ b/plugins/wazuh-core/public/services/state/hooks/creator.ts @@ -0,0 +1,21 @@ +import useObservable from 'react-use/lib/useObservable'; +import { State } from '../types'; + +export const createHooks = ({ state }: { state: State }) => { + function useStateContainer(name: string) { + const value: T = useObservable( + state.getStateContainer(name).updater$, + state.get(name), + ); + function setValue(value) { + state.set(name, value); + } + function removeValue() { + state.remove(name, value); + } + return [value, { set: setValue, remove: removeValue }]; + } + return { + useStateContainer, + }; +}; diff --git a/plugins/wazuh-core/public/services/state/index.ts b/plugins/wazuh-core/public/services/state/index.ts new file mode 100644 index 0000000000..8a4e7cbf4f --- /dev/null +++ b/plugins/wazuh-core/public/services/state/index.ts @@ -0,0 +1,2 @@ +export * from './types'; +export { CoreState } from './state'; diff --git a/plugins/wazuh-core/public/services/state/state.ts b/plugins/wazuh-core/public/services/state/state.ts new file mode 100644 index 0000000000..d98156e269 --- /dev/null +++ b/plugins/wazuh-core/public/services/state/state.ts @@ -0,0 +1,54 @@ +import { ILogger } from '../../../common/services/configuration'; +import { createHOCs } from './hocs/creator'; +import { createHooks } from './hooks/creator'; +import { State, StateContainer } from './types'; + +export class CoreState implements State { + private stateContainers: Map; + constructor(private logger: ILogger) { + this.stateContainers = new Map(); + } + setup() { + this.logger.debug('Setup'); + const hooks = createHooks({ state: this }); + const hocs = createHOCs(hooks); + return { + hooks, + hocs, + }; + } + start() { + this.logger.debug('Start'); + } + stop() { + this.logger.debug('Stop'); + } + getStateContainer(name: string) { + if (!this.stateContainers.has(name)) { + const error = new Error( + `State container [${name}] does not exist. Did you forget to register it?`, + ); + this.logger.error(error.message); + throw error; + } + return this.stateContainers.get(name); + } + register(name: string, value: StateContainer) { + this.stateContainers.set(name, value); + } + get(name: string) { + return this.getStateContainer(name)!.get(); + } + set(name: string, value: any) { + return this.getStateContainer(name)!.set(value); + } + remove(name: string) { + return this.getStateContainer(name)!.remove(value); + } + subscribe(name: string, callback: (value) => void) { + const stateContainerSubscription = + this.getStateContainer(name)!.subscribe(callback); + // TODO: unsubscribe when the app is stopped + return stateContainerSubscription; + } +} diff --git a/plugins/wazuh-core/public/services/state/types.ts b/plugins/wazuh-core/public/services/state/types.ts new file mode 100644 index 0000000000..d70dbf37f8 --- /dev/null +++ b/plugins/wazuh-core/public/services/state/types.ts @@ -0,0 +1,32 @@ +import { LifecycleService } from '../types'; +import { Subscription, BehaviourSubject } from 'rxjs'; + +export interface StateContainer { + get: () => T; + set: (value: T) => T; + remove: () => any; + updater$: BehaviourSubject; + subscribe: (callback: (value: T) => void) => Subscription; +} + +export interface State< + SetupDeps, + SetupReturn, + StartDeps, + StartReturn, + StopDeps, + StopReturn, +> extends LifecycleService< + SetupDeps, + SetupReturn, + StartDeps, + StartReturn, + StopDeps, + StopReturn + > { + get: (name: string) => any; + set: (name: string, value: any) => any; + remove: (name: string) => any; + register: (name: string, value: StateContainer) => any; + subscribe(name: string, callback: StateContainer['subscribe']): () => {}; +} diff --git a/plugins/wazuh-core/public/services/types.ts b/plugins/wazuh-core/public/services/types.ts new file mode 100644 index 0000000000..bb6c43c39f --- /dev/null +++ b/plugins/wazuh-core/public/services/types.ts @@ -0,0 +1,12 @@ +export interface LifecycleService< + SetupDeps, + SetupReturn, + StartDeps, + StartReturn, + StopDeps, + StopReturn, +> { + setup: (deps: SetupDeps) => SetupReturn; + start: (deps: StartDeps) => StartReturn; + stop: (deps: StopDeps) => StopReturn; +} diff --git a/plugins/wazuh-core/public/types.ts b/plugins/wazuh-core/public/types.ts index a3acfa7c4d..75956fc3fa 100644 --- a/plugins/wazuh-core/public/types.ts +++ b/plugins/wazuh-core/public/types.ts @@ -1,5 +1,6 @@ import { API_USER_STATUS_RUN_AS } from '../common/api-user-status-run-as'; import { Configuration } from '../common/services/configuration'; +import { State } from './services/state'; import { DashboardSecurity } from './utils/dashboard-security'; export interface WazuhCorePluginSetup { @@ -7,6 +8,7 @@ export interface WazuhCorePluginSetup { API_USER_STATUS_RUN_AS: API_USER_STATUS_RUN_AS; configuration: Configuration; dashboardSecurity: DashboardSecurity; + state: State; } // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface WazuhCorePluginStart { @@ -15,6 +17,7 @@ export interface WazuhCorePluginStart { API_USER_STATUS_RUN_AS: API_USER_STATUS_RUN_AS; configuration: Configuration; dashboardSecurity: DashboardSecurity; + state: State; } export interface AppPluginStartDependencies {} diff --git a/plugins/wazuh-core/yarn.lock b/plugins/wazuh-core/yarn.lock index d85475c912..8b5f16ab52 100644 --- a/plugins/wazuh-core/yarn.lock +++ b/plugins/wazuh-core/yarn.lock @@ -141,6 +141,19 @@ version "0.0.0-semantically-released" resolved "https://codeload.github.com/testing-library/user-event/tar.gz/4be87b3452f524bcc256d43cfb891ba1f0e236d6" +"@types/cookie@^0.3.3": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.3.3.tgz#85bc74ba782fb7aa3a514d11767832b0e3bc6803" + integrity sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow== + +"@types/hoist-non-react-statics@^3.0.1": + version "3.3.5" + resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz#dab7867ef789d87e2b4b0003c9d65c49cc44a494" + integrity sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg== + dependencies: + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + "@types/json-schema@^7.0.12": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" @@ -156,6 +169,19 @@ 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/prop-types@*": + version "15.7.13" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.13.tgz#2af91918ee12d9d32914feb13f5326658461b451" + integrity sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA== + +"@types/react@*": + version "18.3.10" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.10.tgz#6edc26dc22ff8c9c226d3c7bf8357b013c842219" + integrity sha512-02sAAlBnP39JgXwkAq3PeU9DVaaGpZyF3MGcC0MKgQVkZor5IiiDAipVaxQHtDJAmO4GIy/rVBy/LzVj76Cyqg== + dependencies: + "@types/prop-types" "*" + csstype "^3.0.2" + "@types/semver@^7.5.0": version "7.5.8" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" @@ -546,6 +572,11 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +cookie@^0.4.0: + version "0.4.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== + cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -560,6 +591,11 @@ crypt@0.0.2: resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow== +csstype@^3.0.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" + integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== + data-view-buffer@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2" @@ -1277,6 +1313,13 @@ hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: dependencies: function-bind "^1.1.2" +hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + identity-function@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/identity-function/-/identity-function-1.0.0.tgz#bea1159f0985239be3ca348edf40ce2f0dd2c21d" @@ -1997,7 +2040,16 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -react-is@^16.13.1: +react-cookie@^4.0.3: + version "4.1.1" + resolved "https://registry.yarnpkg.com/react-cookie/-/react-cookie-4.1.1.tgz#832e134ad720e0de3e03deaceaab179c4061a19d" + integrity sha512-ffn7Y7G4bXiFbnE+dKhHhbP+b8I34mH9jqnm8Llhj89zF4nPxPutxHT1suUqMeCEhLDBI7InYwf1tpaSoK5w8A== + dependencies: + "@types/hoist-non-react-statics" "^3.0.1" + hoist-non-react-statics "^3.0.0" + universal-cookie "^4.0.0" + +react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -2411,6 +2463,14 @@ unescape-js@^1.0.5: dependencies: string.fromcodepoint "^0.2.1" +universal-cookie@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-4.0.4.tgz#06e8b3625bf9af049569ef97109b4bb226ad798d" + integrity sha512-lbRVHoOMtItjWbM7TwDLdl8wug7izB0tq3/YVKhT/ahB4VDvWMyvnADfnJI8y6fSvsjh51Ix7lTGC6Tn4rMPhw== + dependencies: + "@types/cookie" "^0.3.3" + cookie "^0.4.0" + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" From 43286094e9c5f915a1d69c0987751eaa0ae6d392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20David=20Guti=C3=A9rrez?= Date: Mon, 30 Sep 2024 16:14:46 +0200 Subject: [PATCH 02/28] chore: remove unused methods and no-effect functions - Remove unused methods: - AppState.setCreatedAt - AppState.getCreatedAt - AppState.getAPISelector - AppState.getPatternSelector - AppState.setPatternSelector (no-effect) - Remove usage of AppState.setPatternSelector because this has no effect in the current application --- .../container/health-check.container.test.tsx | 4 +- .../container/health-check.container.tsx | 1 - .../public/components/settings/settings.tsx | 2 - .../main/public/react-services/app-state.js | 80 ------------------- .../public/react-services/wz-api-check.js | 52 ++++++------ 5 files changed, 30 insertions(+), 109 deletions(-) diff --git a/plugins/main/public/components/health-check/container/health-check.container.test.tsx b/plugins/main/public/components/health-check/container/health-check.container.test.tsx index 8ef5f8458c..242bd8be24 100644 --- a/plugins/main/public/components/health-check/container/health-check.container.test.tsx +++ b/plugins/main/public/components/health-check/container/health-check.container.test.tsx @@ -68,9 +68,7 @@ jest.mock('../components/check-result', () => ({ })); jest.mock('../../../react-services', () => ({ - AppState: { - setPatternSelector: () => {}, - }, + AppState: {}, ErrorHandler: { handle: error => error, }, diff --git a/plugins/main/public/components/health-check/container/health-check.container.tsx b/plugins/main/public/components/health-check/container/health-check.container.tsx index 009d03139f..a05ca42427 100644 --- a/plugins/main/public/components/health-check/container/health-check.container.tsx +++ b/plugins/main/public/components/health-check/container/health-check.container.tsx @@ -131,7 +131,6 @@ function HealthCheckComponent() { useEffect(() => { if (appConfig.isReady && !checksInitiated.current) { checksInitiated.current = true; - AppState.setPatternSelector(appConfig.data['ip.selector']); } }, [appConfig]); diff --git a/plugins/main/public/components/settings/settings.tsx b/plugins/main/public/components/settings/settings.tsx index 418de2ed08..f869502fb3 100644 --- a/plugins/main/public/components/settings/settings.tsx +++ b/plugins/main/public/components/settings/settings.tsx @@ -299,8 +299,6 @@ class SettingsComponent extends React.Component { }; this.setState({ load: false }); - // TODO: this seems not to be used to display or not the index pattern selector - AppState.setPatternSelector(this.props.configurationIPSelector); const pattern = AppState.getCurrentPattern(); this.getCurrentAPIIndex(); diff --git a/plugins/main/public/react-services/app-state.js b/plugins/main/public/react-services/app-state.js index cbbfe23719..00ac513b3f 100644 --- a/plugins/main/public/react-services/app-state.js +++ b/plugins/main/public/react-services/app-state.js @@ -79,59 +79,6 @@ export class AppState { } } - /** - * Set a new value to the 'createdAt' cookie - * @param {*} date - */ - static setCreatedAt(date) { - try { - const createdAt = encodeURI(date); - const exp = new Date(); - exp.setDate(exp.getDate() + 365); - getCookies().set('createdAt', createdAt, { - expires: exp, - }); - } catch (error) { - const options = { - context: `${AppState.name}.setCreatedAt`, - level: UI_LOGGER_LEVELS.ERROR, - severity: UI_ERROR_SEVERITIES.UI, - error: { - error: error, - message: error.message || error, - title: `${error.name}: Error set createdAt date`, - }, - }; - getErrorOrchestrator().handleError(options); - throw error; - } - } - - /** - * Get 'createdAt' value - */ - static getCreatedAt() { - try { - const createdAt = getCookies().get('createdAt') - ? decodeURI(getCookies().get('createdAt')) - : false; - return createdAt ? createdAt : false; - } catch (error) { - const options = { - context: `${AppState.name}.getCreatedAt`, - level: UI_LOGGER_LEVELS.ERROR, - severity: UI_ERROR_SEVERITIES.UI, - error: { - error: error, - message: error.message || error, - title: `${error.name}: Error get createdAt date`, - }, - }; - getErrorOrchestrator().handleError(options); - throw error; - } - } - /** * Get 'API' value */ @@ -190,33 +137,6 @@ export class AppState { } } - /** - * Get 'APISelector' value - */ - static getAPISelector() { - return getCookies().get('APISelector') - ? decodeURI(getCookies().get('APISelector')) == 'true' - : false; - } - - /** - * Get 'patternSelector' value - */ - static getPatternSelector() { - return getCookies().get('patternSelector') - ? decodeURI(getCookies().get('patternSelector')) == 'true' - : false; - } - - /** - * Set a new value to the 'patternSelector' cookie - * @param {*} value - */ - static setPatternSelector(value) { - const encodedPattern = encodeURI(value); - getCookies().set('patternSelector', encodedPattern, {}); - } - /** * Set a new value to the 'currentPattern' cookie * @param {*} newPattern diff --git a/plugins/main/public/react-services/wz-api-check.js b/plugins/main/public/react-services/wz-api-check.js index 30761a277a..2b037f68ed 100644 --- a/plugins/main/public/react-services/wz-api-check.js +++ b/plugins/main/public/react-services/wz-api-check.js @@ -30,16 +30,15 @@ export class ApiCheck { const url = getHttp().basePath.prepend('/api/check-stored-api'); const options = { method: 'POST', - headers: { ...PLUGIN_PLATFORM_REQUEST_HEADERS, 'content-type': 'application/json' }, + headers: { + ...PLUGIN_PLATFORM_REQUEST_HEADERS, + 'content-type': 'application/json', + }, url: url, data: payload, - timeout: timeout || 20000 + timeout: timeout || 20000, }; - if (Object.keys(configuration).length) { - AppState.setPatternSelector(configuration['ip.selector']); - } - const response = await request(options); if (response.error) { @@ -55,8 +54,10 @@ export class ApiCheck { return Promise.reject(this.returnErrorInstance(response)); } else { return (err || {}).message || false - ? Promise.reject(this.returnErrorInstance(err,err.message)) - : Promise.reject(this.returnErrorInstance(err,err || 'Server did not respond')); + ? Promise.reject(this.returnErrorInstance(err, err.message)) + : Promise.reject( + this.returnErrorInstance(err, err || 'Server did not respond'), + ); } } } @@ -65,7 +66,7 @@ export class ApiCheck { * Check the status of an API entry * @param {String} apiObject */ - static async checkApi(apiEntry, forceRefresh=false) { + static async checkApi(apiEntry, forceRefresh = false) { try { const wazuhConfig = new WazuhConfig(); const { timeout } = wazuhConfig.getConfig(); @@ -73,10 +74,13 @@ export class ApiCheck { const options = { method: 'POST', - headers: { ...PLUGIN_PLATFORM_REQUEST_HEADERS, 'content-type': 'application/json' }, + headers: { + ...PLUGIN_PLATFORM_REQUEST_HEADERS, + 'content-type': 'application/json', + }, url: url, - data: {...apiEntry, forceRefresh}, - timeout: timeout || 20000 + data: { ...apiEntry, forceRefresh }, + timeout: timeout || 20000, }; const response = await request(options); @@ -92,23 +96,25 @@ export class ApiCheck { return Promise.reject(this.returnErrorInstance(response)); } else { return (err || {}).message || false - ? Promise.reject(this.returnErrorInstance(err,err.message)) - : Promise.reject(this.returnErrorInstance(err,err || 'Server did not respond')); + ? Promise.reject(this.returnErrorInstance(err, err.message)) + : Promise.reject( + this.returnErrorInstance(err, err || 'Server did not respond'), + ); } } } - /** + /** * Customize message and return an error object - * @param error - * @param message + * @param error + * @param message * @returns error */ - static returnErrorInstance(error, message){ - if(!error || typeof error === 'string'){ - return new Error(message || error); - } - error.message = message - return error + static returnErrorInstance(error, message) { + if (!error || typeof error === 'string') { + return new Error(message || error); } + error.message = message; + return error; + } } From bf5a9ab43747dd822da9c9178c38526a408d211c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20David=20Guti=C3=A9rrez?= Date: Mon, 30 Sep 2024 16:38:35 +0200 Subject: [PATCH 03/28] feat(state): add tests --- .../public/services/state/state.test.ts | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 plugins/wazuh-core/public/services/state/state.test.ts diff --git a/plugins/wazuh-core/public/services/state/state.test.ts b/plugins/wazuh-core/public/services/state/state.test.ts new file mode 100644 index 0000000000..7f1800d9fd --- /dev/null +++ b/plugins/wazuh-core/public/services/state/state.test.ts @@ -0,0 +1,61 @@ +import { CoreState } from './state'; + +const noop = () => {}; +const logger = { + info: noop, + warn: noop, + error: noop, + debug: noop, +}; + +describe('State', () => { + it('Throw error accessing to non-existent state container', () => { + const state = new CoreState(logger); + expect(() => { + state.get('no_existent_state_container'); + }).toThrow( + 'State container [no_existent_state_container] does not exist. Did you forget to register it?', + ); + + expect(() => { + state.set('no_existent_state_container', {}); + }).toThrow( + 'State container [no_existent_state_container] does not exist. Did you forget to register it?', + ); + + expect(() => { + state.remove('no_existent_state_container'); + }).toThrow( + 'State container [no_existent_state_container] does not exist. Did you forget to register it?', + ); + + expect(() => { + state.subscribe('no_existent_state_container', () => {}); + }).toThrow( + 'State container [no_existent_state_container] does not exist. Did you forget to register it?', + ); + }); + + it('Register a state container, get value, set new value and get new value', () => { + const state = new CoreState(logger); + + const subscribe = jest.fn(); + state.register('state_container', { + _value: true, // mock state. This is not exist in the StateContainer type + get() { + return this._value; + }, + set() { + this._value = false; + }, + remove: () => {}, + subscribe: subscribe, + }); + + expect(state.get('state_container')).toBe(true); + + state.set('state_container', false); + + expect(state.get('state_container')).toBe(false); + }); +}); From f2e54f66f0702f55f9e965073ea0445b7485c46d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20David=20Guti=C3=A9rrez?= Date: Tue, 1 Oct 2024 10:00:04 +0200 Subject: [PATCH 04/28] feat(state): wrap the subscrition handler to allow they can unsubscribed when the plugin stops - Remove console.log - Dispatch new values when remove from the state containers --- plugins/wazuh-core/public/plugin.ts | 9 +++++---- .../state/containers/data-source-alerts.ts | 1 + .../services/state/containers/server-host.ts | 1 + plugins/wazuh-core/public/services/state/state.ts | 15 +++++++++++++-- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/plugins/wazuh-core/public/plugin.ts b/plugins/wazuh-core/public/plugin.ts index 8137ff0e5a..a2dfcd34fd 100644 --- a/plugins/wazuh-core/public/plugin.ts +++ b/plugins/wazuh-core/public/plugin.ts @@ -71,15 +71,13 @@ export class WazuhCorePlugin new ServerHostStateContainer(logger, { store: cookiesStore }), ); this.services.state.subscribe('server_host', value => { - // TODO: refresh the auth when the server_host data changed + // TODO: refresh the auth when the server_host data changed and there is value }); this.services.state.register( 'data_source_alerts', new DataSourceAlertsStateContainer(logger, { store: cookiesStore }), ); - console.log(this.services.state.get('server_host')); - await this.services.dashboardSecurity.setup(); return { @@ -98,6 +96,7 @@ export class WazuhCorePlugin setUiSettings(core.uiSettings); await this.services.configuration.start({ http: core.http }); + this.services.state.start(); return { ...this.services, @@ -111,5 +110,7 @@ export class WazuhCorePlugin }; } - public stop() {} + public stop() { + this.services.state.stop(); + } } diff --git a/plugins/wazuh-core/public/services/state/containers/data-source-alerts.ts b/plugins/wazuh-core/public/services/state/containers/data-source-alerts.ts index 497cdb84fe..c03eaf4b25 100644 --- a/plugins/wazuh-core/public/services/state/containers/data-source-alerts.ts +++ b/plugins/wazuh-core/public/services/state/containers/data-source-alerts.ts @@ -81,6 +81,7 @@ export class DataSourceAlertsStateContainer implements StateContainer { try { this.logger.debug('Removing'); const result = this.store.remove(this.storeKey); + this.updater$.next(undefined); this.logger.debug('Removed'); return result; } catch (error) { diff --git a/plugins/wazuh-core/public/services/state/containers/server-host.ts b/plugins/wazuh-core/public/services/state/containers/server-host.ts index b20ccabe84..c0edb7d105 100644 --- a/plugins/wazuh-core/public/services/state/containers/server-host.ts +++ b/plugins/wazuh-core/public/services/state/containers/server-host.ts @@ -55,6 +55,7 @@ export class ServerHostStateContainer implements StateContainer { remove() { this.logger.debug('Removing'); const result = this.store.remove(this.storeKey); + this.updater$.next(undefined); this.logger.debug('Removed'); return result; } diff --git a/plugins/wazuh-core/public/services/state/state.ts b/plugins/wazuh-core/public/services/state/state.ts index d98156e269..3850e9a17b 100644 --- a/plugins/wazuh-core/public/services/state/state.ts +++ b/plugins/wazuh-core/public/services/state/state.ts @@ -2,8 +2,10 @@ import { ILogger } from '../../../common/services/configuration'; import { createHOCs } from './hocs/creator'; import { createHooks } from './hooks/creator'; import { State, StateContainer } from './types'; +import { Subscription } from 'rxjs'; export class CoreState implements State { + private _subscriptions: Subscription = new Subscription(); private stateContainers: Map; constructor(private logger: ILogger) { this.stateContainers = new Map(); @@ -22,6 +24,9 @@ export class CoreState implements State { } stop() { this.logger.debug('Stop'); + this.logger.debug('Unsubscribing'); + this._subscriptions.unsubscribe(); + this.logger.debug('Unsubscribed'); } getStateContainer(name: string) { if (!this.stateContainers.has(name)) { @@ -48,7 +53,13 @@ export class CoreState implements State { subscribe(name: string, callback: (value) => void) { const stateContainerSubscription = this.getStateContainer(name)!.subscribe(callback); - // TODO: unsubscribe when the app is stopped - return stateContainerSubscription; + + // Create a wrapper of the original subscription to remove all in the stop plugin lifecycle + const stateContainerSub = () => { + stateContainerSubscription(); + this._subscriptions.remove(stateContainerSubscription); + }; + this._subscriptions.add(stateContainerSubscription); + return stateContainerSub; } } From f11467bb32cb24623294947060a521fbb0a92032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20David=20Guti=C3=A9rrez?= Date: Wed, 27 Nov 2024 11:47:43 +0100 Subject: [PATCH 05/28] fix(state): enhance README.md examples --- .../public/services/state/README.md | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/plugins/wazuh-core/public/services/state/README.md b/plugins/wazuh-core/public/services/state/README.md index 2ca9274d1a..6a305a36c5 100644 --- a/plugins/wazuh-core/public/services/state/README.md +++ b/plugins/wazuh-core/public/services/state/README.md @@ -65,11 +65,19 @@ Use the state container. ```tsx const MyComponent = withStateContainer('my_state_container')(props => { - // access to state container value - props['stateContainer:my_state_container'].value; - // set a new value - props['stateContainer:my_state_container'].set(newValue); - // remove the value - props['stateContainer:my_state_container'].remove(); + const getStateContainerValue = () => { + // access to state container value + return props['stateContainer:my_state_container'].value; + }; + + const setStateContainterValue = newValue => { + // set a new value + props['stateContainer:my_state_container'].set(newValue); + }; + + const removeStateContainerValue = () => { + // remove the value + props['stateContainer:my_state_container'].remove(); + }; }); ``` From d5dd4bb4558b503bb498a80862088be6af968c87 Mon Sep 17 00:00:00 2001 From: Antonio <34042064+Desvelao@users.noreply.github.com> Date: Wed, 27 Nov 2024 11:53:36 +0100 Subject: [PATCH 06/28] Apply suggestions from code review Co-authored-by: Guido Modarelli <38738725+guidomodarelli@users.noreply.github.com> --- .../public/services/state/containers/data-source-alerts.ts | 2 +- .../services/state/containers/server-host-cluster-info.ts | 4 ++-- .../public/services/state/containers/server-host.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/wazuh-core/public/services/state/containers/data-source-alerts.ts b/plugins/wazuh-core/public/services/state/containers/data-source-alerts.ts index c03eaf4b25..a2180c1eec 100644 --- a/plugins/wazuh-core/public/services/state/containers/data-source-alerts.ts +++ b/plugins/wazuh-core/public/services/state/containers/data-source-alerts.ts @@ -5,7 +5,7 @@ import { BehaviorSubject } from 'rxjs'; export class DataSourceAlertsStateContainer implements StateContainer { private store: any; private storeKey: string = 'currentPattern'; - updater$: BehaviorSubject; + updater$: BehaviorSubject; constructor(private logger: ILogger, { store }) { this.store = store; this.updater$ = new BehaviorSubject(this.get()); diff --git a/plugins/wazuh-core/public/services/state/containers/server-host-cluster-info.ts b/plugins/wazuh-core/public/services/state/containers/server-host-cluster-info.ts index 923303049e..cf8c9e4a7a 100644 --- a/plugins/wazuh-core/public/services/state/containers/server-host-cluster-info.ts +++ b/plugins/wazuh-core/public/services/state/containers/server-host-cluster-info.ts @@ -5,7 +5,7 @@ import { BehaviorSubject } from 'rxjs'; export class ServerHostClusterInfoStateContainer implements StateContainer { private store: any; private storeKey: string = 'clusterInfo'; - updater$: BehaviorSubject; + updater$: BehaviorSubject; constructor(private logger: ILogger, { store }) { this.store = store; this.updater$ = new BehaviorSubject(this.get()); @@ -17,7 +17,7 @@ export class ServerHostClusterInfoStateContainer implements StateContainer { let result = {}; if (rawData) { this.logger.debug('Getting decoded data'); - const decodedData = decodeURI(this.store.get(this.storeKey)); + const decodedData = decodeURI(rawData); this.logger.debug(`Decoded data: ${decodedData}`); result = JSON.parse(decodedData); } else { diff --git a/plugins/wazuh-core/public/services/state/containers/server-host.ts b/plugins/wazuh-core/public/services/state/containers/server-host.ts index c0edb7d105..df9b605264 100644 --- a/plugins/wazuh-core/public/services/state/containers/server-host.ts +++ b/plugins/wazuh-core/public/services/state/containers/server-host.ts @@ -5,7 +5,7 @@ import { BehaviorSubject } from 'rxjs'; export class ServerHostStateContainer implements StateContainer { private store: any; private storeKey: string = 'currentApi'; - updater$: BehaviorSubject; + updater$: BehaviorSubject; constructor(private logger: ILogger, { store }) { this.store = store; this.updater$ = new BehaviorSubject(this.get()); From 05cfa5e85daf835df42964acb9af25bcc9bdd895 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20David=20Guti=C3=A9rrez?= Date: Wed, 27 Nov 2024 11:54:08 +0100 Subject: [PATCH 07/28] fix(state): typos --- plugins/wazuh-core/public/services/state/README.md | 2 +- plugins/wazuh-core/public/services/state/state.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/wazuh-core/public/services/state/README.md b/plugins/wazuh-core/public/services/state/README.md index 6a305a36c5..4871eb1b01 100644 --- a/plugins/wazuh-core/public/services/state/README.md +++ b/plugins/wazuh-core/public/services/state/README.md @@ -54,7 +54,7 @@ Use the state container. ```ts const [value, { set: setValue, remove: removeValue }] = - useStateContainter('my_state_container'); + useStateContainer('my_state_container'); ``` ### HOCs diff --git a/plugins/wazuh-core/public/services/state/state.test.ts b/plugins/wazuh-core/public/services/state/state.test.ts index 7f1800d9fd..32df877d6b 100644 --- a/plugins/wazuh-core/public/services/state/state.test.ts +++ b/plugins/wazuh-core/public/services/state/state.test.ts @@ -41,7 +41,7 @@ describe('State', () => { const subscribe = jest.fn(); state.register('state_container', { - _value: true, // mock state. This is not exist in the StateContainer type + _value: true, // mock state. This does not exist in the StateContainer type get() { return this._value; }, From d86df0c3938e71ece8168b7041cf08abffb148fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20David=20Guti=C3=A9rrez?= Date: Wed, 27 Nov 2024 12:28:35 +0100 Subject: [PATCH 08/28] fix(state): minor fixes --- plugins/wazuh-core/public/services/state/hocs/creator.tsx | 5 +++-- plugins/wazuh-core/public/services/state/hooks/creator.ts | 2 +- plugins/wazuh-core/public/services/state/state.test.ts | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/plugins/wazuh-core/public/services/state/hocs/creator.tsx b/plugins/wazuh-core/public/services/state/hocs/creator.tsx index ead4a69f07..171cf1c79c 100644 --- a/plugins/wazuh-core/public/services/state/hocs/creator.tsx +++ b/plugins/wazuh-core/public/services/state/hocs/creator.tsx @@ -2,7 +2,8 @@ import React from 'react'; export const createHOCs = ({ useStateContainer }) => { return { withStateContainer: (name: string) => WrappedComponent => props => { - const [state, setState, removeValue] = useStateContainer(name); + const [state, { set: setState, remove: removeState }] = + useStateContainer(name); return ( { [`stateContainer:${name}`]: { value: state, set: setState, - remove: removeValue, + remove: removeState, }, }} /> diff --git a/plugins/wazuh-core/public/services/state/hooks/creator.ts b/plugins/wazuh-core/public/services/state/hooks/creator.ts index 9902c6c1a1..96679e634d 100644 --- a/plugins/wazuh-core/public/services/state/hooks/creator.ts +++ b/plugins/wazuh-core/public/services/state/hooks/creator.ts @@ -11,7 +11,7 @@ export const createHooks = ({ state }: { state: State }) => { state.set(name, value); } function removeValue() { - state.remove(name, value); + state.remove(name); } return [value, { set: setValue, remove: removeValue }]; } diff --git a/plugins/wazuh-core/public/services/state/state.test.ts b/plugins/wazuh-core/public/services/state/state.test.ts index 32df877d6b..e2736f958b 100644 --- a/plugins/wazuh-core/public/services/state/state.test.ts +++ b/plugins/wazuh-core/public/services/state/state.test.ts @@ -45,8 +45,8 @@ describe('State', () => { get() { return this._value; }, - set() { - this._value = false; + set(newValue) { + this._value = newValue; }, remove: () => {}, subscribe: subscribe, From b883a65e8be9f096b5c09b543bbe1e21be729422 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20David=20Guti=C3=A9rrez?= Date: Wed, 27 Nov 2024 13:36:42 +0100 Subject: [PATCH 09/28] fix(state): unsubscribe from state container --- .../public/services/state/README.md | 7 +++- .../public/services/state/state.test.ts | 38 ++++++++++++++++++- .../wazuh-core/public/services/state/state.ts | 6 +-- 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/plugins/wazuh-core/public/services/state/README.md b/plugins/wazuh-core/public/services/state/README.md index 4871eb1b01..d683a9a89b 100644 --- a/plugins/wazuh-core/public/services/state/README.md +++ b/plugins/wazuh-core/public/services/state/README.md @@ -35,15 +35,18 @@ state.set('my_state', newMyState); ### Remove data of a state container ```ts -state.remove('my_state', newMyState); +state.remove('my_state'); ``` ### Subscribe to a state container ```ts -state.subscribe('my_state', data => { +const unsubscribe = state.subscribe('my_state', data => { // Do something with the data }); + +// Unsubscribe +unsubscribe(); ``` ### Hooks diff --git a/plugins/wazuh-core/public/services/state/state.test.ts b/plugins/wazuh-core/public/services/state/state.test.ts index e2736f958b..4de8fb8da4 100644 --- a/plugins/wazuh-core/public/services/state/state.test.ts +++ b/plugins/wazuh-core/public/services/state/state.test.ts @@ -1,4 +1,5 @@ import { CoreState } from './state'; +import { BehaviorSubject } from 'rxjs'; const noop = () => {}; const logger = { @@ -48,7 +49,7 @@ describe('State', () => { set(newValue) { this._value = newValue; }, - remove: () => {}, + remove() {}, subscribe: subscribe, }); @@ -58,4 +59,39 @@ describe('State', () => { expect(state.get('state_container')).toBe(false); }); + + it('Register a state container, subscribe, set new value and remove the subscription', () => { + const state = new CoreState(logger); + + const subscriber = jest.fn(); + state.register('state_container', { + _value: true, // mock state. This does not exist in the StateContainer type + get() { + return this._value; + }, + set(newValue) { + this._value = newValue; + this.updater$.next(this._value); + }, + remove() {}, + updater$: new BehaviorSubject(), + subscribe(cb) { + return this.updater$.subscribe(cb); + }, + }); + + expect(state._subscriptions._subscriptions).toEqual(null); + + const unsubscribe = state.subscribe('state_container', subscriber); + expect(state._subscriptions._subscriptions).toHaveLength(1); + + state.set('state_container', false); + expect(subscriber).toHaveBeenCalledTimes(2); + + unsubscribe(); + expect(state._subscriptions._subscriptions).toHaveLength(0); + + state.set('state_container', false); + expect(subscriber).toHaveBeenCalledTimes(2); + }); }); diff --git a/plugins/wazuh-core/public/services/state/state.ts b/plugins/wazuh-core/public/services/state/state.ts index 3850e9a17b..3c0173d3b0 100644 --- a/plugins/wazuh-core/public/services/state/state.ts +++ b/plugins/wazuh-core/public/services/state/state.ts @@ -55,11 +55,11 @@ export class CoreState implements State { this.getStateContainer(name)!.subscribe(callback); // Create a wrapper of the original subscription to remove all in the stop plugin lifecycle - const stateContainerSub = () => { - stateContainerSubscription(); + const stateContainerUnsub = () => { + stateContainerSubscription.unsubscribe(); this._subscriptions.remove(stateContainerSubscription); }; this._subscriptions.add(stateContainerSubscription); - return stateContainerSub; + return stateContainerUnsub; } } From 01fc56f8dbd6b6b96997b4a31abb50efaa4fd737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20David=20Guti=C3=A9rrez?= Date: Wed, 27 Nov 2024 14:37:11 +0100 Subject: [PATCH 10/28] fix: upgrade react-cookie dependency to remove vulnerability in package --- plugins/wazuh-core/package.json | 2 +- plugins/wazuh-core/yarn.lock | 52 ++++++++++++++++----------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/plugins/wazuh-core/package.json b/plugins/wazuh-core/package.json index 088bdc21d7..a709226e7e 100644 --- a/plugins/wazuh-core/package.json +++ b/plugins/wazuh-core/package.json @@ -25,7 +25,7 @@ "jwt-decode": "^3.1.2", "md5": "^2.3.0", "node-cron": "^3.0.2", - "react-cookie": "^4.0.3" + "react-cookie": "^7.2.2" }, "devDependencies": { "@testing-library/user-event": "^14.5.0", diff --git a/plugins/wazuh-core/yarn.lock b/plugins/wazuh-core/yarn.lock index 8b5f16ab52..9272fbeda9 100644 --- a/plugins/wazuh-core/yarn.lock +++ b/plugins/wazuh-core/yarn.lock @@ -141,12 +141,12 @@ version "0.0.0-semantically-released" resolved "https://codeload.github.com/testing-library/user-event/tar.gz/4be87b3452f524bcc256d43cfb891ba1f0e236d6" -"@types/cookie@^0.3.3": - version "0.3.3" - resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.3.3.tgz#85bc74ba782fb7aa3a514d11767832b0e3bc6803" - integrity sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow== +"@types/cookie@^0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.6.0.tgz#eac397f28bf1d6ae0ae081363eca2f425bedf0d5" + integrity sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA== -"@types/hoist-non-react-statics@^3.0.1": +"@types/hoist-non-react-statics@^3.3.5": version "3.3.5" resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz#dab7867ef789d87e2b4b0003c9d65c49cc44a494" integrity sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg== @@ -175,9 +175,9 @@ integrity sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA== "@types/react@*": - version "18.3.10" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.10.tgz#6edc26dc22ff8c9c226d3c7bf8357b013c842219" - integrity sha512-02sAAlBnP39JgXwkAq3PeU9DVaaGpZyF3MGcC0MKgQVkZor5IiiDAipVaxQHtDJAmO4GIy/rVBy/LzVj76Cyqg== + version "18.3.12" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.12.tgz#99419f182ccd69151813b7ee24b792fe08774f60" + integrity sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw== dependencies: "@types/prop-types" "*" csstype "^3.0.2" @@ -572,10 +572,10 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -cookie@^0.4.0: - version "0.4.2" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" - integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== +cookie@^0.7.2: + version "0.7.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" + integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== cross-spawn@^7.0.2: version "7.0.3" @@ -1313,7 +1313,7 @@ hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: dependencies: function-bind "^1.1.2" -hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0: +hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -2040,14 +2040,14 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -react-cookie@^4.0.3: - version "4.1.1" - resolved "https://registry.yarnpkg.com/react-cookie/-/react-cookie-4.1.1.tgz#832e134ad720e0de3e03deaceaab179c4061a19d" - integrity sha512-ffn7Y7G4bXiFbnE+dKhHhbP+b8I34mH9jqnm8Llhj89zF4nPxPutxHT1suUqMeCEhLDBI7InYwf1tpaSoK5w8A== +react-cookie@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/react-cookie/-/react-cookie-7.2.2.tgz#a7559e552ea9cca39a4b3686723a5acf504b8f84" + integrity sha512-e+hi6axHcw9VODoeVu8WyMWyoosa1pzpyjfvrLdF7CexfU+WSGZdDuRfHa4RJgTpfv3ZjdIpHE14HpYBieHFhg== dependencies: - "@types/hoist-non-react-statics" "^3.0.1" - hoist-non-react-statics "^3.0.0" - universal-cookie "^4.0.0" + "@types/hoist-non-react-statics" "^3.3.5" + hoist-non-react-statics "^3.3.2" + universal-cookie "^7.0.0" react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" @@ -2463,13 +2463,13 @@ unescape-js@^1.0.5: dependencies: string.fromcodepoint "^0.2.1" -universal-cookie@^4.0.0: - version "4.0.4" - resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-4.0.4.tgz#06e8b3625bf9af049569ef97109b4bb226ad798d" - integrity sha512-lbRVHoOMtItjWbM7TwDLdl8wug7izB0tq3/YVKhT/ahB4VDvWMyvnADfnJI8y6fSvsjh51Ix7lTGC6Tn4rMPhw== +universal-cookie@^7.0.0: + version "7.2.2" + resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-7.2.2.tgz#93ae9ec55baab89b24300473543170bb8112773c" + integrity sha512-fMiOcS3TmzP2x5QV26pIH3mvhexLIT0HmPa3V7Q7knRfT9HG6kTwq02HZGLPw0sAOXrAmotElGRvTLCMbJsvxQ== dependencies: - "@types/cookie" "^0.3.3" - cookie "^0.4.0" + "@types/cookie" "^0.6.0" + cookie "^0.7.2" uri-js@^4.2.2: version "4.4.1" From f6e7854c29be59b2f5516b2432796f9627f5681a Mon Sep 17 00:00:00 2001 From: Antonio <34042064+Desvelao@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:02:28 +0100 Subject: [PATCH 11/28] Apply suggestions from code review Co-authored-by: Guido Modarelli <38738725+guidomodarelli@users.noreply.github.com> --- plugins/wazuh-core/public/plugin.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/wazuh-core/public/plugin.ts b/plugins/wazuh-core/public/plugin.ts index a2dfcd34fd..764181449a 100644 --- a/plugins/wazuh-core/public/plugin.ts +++ b/plugins/wazuh-core/public/plugin.ts @@ -20,10 +20,10 @@ import { DataSourceAlertsStateContainer } from './services/state/containers/data export class WazuhCorePlugin implements Plugin { - runtime: { [key: string]: any } = { + runtime: Record = { state: {}, }; - _internal: { [key: string]: any } = {}; + _internal: Record = {}; services: { [key: string]: any; dashboardSecurity?: DashboardSecurity; @@ -102,7 +102,6 @@ export class WazuhCorePlugin ...this.services, utils, API_USER_STATUS_RUN_AS, - // hooks, hooks: { ...hooks, ...this.runtime.state.setup.hooks, From 9a2dafa01e12feddc9355782417cb1969862d679 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20David=20Guti=C3=A9rrez?= Date: Thu, 12 Dec 2024 10:15:55 +0100 Subject: [PATCH 12/28] fix(state): lint and prettier --- .../container/health-check.container.tsx | 168 +++++++++--------- .../state/containers/data-source-alerts.ts | 55 ++++-- .../containers/server-host-cluster-info.ts | 42 ++++- .../services/state/containers/server-host.ts | 47 +++-- 4 files changed, 193 insertions(+), 119 deletions(-) diff --git a/plugins/main/public/components/health-check/container/health-check.container.tsx b/plugins/main/public/components/health-check/container/health-check.container.tsx index a05ca42427..91df644add 100644 --- a/plugins/main/public/components/health-check/container/health-check.container.tsx +++ b/plugins/main/public/components/health-check/container/health-check.container.tsx @@ -17,10 +17,12 @@ import { EuiCallOut, EuiDescriptionList, EuiSpacer, + EuiFlexGroup, + EuiFlexItem, } from '@elastic/eui'; import React, { Fragment, useState, useEffect, useRef } from 'react'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { AppState, ErrorHandler } from '../../../react-services'; +import { compose } from 'redux'; +import { ErrorHandler } from '../../../react-services'; import { useAppConfig, useRouterSearch, @@ -33,7 +35,7 @@ import { } from '../services'; import { CheckResult } from '../components/check-result'; import { withErrorBoundary, withRouteResolvers } from '../../common/hocs'; -import { getCore, getHttp, getWzCurrentAppID } from '../../../kibana-services'; +import { getCore, getHttp } from '../../../kibana-services'; import { HEALTH_CHECK_REDIRECTION_TIME, WAZUH_INDEX_TYPE_MONITORING, @@ -43,7 +45,6 @@ import { getThemeAssetURL, getAssetURL } from '../../../utils/assets'; import { serverApis } from '../../../utils/applications'; import { RedirectAppLinks } from '../../../../../../src/plugins/opensearch_dashboards_react/public'; import { ip, wzConfig } from '../../../services/resolves'; -import { compose } from 'redux'; import NavigationService from '../../../react-services/navigation-service'; const checks = { @@ -94,12 +95,51 @@ const checks = { }, }; +const addTagsToUrl = (error: string) => { + const words = error.split(' '); + + for (const [index, word] of words.entries()) { + if (word.includes('http://') || word.includes('https://')) { + if (words[index - 1] === 'guide:') { + if (word.endsWith('.') || word.endsWith(',')) { + words[index - 2] = `${ + words[index - 2] + } ${words[index - 1].slice(0, -1)}${word.slice(-1)}`; + } else { + words[index - 2] = + `${ + words[index - 2] + } ${words[index - 1].slice(0, -1)} `; + } + + words.splice(index - 1, 2); + } else { + if (word.endsWith('.') || word.endsWith(',')) { + words[index] = `${word.slice( + 0, + -1, + )}${word.slice(-1)}`; + } else { + words[index] = + `${word}`; + } + } + } + } + + return words.join(' '); +}; + function HealthCheckComponent() { - const [checkWarnings, setCheckWarnings] = useState<{ [key: string]: [] }>({}); - const [checkErrors, setCheckErrors] = useState<{ [key: string]: [] }>({}); - const [checksReady, setChecksReady] = useState<{ [key: string]: boolean }>( - {}, - ); + const [checkWarnings, setCheckWarnings] = useState>({}); + const [checkErrors, setCheckErrors] = useState>({}); + const [checksReady, setChecksReady] = useState>({}); const [isDebugMode, setIsDebugMode] = useState(false); const appConfig = useAppConfig(); const checksInitiated = useRef(false); @@ -119,6 +159,7 @@ function HealthCheckComponent() { .pathname + '?' + searchParams; + NavigationService.getInstance().navigate(relativePath); } else { NavigationService.getInstance().navigate('/'); @@ -136,19 +177,18 @@ function HealthCheckComponent() { useEffect(() => { // Redirect to app when all checks are ready - Object.keys(checks).every(check => checksReady[check]) && + if ( + Object.keys(checks).every(check => checksReady[check]) && !isDebugMode && - !thereAreWarnings && - (() => - setTimeout( - redirectionPassHealthcheck, - HEALTH_CHECK_REDIRECTION_TIME, - ))(); + !thereAreWarnings + ) { + setTimeout(redirectionPassHealthcheck, HEALTH_CHECK_REDIRECTION_TIME); + } }, [checksReady]); useEffect(() => { // Check if Health should not redirect automatically (Debug mode) - setIsDebugMode(typeof search.debug !== 'undefined'); + setIsDebugMode(search.debug !== undefined); }, []); const handleWarnings = (checkID, warnings, parsed) => { @@ -160,6 +200,7 @@ function HealthCheckComponent() { }), ) : warnings; + setCheckWarnings(prev => ({ ...prev, [checkID]: newWarnings })); }; @@ -172,6 +213,7 @@ function HealthCheckComponent() { }), ) : errors; + setCheckErrors(prev => ({ ...prev, [checkID]: newErrors })); }; @@ -198,73 +240,32 @@ function HealthCheckComponent() { const renderChecks = () => { const showLogButton = thereAreErrors || thereAreWarnings || isDebugMode; - return Object.keys(checks).map((check, index) => { - return ( - - ); - }); - }; - const addTagsToUrl = error => { - const words = error.split(' '); - words.forEach((word, index) => { - if (word.includes('http://') || word.includes('https://')) { - if (words[index - 1] === 'guide:') { - if (word.endsWith('.') || word.endsWith(',')) { - words[index - 2] = `${ - words[index - 2] - } ${words[index - 1].slice(0, -1)}${word.slice(-1)}`; - } else { - words[ - index - 2 - ] = `${ - words[index - 2] - } ${words[index - 1].slice(0, -1)} `; - } - words.splice(index - 1, 2); - } else { - if (word.endsWith('.') || word.endsWith(',')) { - words[index] = `${word.slice( - 0, - -1, - )}${word.slice(-1)}`; - } else { - words[ - index - ] = `${word}`; - } + return Object.keys(checks).map(check => ( + + )); }; - const renderWarnings = () => { - return Object.keys(checkWarnings).map(checkID => + const renderWarnings = () => + Object.keys(checkWarnings).map(checkID => checkWarnings[checkID].map((warning, index) => ( )), ); - }; - - const renderErrors = () => { - return Object.keys(checkErrors).map(checkID => + const renderErrors = () => + Object.keys(checkErrors).map(checkID => checkErrors[checkID].map((error, index) => ( )), ); - }; return (
diff --git a/plugins/wazuh-core/public/services/state/containers/data-source-alerts.ts b/plugins/wazuh-core/public/services/state/containers/data-source-alerts.ts index a2180c1eec..b9fe07b0f8 100644 --- a/plugins/wazuh-core/public/services/state/containers/data-source-alerts.ts +++ b/plugins/wazuh-core/public/services/state/containers/data-source-alerts.ts @@ -1,24 +1,32 @@ -import { ILogger } from '../../../../common/services/configuration'; -import { StateContainer } from '../types'; import { BehaviorSubject } from 'rxjs'; +import { Logger } from '../../../../common/services/configuration'; +import { StateContainer } from '../types'; export class DataSourceAlertsStateContainer implements StateContainer { - private store: any; - private storeKey: string = 'currentPattern'; + private readonly store: any; + private readonly STORE_KEY = 'currentPattern'; updater$: BehaviorSubject; - constructor(private logger: ILogger, { store }) { + + constructor( + private readonly logger: Logger, + { store }, + ) { this.store = store; this.updater$ = new BehaviorSubject(this.get()); } + get() { try { this.logger.debug('Getting data'); - const rawData = this.store.get(this.storeKey); + const rawData = this.store.get(this.STORE_KEY); let result = ''; + if (rawData) { this.logger.debug('Getting decoded data'); + let decodedData = decodeURI(rawData); + this.logger.debug(`Decoded data: ${decodedData}`); /* I assume in previous versions, the selected index pattern could be wrapped with " characters. @@ -27,9 +35,10 @@ export class DataSourceAlertsStateContainer implements StateContainer { if ( decodedData && decodedData[0] === '"' && - decodedData[decodedData.length - 1] === '"' + decodedData.at(-1) === '"' ) { - const newPattern = decodedData.substring(1, decodedData.length - 1); + const newPattern = decodedData.slice(1, -1); + this.set(newPattern); decodedData = decodeURI(this.store.get('currentPattern')); } @@ -39,29 +48,37 @@ export class DataSourceAlertsStateContainer implements StateContainer { this.logger.debug('No raw data'); result = ''; } + this.logger.debug(`Data: ${result}`); + return result; } catch (error) { - this.logger.error(error.message); + this.logger.error(`Error getting data: ${error.message}`); throw error; } } - set(data) { + + set(data: any) { try { this.logger.debug(`Setting data: ${data}`); + const encodedData = encodeURI(data); + this.logger.debug(`Setting encoded data: ${encodedData}`); + const exp = new Date(); + exp.setDate(exp.getDate() + 365); + if (data) { - this.store.set(this.storeKey, encodedData, { + this.store.set(this.STORE_KEY, encodedData, { expires: exp, }); this.updater$.next(encodedData); this.logger.debug(`Encoded data was set: ${encodedData}`); } } catch (error) { - this.logger.error(error.message); + this.logger.error(`Error setting data: ${error.message}`); // TODO: implement // const options = { // context: `${AppState.name}.setClusterInfo`, @@ -77,22 +94,30 @@ export class DataSourceAlertsStateContainer implements StateContainer { throw error; } } + remove() { try { this.logger.debug('Removing'); - const result = this.store.remove(this.storeKey); - this.updater$.next(undefined); + + const result = this.store.remove(this.STORE_KEY); + + this.updater$.next(); this.logger.debug('Removed'); + return result; } catch (error) { this.logger.error(error.message); throw error; } } - subscribe(callback) { + + subscribe(callback: (value: any) => void) { this.logger.debug('Subscribing'); + const subscription = this.updater$.subscribe(callback); + this.logger.debug('Subscribed'); + return subscription; } } diff --git a/plugins/wazuh-core/public/services/state/containers/server-host-cluster-info.ts b/plugins/wazuh-core/public/services/state/containers/server-host-cluster-info.ts index cf8c9e4a7a..f2a78f2892 100644 --- a/plugins/wazuh-core/public/services/state/containers/server-host-cluster-info.ts +++ b/plugins/wazuh-core/public/services/state/containers/server-host-cluster-info.ts @@ -1,32 +1,44 @@ -import { ILogger } from '../../../../common/services/configuration'; -import { StateContainer } from '../types'; import { BehaviorSubject } from 'rxjs'; +import { Logger } from '../../../../common/services/configuration'; +import { StateContainer } from '../types'; export class ServerHostClusterInfoStateContainer implements StateContainer { - private store: any; - private storeKey: string = 'clusterInfo'; + private readonly store: any; + private readonly STORE_KEY = 'clusterInfo'; updater$: BehaviorSubject; - constructor(private logger: ILogger, { store }) { + + constructor( + private readonly logger: Logger, + { store }, + ) { this.store = store; this.updater$ = new BehaviorSubject(this.get()); } + get() { try { this.logger.debug('Getting data'); - const rawData = this.store.get(this.storeKey); + + const rawData = this.store.get(this.STORE_KEY); let result = {}; + if (rawData) { this.logger.debug('Getting decoded data'); + const decodedData = decodeURI(rawData); + this.logger.debug(`Decoded data: ${decodedData}`); result = JSON.parse(decodedData); } else { this.logger.debug('No raw data'); result = {}; } + this.logger.debug(`Data: ${result}`); + return result; } catch (error) { + this.logger.error(`Error getting data: ${error.message}`); // TODO: implement // const options = { // context: `${AppState.name}.getClusterInfo`, @@ -42,21 +54,28 @@ export class ServerHostClusterInfoStateContainer implements StateContainer { throw error; } } - set(data) { + + set(data: any) { try { this.logger.debug(`Setting data: ${data}`); + const encodedData = encodeURI(JSON.stringify(data)); + this.logger.debug(`Setting encoded data: ${encodedData}`); + const exp = new Date(); + exp.setDate(exp.getDate() + 365); + if (data) { - this.store.set(this.storeKey, encodedData, { + this.store.set(this.STORE_KEY, encodedData, { expires: exp, }); this.updater$.next(encodedData); this.logger.debug(`Encoded data was set: ${encodedData}`); } } catch (error) { + this.logger.error(`Error setting data: ${error.message}`); // TODO: implement // const options = { // context: `${AppState.name}.setClusterInfo`, @@ -72,11 +91,16 @@ export class ServerHostClusterInfoStateContainer implements StateContainer { throw error; } } + remove() {} - subscribe(callback) { + + subscribe(callback: (value: any) => void) { this.logger.debug('Subscribing'); + const subscription = this.updater$.subscribe(callback); + this.logger.debug('Subscribed'); + return subscription; } } diff --git a/plugins/wazuh-core/public/services/state/containers/server-host.ts b/plugins/wazuh-core/public/services/state/containers/server-host.ts index df9b605264..576111a295 100644 --- a/plugins/wazuh-core/public/services/state/containers/server-host.ts +++ b/plugins/wazuh-core/public/services/state/containers/server-host.ts @@ -1,42 +1,61 @@ -import { ILogger } from '../../../../common/services/configuration'; -import { StateContainer } from '../types'; import { BehaviorSubject } from 'rxjs'; +import { Logger } from '../../../../common/services/configuration'; +import { StateContainer } from '../types'; export class ServerHostStateContainer implements StateContainer { - private store: any; - private storeKey: string = 'currentApi'; + private readonly store: any; + private readonly STORE_KEY = 'currentApi'; updater$: BehaviorSubject; - constructor(private logger: ILogger, { store }) { + + constructor( + private readonly logger: Logger, + { store }, + ) { this.store = store; this.updater$ = new BehaviorSubject(this.get()); } + get() { this.logger.debug('Getting data'); - const currentAPI = this.store.get(this.storeKey); + + const currentAPI = this.store.get(this.STORE_KEY); + this.logger.debug(`Raw data: ${currentAPI}`); + if (currentAPI) { this.logger.debug('Decoding data'); + const decodedData = decodeURI(currentAPI); + this.logger.debug(`Decoded data: ${decodedData}`); + return decodedData; } + return false; } + set(data) { try { this.logger.debug(`Setting data: ${data}`); + const encodedData = encodeURI(data); + this.logger.debug(`Setting encoded data: ${encodedData}`); + const exp = new Date(); + exp.setDate(exp.getDate() + 365); + if (data) { - this.store.set(this.storeKey, encodedData, { + this.store.set(this.STORE_KEY, encodedData, { expires: exp, }); this.updater$.next(encodedData); this.logger.debug(`Encoded data was set: ${encodedData}`); } } catch (error) { + this.logger.error(`Error setting data: ${error.message}`); // TODO: implement // const options = { // context: `${AppState.name}.setCurrentAPI`, @@ -52,17 +71,25 @@ export class ServerHostStateContainer implements StateContainer { throw error; } } + remove() { this.logger.debug('Removing'); - const result = this.store.remove(this.storeKey); - this.updater$.next(undefined); + + const result = this.store.remove(this.STORE_KEY); + + this.updater$.next(); this.logger.debug('Removed'); + return result; } - subscribe(callback) { + + subscribe(callback: (value: any) => void) { this.logger.debug('Subscribing'); + const subscription = this.updater$.subscribe(callback); + this.logger.debug('Subscribed'); + return subscription; } } From d45a4c32aaf245204c87d7fd65777622227356a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20David=20Guti=C3=A9rrez?= Date: Thu, 12 Dec 2024 10:48:31 +0100 Subject: [PATCH 13/28] fix(state): lint and prettier --- .../container/health-check.container.test.tsx | 19 +-- .../public/components/settings/settings.tsx | 125 +++++++++++------- .../main/public/react-services/app-state.js | 76 +++++++---- .../public/react-services/wz-api-check.js | 54 +++++--- .../public/services/state/hocs/creator.tsx | 11 +- .../public/services/state/hooks/creator.ts | 6 +- .../public/services/state/state.test.ts | 17 ++- .../wazuh-core/public/services/state/state.ts | 36 +++-- .../wazuh-core/public/services/state/types.ts | 7 +- 9 files changed, 226 insertions(+), 125 deletions(-) diff --git a/plugins/main/public/components/health-check/container/health-check.container.test.tsx b/plugins/main/public/components/health-check/container/health-check.container.test.tsx index 242bd8be24..1ce65e852d 100644 --- a/plugins/main/public/components/health-check/container/health-check.container.test.tsx +++ b/plugins/main/public/components/health-check/container/health-check.container.test.tsx @@ -1,3 +1,4 @@ +/* eslint-disable unicorn/consistent-function-scoping */ /* * Wazuh app - Health Check Component - Test * @@ -53,14 +54,14 @@ jest.mock('../../../components/common/hooks', () => ({ })); jest.mock('../services', () => ({ - checkPatternService: appInfo => () => undefined, - checkTemplateService: appInfo => () => undefined, - checkApiService: appInfo => () => undefined, - checkSetupService: appInfo => () => undefined, - checkFieldsService: appInfo => () => undefined, - checkPluginPlatformSettings: appInfo => () => undefined, - checkPatternSupportService: appInfo => () => undefined, - checkIndexPatternService: appInfo => () => undefined, + checkPatternService: appInfo => () => {}, + checkTemplateService: appInfo => () => {}, + checkApiService: appInfo => () => {}, + checkSetupService: appInfo => () => {}, + checkFieldsService: appInfo => () => {}, + checkPluginPlatformSettings: appInfo => () => {}, + checkPatternSupportService: appInfo => () => {}, + checkIndexPatternService: appInfo => () => {}, })); jest.mock('../components/check-result', () => ({ @@ -121,6 +122,7 @@ describe('Health Check container', () => { ]); // invoke is wrapped with act to await for setState const callOutError = component.find('EuiCallOut'); + expect(callOutError.text()).toBe('[API version] Test error'); }); @@ -132,6 +134,7 @@ describe('Health Check container', () => { ]); const callOutWarning = component.find('EuiCallOut'); + expect(callOutWarning.text()).toBe('[API version] Test warning'); }); }); diff --git a/plugins/main/public/components/settings/settings.tsx b/plugins/main/public/components/settings/settings.tsx index f869502fb3..4d0b8e17b1 100644 --- a/plugins/main/public/components/settings/settings.tsx +++ b/plugins/main/public/components/settings/settings.tsx @@ -1,3 +1,4 @@ +/* eslint-disable react/prop-types */ /* * Wazuh app - Settings controller * Copyright (C) 2015-2022 Wazuh, Inc. @@ -11,6 +12,8 @@ */ import React from 'react'; import { EuiProgress, EuiTabs, EuiTab } from '@elastic/eui'; +import { compose } from 'redux'; +import { connect } from 'react-redux'; import { AppState } from '../../react-services/app-state'; import { GenericRequest } from '../../react-services/generic-request'; import { WzMisc } from '../../factories/misc'; @@ -33,9 +36,7 @@ import { serverApis, appSettings, } from '../../utils/applications'; -import { compose } from 'redux'; import { withErrorBoundary, withRouteResolvers } from '../common/hocs'; -import { connect } from 'react-redux'; import { enableMenu, ip, @@ -46,28 +47,17 @@ import { Route, Switch } from '../router-search'; import { useRouterSearch } from '../common/hooks'; import NavigationService from '../../react-services/navigation-service'; -const configurationTabID = 'configuration'; - +const CONFIGURATION_TAB_ID = 'configuration'; const mapStateToProps = state => ({ configurationUIEditable: state.appConfig.data['configuration.ui_api_editable'], configurationIPSelector: state.appConfig.data['ip.selector'], }); - const mapDispatchToProps = dispatch => ({ updateGlobalBreadcrumb: breadcrumb => dispatch(updateGlobalBreadcrumb(breadcrumb)), }); -export const Settings = compose( - withErrorBoundary, - withRouteResolvers({ enableMenu, ip, nestedResolve, savedSearch }), - connect(mapStateToProps, mapDispatchToProps), -)(props => { - const { tab } = useRouterSearch(); - return ; -}); - class SettingsComponent extends React.Component { state: { tabs: { id: string; name: string }[] | null; @@ -76,12 +66,6 @@ class SettingsComponent extends React.Component { indexPatterns; apiEntries; }; - wzMisc: WzMisc; - tabsConfiguration: { id: string; name: string }[]; - apiIsDown; - googleGroupsSVG; - currentDefault; - appInfo; constructor(props) { super(props); @@ -89,9 +73,10 @@ class SettingsComponent extends React.Component { this.wzMisc = new WzMisc(); if (this.wzMisc.getWizard()) { - window.sessionStorage.removeItem('healthCheck'); + globalThis.sessionStorage.removeItem('healthCheck'); this.wzMisc.setWizard(false); } + this.apiIsDown = this.wzMisc.getApiIsDown(); this.state = { currentApiEntryIndex: false, @@ -105,7 +90,7 @@ class SettingsComponent extends React.Component { getAssetURL('images/icons/google_groups.svg'), ); this.tabsConfiguration = [ - { id: configurationTabID, name: 'Configuration' }, + { id: CONFIGURATION_TAB_ID, name: 'Configuration' }, { id: 'miscellaneous', name: 'Miscellaneous' }, ]; } @@ -119,9 +104,11 @@ class SettingsComponent extends React.Component { ({ id }) => getWzCurrentAppID() === id, ).breadcrumbLabel; const breadcrumb = [{ text: tabActiveName }]; + this.props.updateGlobalBreadcrumb(breadcrumb); } else { const breadcrumb = [{ text: serverApis.breadcrumbLabel }]; + this.props.updateGlobalBreadcrumb(breadcrumb); } @@ -134,6 +121,7 @@ class SettingsComponent extends React.Component { await this.getAppInfo(); } catch (error) { const options = { + // eslint-disable-next-line no-use-before-define context: `${Settings.name}.onInit`, level: UI_LOGGER_LEVELS.ERROR, severity: UI_ERROR_SEVERITIES.BUSINESS, @@ -144,32 +132,44 @@ class SettingsComponent extends React.Component { title: `${error.name}: Cannot initialize Settings`, }, }; + getErrorOrchestrator().handleError(options); } } + wzMisc: WzMisc; + tabsConfiguration: { id: string; name: string }[]; + apiIsDown; + googleGroupsSVG; + currentDefault; + appInfo; + isConfigurationUIEditable() { return this.props.configurationUIEditable; } + /** * Sets the component props */ - setComponentProps(currentTab: string = 'api') { + setComponentProps(currentTab = 'api') { const isConfigurationUIEditable = this.isConfigurationUIEditable(); - if (currentTab === configurationTabID && !isConfigurationUIEditable) { + + if (currentTab === CONFIGURATION_TAB_ID && !isConfigurationUIEditable) { // Change the inaccessible configuration to another accessible NavigationService.getInstance().replace( `/settings?tab=${ - this.tabsConfiguration.find(({ id }) => id !== configurationTabID)!.id + this.tabsConfiguration.find(({ id }) => id !== CONFIGURATION_TAB_ID) + .id }`, ); } + this.setState({ tabs: getWzCurrentAppID() === appSettings.id ? // WORKAROUND: This avoids the configuration tab is displayed this.tabsConfiguration.filter(({ id }) => - !isConfigurationUIEditable ? id !== configurationTabID : true, + isConfigurationUIEditable ? true : id !== CONFIGURATION_TAB_ID, ) : null, }); @@ -177,10 +177,11 @@ class SettingsComponent extends React.Component { // Get current API index getCurrentAPIIndex() { - if (this.state.apiEntries.length) { + if (this.state.apiEntries.length > 0) { const idx = this.state.apiEntries .map(entry => entry.id) .indexOf(this.currentDefault); + this.setState({ currentApiEntryIndex: idx }); } } @@ -200,9 +201,10 @@ class SettingsComponent extends React.Component { this.setState({ indexPatterns: await SavedObject.getListOfWazuhValidIndexPatterns(), }); - } catch (error) { + } catch { this.wzMisc.setBlankScr('Sorry but no valid index patterns were found'); NavigationService.getInstance().navigate('/blank-screen'); + return; } @@ -212,8 +214,10 @@ class SettingsComponent extends React.Component { if (currentApi) { const { id } = JSON.parse(currentApi); + this.currentDefault = id; } + this.getCurrentAPIIndex(); // TODO: what is the purpose of this? @@ -225,6 +229,7 @@ class SettingsComponent extends React.Component { } } catch (error) { const options = { + // eslint-disable-next-line no-use-before-define context: `${Settings.name}.getSettings`, level: UI_LOGGER_LEVELS.ERROR, severity: UI_ERROR_SEVERITIES.BUSINESS, @@ -234,8 +239,10 @@ class SettingsComponent extends React.Component { title: `${error.name}: Error getting API entries`, }, }; + getErrorOrchestrator().handleError(options); } + return; } @@ -244,7 +251,6 @@ class SettingsComponent extends React.Component { try { // Get the index of the API in the entries const index = isIndex ? item : this.getApiIndex(item); - // Get the Api information const api = this.state.apiEntries[index]; const { username, url, port, id } = api; @@ -256,21 +262,31 @@ class SettingsComponent extends React.Component { insecure: 'true', id: id, }; - // Test the connection const data = await ApiCheck.checkApi(tmpData, true); + tmpData.cluster_info = data?.data; - const { cluster_info } = tmpData; + + const { cluster_info: clusterInfo } = tmpData; + // Updates the cluster-information in the registry - this.state.apiEntries[index].cluster_info = cluster_info; + // eslint-disable-next-line react/no-direct-mutation-state + this.state.apiEntries[index].cluster_info = clusterInfo; + // eslint-disable-next-line react/no-direct-mutation-state this.state.apiEntries[index].status = 'online'; + // eslint-disable-next-line react/no-direct-mutation-state this.state.apiEntries[index].allow_run_as = data.data.allow_run_as; this.wzMisc.setApiIsDown(false); - !silent && ErrorHandler.info('Connection success', 'Settings'); + + if (!silent) { + ErrorHandler.info('Connection success', 'Settings'); + } } catch (error) { this.setState({ load: false }); + if (!silent) { const options = { + // eslint-disable-next-line no-use-before-define context: `${Settings.name}.checkManager`, level: UI_LOGGER_LEVELS.ERROR, severity: UI_ERROR_SEVERITIES.BUSINESS, @@ -280,9 +296,11 @@ class SettingsComponent extends React.Component { title: error.name || error, }, }; + getErrorOrchestrator().handleError(options); } - return Promise.reject(error); + + throw error; } } @@ -293,15 +311,18 @@ class SettingsComponent extends React.Component { try { const data = await GenericRequest.request('GET', '/api/setup'); const response = data.data.data; + this.appInfo = { 'app-version': response['app-version'], revision: response['revision'], }; this.setState({ load: false }); - const pattern = AppState.getCurrentPattern(); + + AppState.getCurrentPattern(); this.getCurrentAPIIndex(); + if ( (this.state.currentApiEntryIndex || this.state.currentApiEntryIndex === 0) && @@ -311,7 +332,9 @@ class SettingsComponent extends React.Component { } } catch (error) { AppState.removeNavigation(); + const options = { + // eslint-disable-next-line no-use-before-define context: `${Settings.name}.getAppInfo`, level: UI_LOGGER_LEVELS.ERROR, severity: UI_ERROR_SEVERITIES.BUSINESS, @@ -321,6 +344,7 @@ class SettingsComponent extends React.Component { title: error.name || error, }, }; + getErrorOrchestrator().handleError(options); } } @@ -329,26 +353,25 @@ class SettingsComponent extends React.Component { * Get the API hosts */ async getHosts() { - try { - const result = await GenericRequest.request('GET', '/hosts/apis', {}); - const hosts = result.data || []; - this.setState({ - apiEntries: hosts, - }); - return hosts; - } catch (error) { - return Promise.reject(error); - } + const result = await GenericRequest.request('GET', '/hosts/apis', {}); + const hosts = result.data || []; + + this.setState({ + apiEntries: hosts, + }); + + return hosts; } renderView() { // WORKAROUND: This avoids the configuration view is displayed if ( - this.props.tab === configurationTabID && + this.props.tab === CONFIGURATION_TAB_ID && !this.isConfigurationUIEditable() ) { return null; } + return ( @@ -419,3 +442,13 @@ class SettingsComponent extends React.Component { ); } } + +export const Settings = compose( + withErrorBoundary, + withRouteResolvers({ enableMenu, ip, nestedResolve, savedSearch }), + connect(mapStateToProps, mapDispatchToProps), +)(props => { + const { tab } = useRouterSearch(); + + return ; +}); diff --git a/plugins/main/public/react-services/app-state.js b/plugins/main/public/react-services/app-state.js index 00ac513b3f..9709c6a995 100644 --- a/plugins/main/public/react-services/app-state.js +++ b/plugins/main/public/react-services/app-state.js @@ -18,11 +18,12 @@ import { import { CSVRequest } from '../services/csv-request'; import { getToasts, getCookies } from '../kibana-services'; import * as FileSaver from '../services/file-saver'; +import { UI_LOGGER_LEVELS } from '../../common/constants'; import { WzAuthentication } from './wz-authentication'; import { UI_ERROR_SEVERITIES } from './error-orchestrator/types'; -import { UI_LOGGER_LEVELS } from '../../common/constants'; import { getErrorOrchestrator } from './common-services'; +// eslint-disable-next-line unicorn/no-static-only-class export class AppState { /** * Cluster setters and getters @@ -32,6 +33,7 @@ export class AppState { const clusterInfo = getCookies().get('clusterInfo') ? decodeURI(getCookies().get('clusterInfo')) : false; + return clusterInfo ? JSON.parse(clusterInfo) : {}; } catch (error) { const options = { @@ -44,6 +46,7 @@ export class AppState { title: `${error.name}: Error get cluster info`, }, }; + getErrorOrchestrator().handleError(options); throw error; } @@ -51,14 +54,16 @@ export class AppState { /** * Sets a new value to the cookie 'clusterInfo' object - * @param {*} cluster_info + * @param {*} clusterInfo */ - static setClusterInfo(cluster_info) { + static setClusterInfo(clusterInfo) { try { - const encodedClusterInfo = encodeURI(JSON.stringify(cluster_info)); + const encodedClusterInfo = encodeURI(JSON.stringify(clusterInfo)); const exp = new Date(); + exp.setDate(exp.getDate() + 365); - if (cluster_info) { + + if (clusterInfo) { getCookies().set('clusterInfo', encodedClusterInfo, { expires: exp, }); @@ -74,6 +79,7 @@ export class AppState { title: `${error.name}: Error set cluster info`, }, }; + getErrorOrchestrator().handleError(options); throw error; } @@ -83,12 +89,9 @@ export class AppState { * Get 'API' value */ static getCurrentAPI() { - try { - const currentAPI = getCookies().get('currentApi'); - return currentAPI ? decodeURI(currentAPI) : false; - } catch (error) { - throw error; - } + const currentAPI = getCookies().get('currentApi'); + + return currentAPI ? decodeURI(currentAPI) : false; } /** @@ -96,7 +99,9 @@ export class AppState { */ static removeCurrentAPI() { const updateApiMenu = updateCurrentApi(false); + store.dispatch(updateApiMenu); + return getCookies().remove('currentApi'); } @@ -108,18 +113,18 @@ export class AppState { try { const encodedApi = encodeURI(API); const exp = new Date(); + exp.setDate(exp.getDate() + 365); + if (API) { getCookies().set('currentApi', encodedApi, { expires: exp, }); - try { - const updateApiMenu = updateCurrentApi(JSON.parse(API).id); - store.dispatch(updateApiMenu); - WzAuthentication.refresh(); - } catch (error) { - throw error; - } + + const updateApiMenu = updateCurrentApi(JSON.parse(API).id); + + store.dispatch(updateApiMenu); + WzAuthentication.refresh(); } } catch (error) { const options = { @@ -132,6 +137,7 @@ export class AppState { title: `${error.name}: Error set current API`, }, }; + getErrorOrchestrator().handleError(options); throw error; } @@ -144,7 +150,9 @@ export class AppState { static setCurrentPattern(newPattern) { const encodedPattern = encodeURI(newPattern); const exp = new Date(); + exp.setDate(exp.getDate() + 365); + if (newPattern) { getCookies().set('currentPattern', encodedPattern, { expires: exp, @@ -159,15 +167,18 @@ export class AppState { const currentPattern = getCookies().get('currentPattern') ? decodeURI(getCookies().get('currentPattern')) : ''; + // check if the current Cookie has the format of 3.11 and previous versions, in that case we remove the extra " " characters if ( currentPattern && currentPattern[0] === '"' && - currentPattern[currentPattern.length - 1] === '"' + currentPattern.at(-1) === '"' ) { - const newPattern = currentPattern.substring(1, currentPattern.length - 1); + const newPattern = currentPattern.slice(1, -1); + this.setCurrentPattern(newPattern); } + return getCookies().get('currentPattern') ? decodeURI(getCookies().get('currentPattern')) : ''; @@ -185,14 +196,14 @@ export class AppState { * @param {*} current **/ static setCurrentDevTools(current) { - window.localStorage.setItem('currentDevTools', current); + globalThis.localStorage.setItem('currentDevTools', current); } /** * Get 'currentDevTools' value **/ static getCurrentDevTools() { - return window.localStorage.getItem('currentDevTools'); + return globalThis.localStorage.getItem('currentDevTools'); } /** @@ -201,7 +212,7 @@ export class AppState { * @param {*} value */ static setSessionStorageItem(key, value) { - window.sessionStorage.setItem(key, value); + globalThis.sessionStorage.setItem(key, value); } /** @@ -209,7 +220,7 @@ export class AppState { * @param {*} key */ static getSessionStorageItem(key) { - return window.sessionStorage.getItem(key); + return globalThis.sessionStorage.getItem(key); } /** @@ -217,19 +228,22 @@ export class AppState { * @param {*} key */ static removeSessionStorageItem(key) { - window.sessionStorage.removeItem(key); + globalThis.sessionStorage.removeItem(key); } static setNavigation(params) { const decodedNavigation = getCookies().get('navigate') ? decodeURI(getCookies().get('navigate')) : false; - var navigate = decodedNavigation ? JSON.parse(decodedNavigation) : {}; - for (var key in params) { + let navigate = decodedNavigation ? JSON.parse(decodedNavigation) : {}; + + for (let key in params) { navigate[key] = params[key]; } + if (navigate) { const encodedURI = encodeURI(JSON.stringify(navigate)); + getCookies().set('navigate', encodedURI); } } @@ -239,6 +253,7 @@ export class AppState { ? decodeURI(getCookies().get('navigate')) : false; const navigation = decodedNavigation ? JSON.parse(decodedNavigation) : {}; + return navigation; } @@ -248,21 +263,24 @@ export class AppState { static setWzMenu(isVisible = true) { const showMenu = updateShowMenu(isVisible); + store.dispatch(showMenu); } static async downloadCsv(path, fileName, filters = []) { try { const csvReq = new CSVRequest(); + getToasts().add({ color: 'success', title: 'CSV', text: 'Your download should begin automatically...', toastLifeTimeMs: 4000, }); + const currentApi = JSON.parse(this.getCurrentAPI()).id; const output = await csvReq.fetch(path, currentApi, filters); - const blob = new Blob([output], { type: 'text/csv' }); // eslint-disable-line + const blob = new Blob([output], { type: 'text/csv' }); FileSaver.saveAs(blob, fileName); } catch (error) { @@ -276,12 +294,14 @@ export class AppState { title: `${error.name}: Error generating CSV`, }, }; + getErrorOrchestrator().handleError(options); } } static checkCookies() { getCookies().set('appName', 'wazuh'); + return !!getCookies().get('appName'); } } diff --git a/plugins/main/public/react-services/wz-api-check.js b/plugins/main/public/react-services/wz-api-check.js index 2b037f68ed..c41f3612aa 100644 --- a/plugins/main/public/react-services/wz-api-check.js +++ b/plugins/main/public/react-services/wz-api-check.js @@ -9,20 +9,21 @@ * * Find more information about this on the LICENSE file. */ -import { WazuhConfig } from './wazuh-config'; -import { AppState } from './app-state'; import { WzMisc } from '../factories/misc'; import { getHttp } from '../kibana-services'; import { PLUGIN_PLATFORM_REQUEST_HEADERS } from '../../common/constants'; import { request } from '../services/request-handler'; +import { WazuhConfig } from './wazuh-config'; +// eslint-disable-next-line unicorn/no-static-only-class export class ApiCheck { static async checkStored(data, idChanged = false) { try { const wazuhConfig = new WazuhConfig(); const configuration = wazuhConfig.getConfig(); - const timeout = configuration ? configuration.timeout : 20000; + const timeout = configuration ? configuration.timeout : 20_000; const payload = { id: data }; + if (idChanged) { payload.idChanged = data; } @@ -36,27 +37,34 @@ export class ApiCheck { }, url: url, data: payload, - timeout: timeout || 20000, + timeout: timeout || 20_000, }; - const response = await request(options); if (response.error) { + // eslint-disable-next-line unicorn/no-useless-promise-resolve-reject return Promise.reject(this.returnErrorInstance(response)); } return response; - } catch (err) { - if (err.response) { + } catch (error) { + if (error.response) { const wzMisc = new WzMisc(); + wzMisc.setApiIsDown(true); - const response = (err.response.data || {}).message || err.message; + + const response = error.response.data?.message || error.message; + + // eslint-disable-next-line unicorn/no-useless-promise-resolve-reject return Promise.reject(this.returnErrorInstance(response)); } else { - return (err || {}).message || false - ? Promise.reject(this.returnErrorInstance(err, err.message)) + return error?.message || false + ? Promise.reject(this.returnErrorInstance(error, error.message)) : Promise.reject( - this.returnErrorInstance(err, err || 'Server did not respond'), + this.returnErrorInstance( + error, + error || 'Server did not respond', + ), ); } } @@ -71,7 +79,6 @@ export class ApiCheck { const wazuhConfig = new WazuhConfig(); const { timeout } = wazuhConfig.getConfig(); const url = getHttp().basePath.prepend('/api/check-api'); - const options = { method: 'POST', headers: { @@ -80,25 +87,30 @@ export class ApiCheck { }, url: url, data: { ...apiEntry, forceRefresh }, - timeout: timeout || 20000, + timeout: timeout || 20_000, }; - const response = await request(options); if (response.error) { + // eslint-disable-next-line unicorn/no-useless-promise-resolve-reject return Promise.reject(this.returnErrorInstance(response)); } return response; - } catch (err) { - if (err.response) { - const response = (err.response.data || {}).message || err.message; + } catch (error) { + if (error.response) { + const response = error.response?.data?.message || error.message; + + // eslint-disable-next-line unicorn/no-useless-promise-resolve-reject return Promise.reject(this.returnErrorInstance(response)); } else { - return (err || {}).message || false - ? Promise.reject(this.returnErrorInstance(err, err.message)) + return error?.message || false + ? Promise.reject(this.returnErrorInstance(error, error.message)) : Promise.reject( - this.returnErrorInstance(err, err || 'Server did not respond'), + this.returnErrorInstance( + error, + error || 'Server did not respond', + ), ); } } @@ -114,7 +126,9 @@ export class ApiCheck { if (!error || typeof error === 'string') { return new Error(message || error); } + error.message = message; + return error; } } diff --git a/plugins/wazuh-core/public/services/state/hocs/creator.tsx b/plugins/wazuh-core/public/services/state/hocs/creator.tsx index 171cf1c79c..7dc8dfc660 100644 --- a/plugins/wazuh-core/public/services/state/hocs/creator.tsx +++ b/plugins/wazuh-core/public/services/state/hocs/creator.tsx @@ -1,9 +1,11 @@ import React from 'react'; -export const createHOCs = ({ useStateContainer }) => { - return { - withStateContainer: (name: string) => WrappedComponent => props => { + +export const createHOCs = ({ useStateContainer }) => ({ + withStateContainer: (name: string) => WrappedComponent => + function WithStateContainer(props: any) { const [state, { set: setState, remove: removeState }] = useStateContainer(name); + return ( { /> ); }, - }; -}; +}); diff --git a/plugins/wazuh-core/public/services/state/hooks/creator.ts b/plugins/wazuh-core/public/services/state/hooks/creator.ts index 96679e634d..998d23fb20 100644 --- a/plugins/wazuh-core/public/services/state/hooks/creator.ts +++ b/plugins/wazuh-core/public/services/state/hooks/creator.ts @@ -7,14 +7,18 @@ export const createHooks = ({ state }: { state: State }) => { state.getStateContainer(name).updater$, state.get(name), ); - function setValue(value) { + + function setValue(value: any) { state.set(name, value); } + function removeValue() { state.remove(name); } + return [value, { set: setValue, remove: removeValue }]; } + return { useStateContainer, }; diff --git a/plugins/wazuh-core/public/services/state/state.test.ts b/plugins/wazuh-core/public/services/state/state.test.ts index 4de8fb8da4..074bec4cbd 100644 --- a/plugins/wazuh-core/public/services/state/state.test.ts +++ b/plugins/wazuh-core/public/services/state/state.test.ts @@ -1,7 +1,8 @@ -import { CoreState } from './state'; import { BehaviorSubject } from 'rxjs'; +import { CoreState } from './state'; const noop = () => {}; + const logger = { info: noop, warn: noop, @@ -12,6 +13,7 @@ const logger = { describe('State', () => { it('Throw error accessing to non-existent state container', () => { const state = new CoreState(logger); + expect(() => { state.get('no_existent_state_container'); }).toThrow( @@ -39,8 +41,8 @@ describe('State', () => { it('Register a state container, get value, set new value and get new value', () => { const state = new CoreState(logger); - const subscribe = jest.fn(); + state.register('state_container', { _value: true, // mock state. This does not exist in the StateContainer type get() { @@ -49,7 +51,9 @@ describe('State', () => { set(newValue) { this._value = newValue; }, - remove() {}, + remove() { + /* empty */ + }, subscribe: subscribe, }); @@ -62,8 +66,8 @@ describe('State', () => { it('Register a state container, subscribe, set new value and remove the subscription', () => { const state = new CoreState(logger); - const subscriber = jest.fn(); + state.register('state_container', { _value: true, // mock state. This does not exist in the StateContainer type get() { @@ -73,7 +77,9 @@ describe('State', () => { this._value = newValue; this.updater$.next(this._value); }, - remove() {}, + remove() { + /* empty */ + }, updater$: new BehaviorSubject(), subscribe(cb) { return this.updater$.subscribe(cb); @@ -83,6 +89,7 @@ describe('State', () => { expect(state._subscriptions._subscriptions).toEqual(null); const unsubscribe = state.subscribe('state_container', subscriber); + expect(state._subscriptions._subscriptions).toHaveLength(1); state.set('state_container', false); diff --git a/plugins/wazuh-core/public/services/state/state.ts b/plugins/wazuh-core/public/services/state/state.ts index 3c0173d3b0..0cd67ded10 100644 --- a/plugins/wazuh-core/public/services/state/state.ts +++ b/plugins/wazuh-core/public/services/state/state.ts @@ -1,65 +1,81 @@ -import { ILogger } from '../../../common/services/configuration'; +import { Subscription } from 'rxjs'; +import { Logger } from '../../../common/services/configuration'; import { createHOCs } from './hocs/creator'; import { createHooks } from './hooks/creator'; import { State, StateContainer } from './types'; -import { Subscription } from 'rxjs'; export class CoreState implements State { - private _subscriptions: Subscription = new Subscription(); - private stateContainers: Map; - constructor(private logger: ILogger) { + private readonly _subscriptions: Subscription = new Subscription(); + private readonly stateContainers: Map; + + constructor(private readonly logger: Logger) { this.stateContainers = new Map(); } + setup() { this.logger.debug('Setup'); + const hooks = createHooks({ state: this }); const hocs = createHOCs(hooks); + return { hooks, hocs, }; } + start() { this.logger.debug('Start'); } + stop() { this.logger.debug('Stop'); this.logger.debug('Unsubscribing'); this._subscriptions.unsubscribe(); this.logger.debug('Unsubscribed'); } + getStateContainer(name: string) { if (!this.stateContainers.has(name)) { const error = new Error( `State container [${name}] does not exist. Did you forget to register it?`, ); + this.logger.error(error.message); throw error; } + return this.stateContainers.get(name); } + register(name: string, value: StateContainer) { this.stateContainers.set(name, value); } + get(name: string) { - return this.getStateContainer(name)!.get(); + return this.getStateContainer(name)?.get(); } + set(name: string, value: any) { - return this.getStateContainer(name)!.set(value); + return this.getStateContainer(name)?.set(value); } + remove(name: string) { - return this.getStateContainer(name)!.remove(value); + return this.getStateContainer(name)?.remove(); } - subscribe(name: string, callback: (value) => void) { + + subscribe(name: string, callback: (value: any) => void) { const stateContainerSubscription = - this.getStateContainer(name)!.subscribe(callback); + this.getStateContainer(name)?.subscribe(callback); // Create a wrapper of the original subscription to remove all in the stop plugin lifecycle const stateContainerUnsub = () => { stateContainerSubscription.unsubscribe(); this._subscriptions.remove(stateContainerSubscription); }; + this._subscriptions.add(stateContainerSubscription); + return stateContainerUnsub; } } diff --git a/plugins/wazuh-core/public/services/state/types.ts b/plugins/wazuh-core/public/services/state/types.ts index d70dbf37f8..9e1ed7f1a4 100644 --- a/plugins/wazuh-core/public/services/state/types.ts +++ b/plugins/wazuh-core/public/services/state/types.ts @@ -1,5 +1,5 @@ -import { LifecycleService } from '../types'; import { Subscription, BehaviourSubject } from 'rxjs'; +import { LifecycleService } from '../types'; export interface StateContainer { get: () => T; @@ -28,5 +28,8 @@ export interface State< set: (name: string, value: any) => any; remove: (name: string) => any; register: (name: string, value: StateContainer) => any; - subscribe(name: string, callback: StateContainer['subscribe']): () => {}; + subscribe: ( + name: string, + callback: StateContainer['subscribe'], + ) => () => void; } From ad1f0d2b58579897c1124d1d7943389576490be9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20David=20Guti=C3=A9rrez?= Date: Thu, 12 Dec 2024 11:44:32 +0100 Subject: [PATCH 14/28] fix(core): dependencies --- plugins/wazuh-core/yarn.lock | 9 --------- 1 file changed, 9 deletions(-) diff --git a/plugins/wazuh-core/yarn.lock b/plugins/wazuh-core/yarn.lock index 1f0836638d..83791d1f58 100644 --- a/plugins/wazuh-core/yarn.lock +++ b/plugins/wazuh-core/yarn.lock @@ -276,15 +276,6 @@ cookie@^0.7.2: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== -cross-spawn@^7.0.2: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - crypt@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" From f03fca39b39b70076ad033e701aa9a018dd1af0a Mon Sep 17 00:00:00 2001 From: Guido Modarelli Date: Thu, 12 Dec 2024 11:31:33 -0300 Subject: [PATCH 15/28] fix(settings): improve types for SettingsComponent props --- plugins/main/public/components/settings/settings.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/plugins/main/public/components/settings/settings.tsx b/plugins/main/public/components/settings/settings.tsx index 4d0b8e17b1..77d8357994 100644 --- a/plugins/main/public/components/settings/settings.tsx +++ b/plugins/main/public/components/settings/settings.tsx @@ -1,4 +1,3 @@ -/* eslint-disable react/prop-types */ /* * Wazuh app - Settings controller * Copyright (C) 2015-2022 Wazuh, Inc. @@ -58,7 +57,14 @@ const mapDispatchToProps = dispatch => ({ dispatch(updateGlobalBreadcrumb(breadcrumb)), }); -class SettingsComponent extends React.Component { +interface SettingsComponentProps { + configurationUIEditable: boolean; + configurationIPSelector: string; + updateGlobalBreadcrumb: (breadcrumb: string) => void; + tab: string; +} + +class SettingsComponent extends React.Component { state: { tabs: { id: string; name: string }[] | null; load: boolean; @@ -67,7 +73,7 @@ class SettingsComponent extends React.Component { apiEntries; }; - constructor(props) { + constructor(props: SettingsComponentProps) { super(props); this.wzMisc = new WzMisc(); From 56395eea215e46311cae7c96783d1bfc2391db7c Mon Sep 17 00:00:00 2001 From: Guido Modarelli Date: Thu, 12 Dec 2024 11:31:51 -0300 Subject: [PATCH 16/28] fix(eslint): disable unicorn/no-static-only-class rule --- .eslintrc.js | 1 + 1 file changed, 1 insertion(+) diff --git a/.eslintrc.js b/.eslintrc.js index 16133261ff..2b573f230b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -169,6 +169,7 @@ module.exports = { /* -------------------------------------------------------------------------- */ /* unicorn */ /* -------------------------------------------------------------------------- */ + 'unicorn/no-static-only-class': 'off', 'unicorn/prefer-module': 'off', 'unicorn/prefer-ternary': 'off', // https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/custom-error-definition.md From 93eedabf96f01ee2643cd84fd5b81c06e034a16b Mon Sep 17 00:00:00 2001 From: Guido Modarelli Date: Thu, 12 Dec 2024 11:36:48 -0300 Subject: [PATCH 17/28] fix(types): improve type annotations for decoratorCheckIsEnabled callback function in index-patterns.ts --- plugins/wazuh-core/server/initialization/index-patterns.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/wazuh-core/server/initialization/index-patterns.ts b/plugins/wazuh-core/server/initialization/index-patterns.ts index 26fef596c5..a2c704d5b0 100644 --- a/plugins/wazuh-core/server/initialization/index-patterns.ts +++ b/plugins/wazuh-core/server/initialization/index-patterns.ts @@ -16,7 +16,12 @@ interface EnsureIndexPatternExistenceContextTaskWithConfigurationSetting // eslint-disable-next-line @typescript-eslint/no-unused-vars const decoratorCheckIsEnabled = - callback => + ( + callback: ( + ctx: InitializationTaskRunContext, + ctxTask: EnsureIndexPatternExistenceContextTask, + ) => Promise, + ) => async ( ctx: InitializationTaskRunContext, { From cc66b049c535189488e6b8a63d92c54c1c2847c6 Mon Sep 17 00:00:00 2001 From: Guido Modarelli Date: Thu, 12 Dec 2024 11:37:40 -0300 Subject: [PATCH 18/28] fix(eslint): remove redundant eslint directive for unicorn/no-static-only-class in app-state.js --- plugins/main/public/react-services/app-state.js | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/main/public/react-services/app-state.js b/plugins/main/public/react-services/app-state.js index 9709c6a995..4caef3141a 100644 --- a/plugins/main/public/react-services/app-state.js +++ b/plugins/main/public/react-services/app-state.js @@ -23,7 +23,6 @@ import { WzAuthentication } from './wz-authentication'; import { UI_ERROR_SEVERITIES } from './error-orchestrator/types'; import { getErrorOrchestrator } from './common-services'; -// eslint-disable-next-line unicorn/no-static-only-class export class AppState { /** * Cluster setters and getters From f6c2ff470854bfeaead2f8a7e432a9e8c708f2f2 Mon Sep 17 00:00:00 2001 From: Guido Modarelli Date: Thu, 12 Dec 2024 11:38:39 -0300 Subject: [PATCH 19/28] fix(types): set default type parameters for State and LifecycleService interfaces in state and services types files --- plugins/wazuh-core/public/services/state/types.ts | 12 ++++++------ plugins/wazuh-core/public/services/types.ts | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/plugins/wazuh-core/public/services/state/types.ts b/plugins/wazuh-core/public/services/state/types.ts index 9e1ed7f1a4..5887c23bc3 100644 --- a/plugins/wazuh-core/public/services/state/types.ts +++ b/plugins/wazuh-core/public/services/state/types.ts @@ -10,12 +10,12 @@ export interface StateContainer { } export interface State< - SetupDeps, - SetupReturn, - StartDeps, - StartReturn, - StopDeps, - StopReturn, + SetupDeps = any, + SetupReturn = any, + StartDeps = any, + StartReturn = any, + StopDeps = any, + StopReturn = any, > extends LifecycleService< SetupDeps, SetupReturn, diff --git a/plugins/wazuh-core/public/services/types.ts b/plugins/wazuh-core/public/services/types.ts index bb6c43c39f..75147d110f 100644 --- a/plugins/wazuh-core/public/services/types.ts +++ b/plugins/wazuh-core/public/services/types.ts @@ -1,10 +1,10 @@ export interface LifecycleService< - SetupDeps, - SetupReturn, - StartDeps, - StartReturn, - StopDeps, - StopReturn, + SetupDeps = any, + SetupReturn = any, + StartDeps = any, + StartReturn = any, + StopDeps = any, + StopReturn = any, > { setup: (deps: SetupDeps) => SetupReturn; start: (deps: StartDeps) => StartReturn; From 3fde6b8a3d90e04e7bdfb4ec4515fa0ef87126f7 Mon Sep 17 00:00:00 2001 From: Guido Modarelli Date: Thu, 12 Dec 2024 11:40:24 -0300 Subject: [PATCH 20/28] fix(eslint): remove unnecessary eslint directive for unicorn/no-static-only-class in wz-api-check.js --- plugins/main/public/react-services/wz-api-check.js | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/main/public/react-services/wz-api-check.js b/plugins/main/public/react-services/wz-api-check.js index c41f3612aa..7c9289cf32 100644 --- a/plugins/main/public/react-services/wz-api-check.js +++ b/plugins/main/public/react-services/wz-api-check.js @@ -15,7 +15,6 @@ import { PLUGIN_PLATFORM_REQUEST_HEADERS } from '../../common/constants'; import { request } from '../services/request-handler'; import { WazuhConfig } from './wazuh-config'; -// eslint-disable-next-line unicorn/no-static-only-class export class ApiCheck { static async checkStored(data, idChanged = false) { try { From 254fcdd619adb5f9f529f0e99da16faa6cddc076 Mon Sep 17 00:00:00 2001 From: Guido Modarelli Date: Thu, 12 Dec 2024 11:45:37 -0300 Subject: [PATCH 21/28] fix(eslint): add consistent-function-scoping rule to enforce function scoping standards across the project --- .eslintrc.js | 6 ++++++ .../health-check/container/health-check.container.test.tsx | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.eslintrc.js b/.eslintrc.js index 2b573f230b..d5f59f3f5b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -169,6 +169,12 @@ module.exports = { /* -------------------------------------------------------------------------- */ /* unicorn */ /* -------------------------------------------------------------------------- */ + 'unicorn/consistent-function-scoping': [ + 'error', + { + checkArrowFunctions: false, + }, + ], 'unicorn/no-static-only-class': 'off', 'unicorn/prefer-module': 'off', 'unicorn/prefer-ternary': 'off', diff --git a/plugins/main/public/components/health-check/container/health-check.container.test.tsx b/plugins/main/public/components/health-check/container/health-check.container.test.tsx index 1ce65e852d..90ee7d2e06 100644 --- a/plugins/main/public/components/health-check/container/health-check.container.test.tsx +++ b/plugins/main/public/components/health-check/container/health-check.container.test.tsx @@ -1,4 +1,3 @@ -/* eslint-disable unicorn/consistent-function-scoping */ /* * Wazuh app - Health Check Component - Test * From c8bc275c3ab07165221844e63a30c77a01630874 Mon Sep 17 00:00:00 2001 From: Guido Modarelli Date: Thu, 12 Dec 2024 11:48:46 -0300 Subject: [PATCH 22/28] fix(eslint): replace Promise.reject with throw for error handling in wz-api-check.js to improve code readability and flow --- plugins/main/public/react-services/wz-api-check.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/plugins/main/public/react-services/wz-api-check.js b/plugins/main/public/react-services/wz-api-check.js index 7c9289cf32..4519602f0b 100644 --- a/plugins/main/public/react-services/wz-api-check.js +++ b/plugins/main/public/react-services/wz-api-check.js @@ -41,8 +41,7 @@ export class ApiCheck { const response = await request(options); if (response.error) { - // eslint-disable-next-line unicorn/no-useless-promise-resolve-reject - return Promise.reject(this.returnErrorInstance(response)); + throw this.returnErrorInstance(response); } return response; @@ -54,8 +53,7 @@ export class ApiCheck { const response = error.response.data?.message || error.message; - // eslint-disable-next-line unicorn/no-useless-promise-resolve-reject - return Promise.reject(this.returnErrorInstance(response)); + throw this.returnErrorInstance(response); } else { return error?.message || false ? Promise.reject(this.returnErrorInstance(error, error.message)) @@ -91,8 +89,7 @@ export class ApiCheck { const response = await request(options); if (response.error) { - // eslint-disable-next-line unicorn/no-useless-promise-resolve-reject - return Promise.reject(this.returnErrorInstance(response)); + throw this.returnErrorInstance(response); } return response; @@ -100,8 +97,7 @@ export class ApiCheck { if (error.response) { const response = error.response?.data?.message || error.message; - // eslint-disable-next-line unicorn/no-useless-promise-resolve-reject - return Promise.reject(this.returnErrorInstance(response)); + throw this.returnErrorInstance(response); } else { return error?.message || false ? Promise.reject(this.returnErrorInstance(error, error.message)) From ee2a167a1a69a2a9b056cab75bfe9ab41657edfe Mon Sep 17 00:00:00 2001 From: Guido Modarelli Date: Thu, 12 Dec 2024 12:01:53 -0300 Subject: [PATCH 23/28] fix(eslint): update type definition for WrappedComponent in createHOCs for better clarity and type safety in creator.tsx --- plugins/wazuh-core/public/services/state/hocs/creator.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/wazuh-core/public/services/state/hocs/creator.tsx b/plugins/wazuh-core/public/services/state/hocs/creator.tsx index 7dc8dfc660..84e6c83277 100644 --- a/plugins/wazuh-core/public/services/state/hocs/creator.tsx +++ b/plugins/wazuh-core/public/services/state/hocs/creator.tsx @@ -1,7 +1,7 @@ import React from 'react'; export const createHOCs = ({ useStateContainer }) => ({ - withStateContainer: (name: string) => WrappedComponent => + withStateContainer: (name: string) => (WrappedComponent: React.ElementType) => function WithStateContainer(props: any) { const [state, { set: setState, remove: removeState }] = useStateContainer(name); From 1f1caa9f5f9b1e9c1b71942350aa4b158cd39e61 Mon Sep 17 00:00:00 2001 From: Guido Modarelli Date: Thu, 12 Dec 2024 12:04:40 -0300 Subject: [PATCH 24/28] fix(types): update remove method type in StateContainer for improved type safety in state management functionalities --- plugins/wazuh-core/public/services/state/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/wazuh-core/public/services/state/types.ts b/plugins/wazuh-core/public/services/state/types.ts index 5887c23bc3..d80347ca1a 100644 --- a/plugins/wazuh-core/public/services/state/types.ts +++ b/plugins/wazuh-core/public/services/state/types.ts @@ -4,7 +4,7 @@ import { LifecycleService } from '../types'; export interface StateContainer { get: () => T; set: (value: T) => T; - remove: () => any; + remove: () => T; updater$: BehaviourSubject; subscribe: (callback: (value: T) => void) => Subscription; } From 7a2c1f27ed94c8bcbc57520df74bd034f758b472 Mon Sep 17 00:00:00 2001 From: Guido Modarelli Date: Thu, 12 Dec 2024 12:19:59 -0300 Subject: [PATCH 25/28] fix(types): update createHooks to handle optional updater$ for better robustness in state management functionality --- plugins/wazuh-core/public/services/state/hooks/creator.ts | 2 +- plugins/wazuh-core/public/services/state/types.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/wazuh-core/public/services/state/hooks/creator.ts b/plugins/wazuh-core/public/services/state/hooks/creator.ts index 998d23fb20..f8417e4ecb 100644 --- a/plugins/wazuh-core/public/services/state/hooks/creator.ts +++ b/plugins/wazuh-core/public/services/state/hooks/creator.ts @@ -4,7 +4,7 @@ import { State } from '../types'; export const createHooks = ({ state }: { state: State }) => { function useStateContainer(name: string) { const value: T = useObservable( - state.getStateContainer(name).updater$, + state.getStateContainer(name)?.updater$, state.get(name), ); diff --git a/plugins/wazuh-core/public/services/state/types.ts b/plugins/wazuh-core/public/services/state/types.ts index d80347ca1a..a1a309de61 100644 --- a/plugins/wazuh-core/public/services/state/types.ts +++ b/plugins/wazuh-core/public/services/state/types.ts @@ -27,6 +27,7 @@ export interface State< get: (name: string) => any; set: (name: string, value: any) => any; remove: (name: string) => any; + getStateContainer: (name: string) => StateContainer | undefined; register: (name: string, value: StateContainer) => any; subscribe: ( name: string, From 5a9c0deb45c0e204c3c539ae21c3d3e0a5e6f2c2 Mon Sep 17 00:00:00 2001 From: Guido Modarelli Date: Thu, 12 Dec 2024 12:21:56 -0300 Subject: [PATCH 26/28] fix(logging): improve error handling by explicitly casting error to Error for better clarity in data source alerts handling --- .../public/services/state/containers/data-source-alerts.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/wazuh-core/public/services/state/containers/data-source-alerts.ts b/plugins/wazuh-core/public/services/state/containers/data-source-alerts.ts index b9fe07b0f8..07c5a836a1 100644 --- a/plugins/wazuh-core/public/services/state/containers/data-source-alerts.ts +++ b/plugins/wazuh-core/public/services/state/containers/data-source-alerts.ts @@ -53,7 +53,7 @@ export class DataSourceAlertsStateContainer implements StateContainer { return result; } catch (error) { - this.logger.error(`Error getting data: ${error.message}`); + this.logger.error(`Error getting data: ${(error as Error).message}`); throw error; } } @@ -78,7 +78,7 @@ export class DataSourceAlertsStateContainer implements StateContainer { this.logger.debug(`Encoded data was set: ${encodedData}`); } } catch (error) { - this.logger.error(`Error setting data: ${error.message}`); + this.logger.error(`Error setting data: ${(error as Error).message}`); // TODO: implement // const options = { // context: `${AppState.name}.setClusterInfo`, @@ -106,7 +106,7 @@ export class DataSourceAlertsStateContainer implements StateContainer { return result; } catch (error) { - this.logger.error(error.message); + this.logger.error((error as Error).message); throw error; } } From ecd91a91c739b30969f5e357fe3015addd58f2db Mon Sep 17 00:00:00 2001 From: Guido Modarelli Date: Thu, 12 Dec 2024 12:22:47 -0300 Subject: [PATCH 27/28] fix(logging): enhance error logging by explicitly casting to Error for clearer messages in server host cluster info handling --- .../services/state/containers/server-host-cluster-info.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/wazuh-core/public/services/state/containers/server-host-cluster-info.ts b/plugins/wazuh-core/public/services/state/containers/server-host-cluster-info.ts index f2a78f2892..92f5f59854 100644 --- a/plugins/wazuh-core/public/services/state/containers/server-host-cluster-info.ts +++ b/plugins/wazuh-core/public/services/state/containers/server-host-cluster-info.ts @@ -38,7 +38,7 @@ export class ServerHostClusterInfoStateContainer implements StateContainer { return result; } catch (error) { - this.logger.error(`Error getting data: ${error.message}`); + this.logger.error(`Error getting data: ${(error as Error).message}`); // TODO: implement // const options = { // context: `${AppState.name}.getClusterInfo`, @@ -75,7 +75,7 @@ export class ServerHostClusterInfoStateContainer implements StateContainer { this.logger.debug(`Encoded data was set: ${encodedData}`); } } catch (error) { - this.logger.error(`Error setting data: ${error.message}`); + this.logger.error(`Error setting data: ${(error as Error).message}`); // TODO: implement // const options = { // context: `${AppState.name}.setClusterInfo`, From e29fb5bfdde456b2c97bdac3877431b4c75e5228 Mon Sep 17 00:00:00 2001 From: Guido Modarelli Date: Thu, 12 Dec 2024 12:24:07 -0300 Subject: [PATCH 28/28] fix(types): improve typing for set method and enhance error logging clarity with explicit Error casting in server host state management --- .../public/services/state/containers/server-host.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/wazuh-core/public/services/state/containers/server-host.ts b/plugins/wazuh-core/public/services/state/containers/server-host.ts index 576111a295..b535e275ff 100644 --- a/plugins/wazuh-core/public/services/state/containers/server-host.ts +++ b/plugins/wazuh-core/public/services/state/containers/server-host.ts @@ -35,7 +35,7 @@ export class ServerHostStateContainer implements StateContainer { return false; } - set(data) { + set(data: any) { try { this.logger.debug(`Setting data: ${data}`); @@ -55,7 +55,7 @@ export class ServerHostStateContainer implements StateContainer { this.logger.debug(`Encoded data was set: ${encodedData}`); } } catch (error) { - this.logger.error(`Error setting data: ${error.message}`); + this.logger.error(`Error setting data: ${(error as Error).message}`); // TODO: implement // const options = { // context: `${AppState.name}.setCurrentAPI`,